/*
* 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);
}
}