/*
* 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.routing;
import static java.util.Collections.singletonList;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.rules.ExpectedException.none;
import static org.mule.runtime.api.message.Message.of;
import org.mule.runtime.api.exception.MuleException;
import org.mule.runtime.api.message.Message;
import org.mule.runtime.core.api.Event;
import org.mule.runtime.core.api.expression.ExpressionRuntimeException;
import org.mule.runtime.core.exception.MessagingException;
import org.mule.runtime.core.internal.message.InternalMessage;
import org.mule.runtime.core.api.processor.Processor;
import org.mule.tck.junit4.AbstractReactiveProcessorTestCase;
import org.mule.tck.testmodels.mule.TestMessageProcessor;
import java.nio.BufferOverflowException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
public class ForeachTestCase extends AbstractReactiveProcessorTestCase {
protected Foreach simpleForeach;
protected Foreach nestedForeach;
protected ArrayList<Event> processedEvents;
private static String ERR_NUMBER_MESSAGES = "Not a correct number of messages processed";
private static String ERR_PAYLOAD_TYPE = "Type error on processed payloads";
private static String ERR_OUTPUT = "Messages processed incorrectly";
@Rule
public ExpectedException expectedException = none();
public ForeachTestCase(Mode mode) {
super(mode);
}
@Before
public void initialise() throws MuleException {
processedEvents = new ArrayList<>();
simpleForeach = createForeach(getSimpleMessageProcessors());
nestedForeach = createForeach(getNestedMessageProcessors());
}
private List<Processor> getSimpleMessageProcessors() {
List<Processor> lmp = new ArrayList<>();
lmp.add(event -> {
String payload = event.getMessage().getPayload().getValue().toString();
event = Event.builder(event).message(InternalMessage.builder(event.getMessage()).payload(payload + ":foo").build()).build();
return event;
});
lmp.add(new TestMessageProcessor("zas"));
lmp.add(event -> {
processedEvents.add(event);
return event;
});
return lmp;
}
private List<Processor> getNestedMessageProcessors() throws MuleException {
List<Processor> lmp = new ArrayList<>();
Foreach internalForeach = new Foreach();
internalForeach.setMessageProcessors(getSimpleMessageProcessors());
lmp.add(internalForeach);
return lmp;
}
private Foreach createForeach(List<Processor> mps) throws MuleException {
Foreach foreachMp = new Foreach();
foreachMp.setMessageProcessors(mps);
foreachMp.setMuleContext(muleContext);
foreachMp.initialise();
return foreachMp;
}
@Test
public void arrayListPayload() throws Exception {
List<String> arrayList = new ArrayList<>();
arrayList.add("bar");
arrayList.add("zip");
process(simpleForeach, eventBuilder().message(of(arrayList)).build());
assertSimpleProcessedMessages();
}
@Test
public void arrayPayload() throws Exception {
String[] array = new String[2];
array[0] = "bar";
array[1] = "zip";
process(simpleForeach, eventBuilder().message(of(array)).build());
assertSimpleProcessedMessages();
}
@Test
public void muleMessageCollectionPayload() throws Exception {
List<Message> list = new ArrayList<>();
list.add(of("bar"));
list.add(of("zip"));
process(simpleForeach, eventBuilder().message(of(list)).build());
assertSimpleProcessedMessages();
}
@Test
public void iterablePayload() throws Exception {
Iterable<String> iterable = new DummySimpleIterableClass();
final Event testEvent = eventBuilder().message(of(iterable)).build();
process(simpleForeach, testEvent);
assertSimpleProcessedMessages();
}
@Test
public void iteratorPayload() throws Exception {
Iterable<String> iterable = new DummySimpleIterableClass();
process(simpleForeach, eventBuilder().message(of(iterable.iterator())).build());
assertSimpleProcessedMessages();
}
@Test
public void nestedArrayListPayload() throws Exception {
List<List<String>> payload = new ArrayList<>();
List<String> elem1 = new ArrayList<>();
List<String> elem2 = new ArrayList<>();
List<String> elem3 = new ArrayList<>();
elem1.add("a1");
elem1.add("a2");
elem1.add("a3");
elem2.add("b1");
elem2.add("b2");
elem3.add("c1");
payload.add(elem1);
payload.add(elem2);
payload.add(elem3);
process(nestedForeach, eventBuilder().message(of(payload)).build());
assertNestedProcessedMessages();
}
@Test
public void nestedArrayPayload() throws Exception {
String[][] payload = new String[3][2];
payload[0][0] = "a1";
payload[0][1] = "a2";
payload[1][0] = "a3";
payload[1][1] = "b1";
payload[2][0] = "b2";
payload[2][1] = "c1";
process(nestedForeach, eventBuilder().message(of(payload)).build());
assertNestedProcessedMessages();
}
@Test
public void nestedMuleMessageCollectionPayload() throws Exception {
List<Message> parentList = new ArrayList<>();
List<Message> list1 = new ArrayList<>();
List<Message> list2 = new ArrayList<>();
list1.add(of("a1"));
list1.add(of("a2"));
list1.add(of("a3"));
list2.add(of("b1"));
list2.add(of("b2"));
list2.add(of("c1"));
parentList.add(of(list1));
parentList.add(of(list2));
Message parentCollection = of(parentList);
process(nestedForeach, eventBuilder().message(parentCollection).build());
assertNestedProcessedMessages();
}
@Test
public void nestedIterablePayload() throws Exception {
Iterable iterable = new DummyNestedIterableClass();
process(nestedForeach, eventBuilder().message(of(iterable)).build());
assertNestedProcessedMessages();
}
@Test
public void nestedIteratorPayload() throws Exception {
Iterable iterable = new DummyNestedIterableClass();
process(nestedForeach, eventBuilder().message(of(iterable)).build());
assertNestedProcessedMessages();
}
@Test
public void failingNestedProcessor() throws Exception {
RuntimeException throwable = new BufferOverflowException();
Foreach foreach = new Foreach();
foreach.setMuleContext(muleContext);
Processor failingProcessor = event -> {
throw throwable;
};
foreach.setMessageProcessors(singletonList(failingProcessor));
foreach.initialise();
expectedException.expect(is(MessagingException.class));
expectedException.expect(new FailingProcessorMatcher(failingProcessor));
expectedException.expectCause(is(throwable));
process(foreach, eventBuilder().message(of(new DummyNestedIterableClass().iterator())).build(), false);
}
@Test
public void failingExpression() throws Exception {
Foreach foreach = new Foreach();
foreach.setMuleContext(muleContext);
foreach.setCollectionExpression("!@INVALID");
foreach.setMessageProcessors(getSimpleMessageProcessors());
foreach.initialise();
expectedException.expect(instanceOf(MessagingException.class));
expectedException.expect(new FailingProcessorMatcher(foreach));
expectedException.expectCause(instanceOf(ExpressionRuntimeException.class));
process(foreach, eventBuilder().message(of(new DummyNestedIterableClass().iterator())).build(), false);
}
private void assertSimpleProcessedMessages() {
assertEquals(ERR_NUMBER_MESSAGES, 2, processedEvents.size());
assertTrue(ERR_PAYLOAD_TYPE, processedEvents.get(0).getMessage().getPayload().getValue() instanceof String);
assertTrue(ERR_PAYLOAD_TYPE, processedEvents.get(1).getMessage().getPayload().getValue() instanceof String);
assertEquals(ERR_OUTPUT, "bar:foo:zas", processedEvents.get(0).getMessage().getPayload().getValue());
assertEquals(ERR_OUTPUT, "zip:foo:zas", processedEvents.get(1).getMessage().getPayload().getValue());
}
private void assertNestedProcessedMessages() {
String[] expectedOutputs = {"a1:foo:zas", "a2:foo:zas", "a3:foo:zas", "b1:foo:zas", "b2:foo:zas", "c1:foo:zas"};
assertEquals(ERR_NUMBER_MESSAGES, 6, processedEvents.size());
for (int i = 0; i < processedEvents.size(); i++) {
assertTrue(ERR_PAYLOAD_TYPE, processedEvents.get(i).getMessage().getPayload().getValue() instanceof String);
}
for (int i = 0; i < processedEvents.size(); i++) {
assertEquals(ERR_OUTPUT, expectedOutputs[i], processedEvents.get(i).getMessage().getPayload().getValue());
}
}
public class DummySimpleIterableClass implements Iterable<String> {
public List<String> strings = new ArrayList<>();
public DummySimpleIterableClass() {
strings.add("bar");
strings.add("zip");
}
@Override
public Iterator<String> iterator() {
return strings.iterator();
}
}
private class DummyNestedIterableClass implements Iterable<DummySimpleIterableClass> {
private List<DummySimpleIterableClass> iterables = new ArrayList<>();
public DummyNestedIterableClass() {
DummySimpleIterableClass dsi1 = new DummySimpleIterableClass();
dsi1.strings = new ArrayList<>();
dsi1.strings.add("a1");
dsi1.strings.add("a2");
DummySimpleIterableClass dsi2 = new DummySimpleIterableClass();
dsi2.strings = new ArrayList<>();
dsi2.strings.add("a3");
dsi2.strings.add("b1");
dsi2.strings.add("b2");
dsi2.strings.add("c1");
iterables.add(dsi1);
iterables.add(dsi2);
}
@Override
public Iterator<DummySimpleIterableClass> iterator() {
return iterables.iterator();
}
}
private static class FailingProcessorMatcher extends BaseMatcher<MessagingException> {
private Processor expectedFailingProcessor;
FailingProcessorMatcher(Processor processor) {
this.expectedFailingProcessor = processor;
}
@Override
public boolean matches(Object o) {
return o instanceof MessagingException && ((MessagingException) o).getFailingMessageProcessor() == expectedFailingProcessor;
}
@Override
public void describeTo(Description description) {
description.appendText("Exception is not a MessagingException or failing processor does not match.");
}
}
}