/* * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com * The software in this package is published under the terms of the CPAL v1.0 * license, a copy of which has been included with this distribution in the * LICENSE.txt file. */ package org.mule.runtime.core.el; import static org.mule.runtime.api.message.Message.of; import static org.mule.runtime.api.metadata.DataType.STRING; import static org.mule.runtime.api.metadata.DataType.fromFunction; import static org.mule.runtime.core.api.Event.builder; import static org.mule.runtime.core.api.config.MuleProperties.OBJECT_EXPRESSION_LANGUAGE; import static org.mule.test.allure.AllureConstants.ExpressionLanguageFeature.EXPRESSION_LANGUAGE; import static org.mule.test.allure.AllureConstants.ExpressionLanguageFeature.ExpressionLanguageStory.SUPPORT_MVEL_DW; import static java.lang.String.format; import static java.util.Arrays.asList; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import org.mule.runtime.api.el.BindingContext; import org.mule.runtime.api.el.ExpressionFunction; import org.mule.runtime.api.exception.MuleException; import org.mule.runtime.api.metadata.DataType; import org.mule.runtime.api.metadata.FunctionParameter; import org.mule.runtime.api.metadata.TypedValue; import org.mule.runtime.core.api.Event; import org.mule.runtime.core.api.MuleContext; import org.mule.runtime.core.api.construct.FlowConstruct; import org.mule.runtime.core.api.expression.ExpressionRuntimeException; import org.mule.runtime.core.el.context.MessageContext; import org.mule.runtime.core.el.mvel.MVELExpressionLanguage; import java.util.List; import java.util.Optional; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import ru.yandex.qatools.allure.annotations.Description; import ru.yandex.qatools.allure.annotations.Features; import ru.yandex.qatools.allure.annotations.Stories; @Features(EXPRESSION_LANGUAGE) @Stories(SUPPORT_MVEL_DW) @Description("Test cases verifying the differences between MVEL and DW ELs.") public class ExtendedExpressionLanguageAdapterTestCase extends AbstractWeaveExpressionLanguageTestCase { @Rule public ExpectedException expectedException = ExpectedException.none(); private ExpressionLanguageAdaptorHandler expressionLanguageAdapter; private BindingContext emptyBindingContext = BindingContext.builder().build(); @Before public void setUp() { super.setUp(); MVELExpressionLanguage mvelExpressionLanguage = muleContext.getRegistry().lookupObject(OBJECT_EXPRESSION_LANGUAGE); expressionLanguageAdapter = new ExpressionLanguageAdaptorHandler(expressionLanguage, mvelExpressionLanguage); } @Test @Description("Verifies that global binding context only work for DW.") public void globalContext() throws Exception { ExpressionFunction expressionFunction = new ExpressionFunction() { private DataType dataType = STRING; @Override public Object call(Object[] objects, BindingContext bindingContext) { return ((String) objects[0]).toUpperCase(); } @Override public Optional<DataType> returnType() { return Optional.of(dataType); } @Override public List<FunctionParameter> parameters() { return asList(new FunctionParameter("x", dataType)); } }; String global = "global"; String value = "var"; BindingContext context = BindingContext.builder() .addBinding(global, new TypedValue(value, STRING)) .addBinding("upper", new TypedValue(expressionFunction, fromFunction(expressionFunction))) .build(); expressionLanguageAdapter.addGlobalBindings(context); assertThat(expressionLanguageAdapter.evaluate(global, testEvent(), emptyBindingContext).getValue(), is(value)); assertThat(expressionLanguageAdapter.evaluate("upper('hey')", testEvent(), emptyBindingContext).getValue(), is("HEY")); assertThat(expressionLanguageAdapter.evaluate(melify(global), testEvent(), context).getValue(), is(value)); expectedException.expect(ExpressionRuntimeException.class); expressionLanguageAdapter.evaluate(melify(global), testEvent(), emptyBindingContext); } @Test @Description("Verifies that the Event variable still works for MVEL but that it fails for DW.") public void eventCompatibilityVariables() throws MuleException { String expression = "_muleEvent"; Object mvelFlowResult = expressionLanguageAdapter.evaluate(melify(expression), testEvent(), emptyBindingContext).getValue(); assertThat(mvelFlowResult, is(instanceOf(Event.class))); expectedException.expect(RuntimeException.class); expressionLanguageAdapter.evaluate(expression, testEvent(), emptyBindingContext).getValue(); } @Test @Description("Verifies that the MuleContext variable still works for MVEL but that it fails for DW.") public void muleContextCompatibilityVariables() throws MuleException { String expression = "_muleContext"; Object mvelFlowResult = expressionLanguageAdapter.evaluate(melify(expression), testEvent(), emptyBindingContext).getValue(); assertThat(mvelFlowResult, is(instanceOf(MuleContext.class))); expectedException.expect(RuntimeException.class); expressionLanguageAdapter.evaluate(expression, testEvent(), emptyBindingContext).getValue(); } @Test @Description("Verifies that the Message variable still works for MVEL but that it fails for DW.") public void messageCompatibilityVariables() throws MuleException { String expression = "message"; Object mvelFlowResult = expressionLanguageAdapter.evaluate(melify(expression), testEvent(), emptyBindingContext).getValue(); assertThat(mvelFlowResult, is(instanceOf(MessageContext.class))); expectedException.expect(ExpressionRuntimeException.class); expressionLanguageAdapter.evaluate(expression, testEvent(), emptyBindingContext).getValue(); } @Test @Description("Verifies that the Flow name variable works for MVEL and DW.") public void flowNameVariable() throws MuleException { String expression = "flow.name"; FlowConstruct mockFlowConstruct = mock(FlowConstruct.class); String myFlowName = "myFlowName"; when(mockFlowConstruct.getName()).thenReturn(myFlowName); TypedValue mvelResult = expressionLanguageAdapter.evaluate(melify(expression), testEvent(), mockFlowConstruct, emptyBindingContext); assertThat(mvelResult.getValue(), is(myFlowName)); TypedValue dwResult = expressionLanguageAdapter.evaluate(expression, testEvent(), mockFlowConstruct, emptyBindingContext); assertThat(dwResult.getValue(), is(myFlowName)); } @Test @Description("Verifies that variables can be modified under MVEL but not DW.") public void variablesMutation() throws Exception { Event event = testEvent(); Event.Builder builder1 = builder(event); TypedValue result = expressionLanguageAdapter.evaluate(melify("flowVars.put(\'key\',\'value\')"), event, builder1, mock(FlowConstruct.class), emptyBindingContext); assertThat(result.getValue(), is(nullValue())); assertThat(builder1.build().getVariableNames(), contains("key")); Event.Builder builder2 = builder(event); TypedValue result2 = expressionLanguageAdapter.evaluate("variables.put(\'key\',\'value\')", event, builder2, mock(FlowConstruct.class), emptyBindingContext); assertThat(result2.getValue(), is(nullValue())); assertThat(builder2.build().getVariableNames(), not(contains("key"))); } @Test @Description("Verifies that the payload can be modified under MVEL but not DW.") public void payloadMutation() throws Exception { Event event = eventBuilder().message(of(1)).build(); Event.Builder builder1 = builder(event); String expression = "payload = 3"; TypedValue result = expressionLanguageAdapter.evaluate(melify(expression), event, builder1, mock(FlowConstruct.class), emptyBindingContext); assertThat(result.getValue(), is(1)); assertThat(builder1.build().getMessage().getPayload().getValue(), is(3)); Event.Builder builder2 = builder(event); expectedException.expect(ExpressionRuntimeException.class); expressionLanguageAdapter.evaluate(expression, event, builder2, mock(FlowConstruct.class), emptyBindingContext); } @Test @Description("Verifies that enrichment using an Object only works for MVEL.") public void enrichObjectCompatibility() throws MuleException { Event event = testEvent(); Event.Builder builder = builder(event); FlowConstruct flowConstruct = mock(FlowConstruct.class); String myPayload = "myPayload"; String expression = "payload"; expressionLanguageAdapter.enrich(melify(expression), event, builder, flowConstruct, myPayload); assertThat(builder.build().getMessage().getPayload().getValue(), is(myPayload)); expectedException.expect(UnsupportedOperationException.class); expressionLanguageAdapter.enrich(expression, event, builder, flowConstruct, myPayload); } @Test @Description("Verifies that enrichment using a TypedValue only works for MVEL.") public void enrichTypedValueCompatibility() throws MuleException { Event event = testEvent(); Event.Builder builder = builder(event); FlowConstruct flowConstruct = mock(FlowConstruct.class); TypedValue myPayload = new TypedValue("myPayload", STRING); String expression = "payload"; expressionLanguageAdapter.enrich(melify(expression), event, builder, flowConstruct, myPayload); Event enrichedEvent = builder.build(); assertThat(enrichedEvent.getMessage().getPayload().getValue(), is(myPayload.getValue())); assertThat(enrichedEvent.getMessage().getPayload().getDataType(), is(myPayload.getDataType())); expectedException.expect(UnsupportedOperationException.class); expressionLanguageAdapter.enrich(expression, event, builder, flowConstruct, myPayload); } private String melify(String expression) { return format("mel:%s", expression); } }