/* * 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 java.util.Arrays.asList; import static java.util.Optional.of; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mule.runtime.api.el.BindingContext.builder; import static org.mule.runtime.api.metadata.DataType.BYTE_ARRAY; import static org.mule.runtime.api.metadata.DataType.OBJECT; import static org.mule.runtime.api.metadata.DataType.STRING; import static org.mule.runtime.api.metadata.DataType.fromFunction; import static org.mule.runtime.api.metadata.DataType.fromType; 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 org.mule.runtime.api.el.BindingContext; import org.mule.runtime.api.el.DefaultExpressionLanguageFactoryService; import org.mule.runtime.api.el.ExpressionFunction; import org.mule.runtime.api.el.ExpressionLanguage; 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.api.streaming.CursorProvider; 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.el.ExtendedExpressionManager; import org.mule.runtime.core.el.mvel.MVELExpressionLanguage; import org.mule.runtime.core.streaming.StreamingManager; import org.mule.tck.junit4.AbstractMuleContextTestCase; import java.util.List; import java.util.Map; import java.util.Optional; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; 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) @RunWith(MockitoJUnitRunner.class) public class DefaultExpressionManagerTestCase extends AbstractMuleContextTestCase { private static final String MY_VAR = "myVar"; @Rule public ExpectedException expectedException = ExpectedException.none(); @Mock private StreamingManager streamingManager; private ExtendedExpressionManager expressionManager; @Before public void setUp() { expressionManager = new DefaultExpressionManager(muleContext, streamingManager); } @Test @Description("Verifies that global bindings can be added.") public void globals() { DataType integerType = fromType(Integer.class); ExpressionFunction multiply = new ExpressionFunction() { @Override public Object call(Object[] objects, BindingContext bindingContext) { return ((Integer) objects[0]) * ((Integer) objects[1]); } @Override public Optional<DataType> returnType() { return of(integerType); } @Override public List<FunctionParameter> parameters() { return asList(new FunctionParameter("x", integerType), new FunctionParameter("y", integerType)); } }; BindingContext globalContext = builder() .addBinding("aNum", new TypedValue<>(4, fromType(Integer.class))) .addBinding("times", new TypedValue<>(multiply, fromFunction(multiply))) .build(); expressionManager.addGlobalBindings(globalContext); TypedValue result = expressionManager.evaluate("aNum times 5"); assertThat(result.getValue(), is(20)); expressionManager.addGlobalBindings(builder().addBinding("otherNum", new TypedValue(3, integerType)).build()); result = expressionManager.evaluate("(times(7, 3) + otherNum) / aNum"); assertThat(result.getValue(), is(6)); } @Test @Description("Verifies that a simple literal expression is successful.") public void simple() { String expression = "\"wow\""; assertString(expression); } @Test @Description("Verifies that a simple literal expression is successful when using brackets.") public void simpleEnclosed() { assertString("#[\"wow\"]"); } private void assertString(String expression) { assertThat(expressionManager.evaluate(expression).getValue(), is("wow")); } @Test @Description("Verifies that a map expression is successful.") public void map() { String expression = "{\'name\' : \'Sarah\', \'surname\' : \'Manning\'}"; Object result = expressionManager.evaluate(expression).getValue(); assertThat(result, is(instanceOf(Map.class))); assertThat((Map<String, String>) result, hasEntry("name", "Sarah")); assertThat((Map<String, String>) result, hasEntry("surname", "Manning")); } @Test @Description("Verifies that custom variables are considered.") public void simpleCustomVariable() { Object object = new Object(); BindingContext context = builder().addBinding(MY_VAR, new TypedValue(object, OBJECT)).build(); assertThat(expressionManager.evaluate("#[myVar]", context).getValue(), equalTo(object)); } @Test @Description("Verifies that the flow variable exposing it's name works.") public void flowName() throws MuleException { FlowConstruct mockFlowConstruct = mock(FlowConstruct.class); when(mockFlowConstruct.getName()).thenReturn("myFlowName"); String result = (String) expressionManager.evaluate("#[flow.name]", testEvent(), mockFlowConstruct).getValue(); assertThat(result, is(mockFlowConstruct.getName())); } @Test @Description("Verifies that payload variable works.") public void payloadVariable() throws MuleException { assertThat(expressionManager.evaluate("payload", testEvent()).getValue(), is(TEST_PAYLOAD)); } @Test @Description("Verifies that flowVars work, returning null for non existent ones and it's value for those that do.") public void flowVars() throws MuleException { Event.Builder eventBuilder = Event.builder(testEvent()); String flowVars = "variables.myVar"; assertThat(expressionManager.evaluate(flowVars, eventBuilder.build()).getValue(), nullValue()); String value = "Leda"; eventBuilder.addVariable(MY_VAR, value); assertThat(expressionManager.evaluate(flowVars, eventBuilder.build()).getValue(), is(value)); } @Test @Description("Verifies that a simple transformation works.") public void transformation() throws MuleException { String expression = "payload"; TypedValue result = expressionManager.evaluate(expression, BYTE_ARRAY, builder().build(), testEvent()); assertThat(result.getValue(), is(TEST_PAYLOAD.getBytes())); assertThat(result.getDataType(), is(BYTE_ARRAY)); } @Test @Description("Verifies that a simple transformation works even when it's not required.") public void transformationNotNeeded() throws MuleException { String expression = "payload"; TypedValue result = expressionManager.evaluate(expression, STRING, builder().build(), testEvent()); assertThat(result.getValue(), is(TEST_PAYLOAD)); assertThat(result.getDataType(), is(STRING)); } @Test @Description("Verifies that parsing works with inner expressions in MVEL but only with regular ones in DW.") public void parseCompatibility() throws MuleException { assertThat(expressionManager.parse("this is #[mel:payload]", testEvent(), mock(FlowConstruct.class)), is(String.format("this is %s", TEST_PAYLOAD))); assertThat(expressionManager.parse("#['this is ' ++ payload]", testEvent(), mock(FlowConstruct.class)), is(String.format("this is %s", TEST_PAYLOAD))); expectedException.expect(RuntimeException.class); expressionManager.parse("this is #[payload]", testEvent(), mock(FlowConstruct.class)); } @Test @Description("Verifies that parsing works for plain String scenarios.") public void parse() throws MuleException { String expression = "this is a test"; assertThat(expressionManager.parse(expression, testEvent(), mock(FlowConstruct.class)), is(expression)); } @Test public void isValid() { String expression = "2*2"; assertThat(expressionManager.isValid(expression), is(true)); } @Test public void isInvalid() { String expression = "2*'2"; assertThat(expressionManager.isValid(expression), is(false)); } @Test public void isExpression() { assertThat(expressionManager.isExpression("2*2 + #[var]"), is(true)); assertThat(expressionManager.isExpression("#[var]"), is(true)); assertThat(expressionManager.isExpression("${var}"), is(false)); } @Test public void managedCursor() throws Exception { final MuleContext mockMuleContext = mock(MuleContext.class, RETURNS_DEEP_STUBS); final DefaultExpressionLanguageFactoryService mockFactory = mock(DefaultExpressionLanguageFactoryService.class, RETURNS_DEEP_STUBS); final ExpressionLanguage expressionLanguage = mock(ExpressionLanguage.class, RETURNS_DEEP_STUBS); final CursorProvider cursorProvider = mock(CursorProvider.class); when(mockMuleContext.getRegistry().lookupObject(DefaultExpressionLanguageFactoryService.class)).thenReturn(mockFactory); when(mockMuleContext.getRegistry().lookupObject(OBJECT_EXPRESSION_LANGUAGE)) .thenReturn(mock(MVELExpressionLanguage.class, RETURNS_DEEP_STUBS)); TypedValue value = new TypedValue(cursorProvider, BYTE_ARRAY); when(expressionLanguage.evaluate(anyString(), any())).thenReturn(value); when(expressionLanguage.evaluate(anyString(), any(), any())).thenReturn(value); when(mockFactory.create()).thenReturn(expressionLanguage); expressionManager = new DefaultExpressionManager(mockMuleContext, streamingManager); final Event event = testEvent(); expressionManager.evaluate("someExpression", event); verify(streamingManager).manage(cursorProvider, event); } }