package org.cleverbus.component.funnel;
import org.apache.camel.EndpointInject;
import org.apache.camel.Produce;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.time.DateUtils;
import org.cleverbus.api.asynch.AsynchConstants;
import org.cleverbus.api.entity.Message;
import org.cleverbus.api.entity.MsgStateEnum;
import org.cleverbus.api.route.AbstractBasicRoute;
import org.cleverbus.component.AbstractComponentsDbTest;
import org.hamcrest.CoreMatchers;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import static org.apache.camel.component.mock.MockEndpoint.assertIsSatisfied;
/**
* Test suite for {@link MsgFunnelComponent} with multi funnel value in {@link Message}.
*
* @author Radek Čermák [<a href="mailto:radek.cermak@cleverlance.com">radek.cermak@cleverlance.com</a>]
* @since 2.0.4
*/
@Transactional
public class MsgMultiFunnelComponentTest extends AbstractComponentsDbTest {
private static final String MSG_BODY = "some body";
private static final String FUNNEL_VALUE = "774724557";
private static final String DIFFERENT_FUNNEL_VALUE = "DIFFERENT_FUEL_VALUE";
private static final String MULTI_FUNNEL_VALUE = "MULTI_FUNNEL_VALUE";
private static final String FUNNEL_ID = "myFunnelId";
@Produce(uri = "direct:start")
private ProducerTemplate producer;
@Produce(uri = "direct:startGuaranteed")
private ProducerTemplate producerForGuaranteed;
@Produce(uri = "direct:startGuaranteedWithoutFailed")
private ProducerTemplate producerForGuaranteedWithoutFailed;
@Produce(uri = "direct:startFunnelValue")
private ProducerTemplate producerForFunnelValue;
@Produce(uri = "direct:startFunnelValueGuaranteed")
private ProducerTemplate producerForFunnelValueGuaranteed;
@Produce(uri = "direct:startDifferentFunnelValueGuaranteed")
private ProducerTemplate producerForDifferentFunnelValueGuaranteed;
@EndpointInject(uri = "mock:test")
private MockEndpoint mock;
private Message firstMsg;
@Before
public void prepareMessage() throws Exception {
firstMsg = MsgFunnelComponentTest.createMessage(FUNNEL_VALUE, MULTI_FUNNEL_VALUE);
em.persist(firstMsg);
em.flush();
}
@Before
public void prepareRoutes() throws Exception {
RouteBuilder defaultRoute = new AbstractBasicRoute() {
@Override
public void doConfigure() throws Exception {
from("direct:start")
.to("msg-funnel:default?idleInterval=50&id=" + FUNNEL_ID)
.to("mock:test");
}
};
getCamelContext().addRoutes(defaultRoute);
RouteBuilder guaranteedRoute = new AbstractBasicRoute() {
@Override
public void doConfigure() throws Exception {
from("direct:startGuaranteed")
.to("msg-funnel:default?idleInterval=50&guaranteedOrder=true&id=" + FUNNEL_ID)
.to("mock:test");
}
};
getCamelContext().addRoutes(guaranteedRoute);
RouteBuilder guaranteedWithoutFailedRoute = new AbstractBasicRoute() {
@Override
public void doConfigure() throws Exception {
from("direct:startGuaranteedWithoutFailed")
.to("msg-funnel:default?idleInterval=50&guaranteedOrder=true&excludeFailedState=true&id=" + FUNNEL_ID)
.to("mock:test");
}
};
getCamelContext().addRoutes(guaranteedWithoutFailedRoute);
RouteBuilder funnelValueRoute = new AbstractBasicRoute() {
@Override
public void doConfigure() throws Exception {
from("direct:startFunnelValue")
.to("msg-funnel:default?idleInterval=50&id=" + FUNNEL_ID + "&funnelValue=" + FUNNEL_VALUE)
.to("mock:test");
}
};
getCamelContext().addRoutes(funnelValueRoute);
RouteBuilder guaranteedFunnelValueRoute = new AbstractBasicRoute() {
@Override
public void doConfigure() throws Exception {
from("direct:startFunnelValueGuaranteed")
.to("msg-funnel:default?idleInterval=50&guaranteedOrder=true&id=" + FUNNEL_ID
+ "&funnelValue=" + FUNNEL_VALUE)
.to("mock:test");
}
};
getCamelContext().addRoutes(guaranteedFunnelValueRoute);
RouteBuilder guaranteedDifferentFunnelValueRoute = new AbstractBasicRoute() {
@Override
public void doConfigure() throws Exception {
from("direct:startDifferentFunnelValueGuaranteed")
.to("msg-funnel:default?idleInterval=50&guaranteedOrder=true&id=" + FUNNEL_ID
+ "&funnelValue=" + DIFFERENT_FUNNEL_VALUE)
.to("mock:test");
}
};
getCamelContext().addRoutes(guaranteedDifferentFunnelValueRoute);
}
@Test
public void testMultiFunnelValue() throws Exception {
mock.setExpectedMessageCount(0);
Message msg = MsgFunnelComponentTest.createMessage(FUNNEL_VALUE, MULTI_FUNNEL_VALUE, DIFFERENT_FUNNEL_VALUE);
em.persist(msg);
em.flush();
// send message with same funnel value => postpone it
producer.sendBodyAndHeader(MSG_BODY, AsynchConstants.MSG_HEADER, msg);
assertIsSatisfied(mock);
Assert.assertThat(em.find(Message.class, msg.getMsgId()).getState(), CoreMatchers.is(MsgStateEnum.POSTPONED));
}
@Test
public void testFunnel_waitingForResponse() throws Exception {
mock.setExpectedMessageCount(1);
Message msg = MsgFunnelComponentTest.createMessage(MULTI_FUNNEL_VALUE);
msg.setStartProcessTimestamp(DateUtils.addSeconds(new Date(), -MsgFunnelEndpoint.DEFAULT_IDLE_INTERVAL - 100));
msg.setState(MsgStateEnum.WAITING_FOR_RES);
em.persist(msg);
em.flush();
producer.sendBodyAndHeader(MSG_BODY, AsynchConstants.MSG_HEADER, msg);
assertIsSatisfied(mock);
Assert.assertThat(em.find(Message.class, msg.getMsgId()).getState(),
CoreMatchers.is(MsgStateEnum.WAITING_FOR_RES));
}
@Test
public void testFunnel_noFilter() throws Exception {
mock.setExpectedMessageCount(1);
Message msg = MsgFunnelComponentTest.createMessage("777123456", "777999111");
// send message with different funnel value
producer.sendBodyAndHeader(MSG_BODY, AsynchConstants.MSG_HEADER, msg);
mock.assertIsSatisfied();
}
@Test
public void testFunnelForGuaranteedOrder_onlyCurrentMessage() throws Exception {
mock.setExpectedMessageCount(1);
// send one message only
producerForGuaranteed.sendBodyAndHeader(MSG_BODY, AsynchConstants.MSG_HEADER, firstMsg);
assertIsSatisfied(mock);
}
@Test
public void testFunnelForGuaranteedOrder_firstMessage() throws Exception {
mock.setExpectedMessageCount(1);
Message msg = MsgFunnelComponentTest.createMessage(FUNNEL_VALUE, DIFFERENT_FUNNEL_VALUE);
msg.setMsgTimestamp(DateUtils.addSeconds(firstMsg.getMsgTimestamp(), -100)); // be before "first" message
msg.setState(MsgStateEnum.PROCESSING);
em.persist(msg);
em.flush();
// send message has "msgTimestamp" before another processing message
producerForGuaranteed.sendBodyAndHeader(MSG_BODY, AsynchConstants.MSG_HEADER, msg);
assertIsSatisfied(mock);
Assert.assertThat(em.find(Message.class, msg.getMsgId()).getState(), CoreMatchers.is(MsgStateEnum.PROCESSING));
}
@Test
public void testFunnelForGuaranteedOrder_postponeMessage() throws Exception {
mock.setExpectedMessageCount(0);
Message msg = MsgFunnelComponentTest.createMessage(FUNNEL_VALUE, DIFFERENT_FUNNEL_VALUE);
msg.setMsgTimestamp(DateUtils.addSeconds(firstMsg.getMsgTimestamp(), 100)); // be after "first" message
msg.setState(MsgStateEnum.PROCESSING);
em.persist(msg);
em.flush();
// send message has "msgTimestamp" after another processing message => postpone it
producerForGuaranteed.sendBodyAndHeader(MSG_BODY, AsynchConstants.MSG_HEADER, msg);
assertIsSatisfied(mock);
Assert.assertThat(em.find(Message.class, msg.getMsgId()).getState(), CoreMatchers.is(MsgStateEnum.POSTPONED));
}
@Test
public void testFunnelForOwnFunnelValue() throws Exception {
mock.setExpectedMessageCount(0);
//message without funnel value
Message msg = MsgFunnelComponentTest.createMessage(DIFFERENT_FUNNEL_VALUE);
em.persist(msg);
em.flush();
// send message with setting funnel value in route => postpone it
producerForFunnelValue.sendBodyAndHeader(MSG_BODY, AsynchConstants.MSG_HEADER, msg);
assertIsSatisfied(mock);
msg = em.find(Message.class, msg.getMsgId());
Assert.assertNotNull(msg);
Assert.assertThat(msg.getState(), CoreMatchers.is(MsgStateEnum.POSTPONED));
Assert.assertEquals(2, msg.getFunnelValues().size());
Assert.assertTrue(CollectionUtils.isEqualCollection(msg.getFunnelValues(),
Arrays.asList(FUNNEL_VALUE, DIFFERENT_FUNNEL_VALUE)));
}
@Test
public void testFunnelForOwnFunnelValueGuaranteed_Postponed() throws Exception {
mock.setExpectedMessageCount(0);
Message msg = MsgFunnelComponentTest.createMessage(FUNNEL_VALUE);
msg.setMsgTimestamp(DateUtils.addSeconds(firstMsg.getMsgTimestamp(), 100)); // be after "first" message
msg.setState(MsgStateEnum.PROCESSING);
em.persist(msg);
em.flush();
// input message to route with different funnel value
producerForDifferentFunnelValueGuaranteed.sendBodyAndHeader(MSG_BODY, AsynchConstants.MSG_HEADER, msg);
mock.assertIsSatisfied();
msg = em.find(Message.class, msg.getMsgId());
Assert.assertNotNull(msg);
Assert.assertThat(msg.getState(), CoreMatchers.is(MsgStateEnum.POSTPONED));
Assert.assertTrue(CollectionUtils.isEqualCollection(msg.getFunnelValues(),
Arrays.asList(FUNNEL_VALUE, DIFFERENT_FUNNEL_VALUE)));
}
@Test
public void testFunnelForOwnFunnelValueGuaranteed_Processing() throws Exception {
mock.setExpectedMessageCount(1);
Message msg = MsgFunnelComponentTest.createMessage("777999888");
msg.setMsgTimestamp(DateUtils.addSeconds(firstMsg.getMsgTimestamp(), 100)); // be after "first" message
msg.setState(MsgStateEnum.PROCESSING);
em.persist(msg);
em.flush();
// input message to route with different funnel value
producerForDifferentFunnelValueGuaranteed.sendBodyAndHeader(MSG_BODY, AsynchConstants.MSG_HEADER, msg);
mock.assertIsSatisfied();
msg = em.find(Message.class, msg.getMsgId());
Assert.assertNotNull(msg);
Assert.assertThat(msg.getState(), CoreMatchers.is(MsgStateEnum.PROCESSING));
Assert.assertEquals(2, msg.getFunnelValues().size());
Assert.assertTrue(CollectionUtils.isEqualCollection(msg.getFunnelValues(),
Arrays.asList("777999888", DIFFERENT_FUNNEL_VALUE)));
}
@Test
public void testFunnelForOwnFunnelValueGuaranteed_firstMessage() throws Exception {
mock.setExpectedMessageCount(1);
Message msg = MsgFunnelComponentTest.createMessage(DIFFERENT_FUNNEL_VALUE);
msg.setMsgTimestamp(DateUtils.addSeconds(firstMsg.getMsgTimestamp(), -100)); // be before "first" message
msg.setState(MsgStateEnum.PROCESSING);
em.persist(msg);
em.flush();
// send message has "msgTimestamp" before another processing message
producerForFunnelValueGuaranteed.sendBodyAndHeader(MSG_BODY, AsynchConstants.MSG_HEADER, msg);
assertIsSatisfied(mock);
msg = em.find(Message.class, msg.getMsgId());
Assert.assertNotNull(msg);
Assert.assertThat(msg.getState(), CoreMatchers.is(MsgStateEnum.PROCESSING));
Assert.assertEquals(2, msg.getFunnelValues().size());
Assert.assertTrue(CollectionUtils.isEqualCollection(msg.getFunnelValues(),
Arrays.asList(FUNNEL_VALUE, DIFFERENT_FUNNEL_VALUE)));
}
@Test
public void testFunnelForOwnFunnelValueGuaranteed_postponeMessage() throws Exception {
mock.setExpectedMessageCount(0);
Message msg = MsgFunnelComponentTest.createMessage(DIFFERENT_FUNNEL_VALUE);
msg.setMsgTimestamp(DateUtils.addSeconds(firstMsg.getMsgTimestamp(), 100)); // be after "first" message
msg.setState(MsgStateEnum.PROCESSING);
em.persist(msg);
em.flush();
// send message has "msgTimestamp" after another processing message => postpone it
producerForFunnelValueGuaranteed.sendBodyAndHeader(MSG_BODY, AsynchConstants.MSG_HEADER, msg);
assertIsSatisfied(mock);
msg = em.find(Message.class, msg.getMsgId());
Assert.assertNotNull(msg);
Assert.assertThat(msg.getState(), CoreMatchers.is(MsgStateEnum.POSTPONED));
Assert.assertEquals(2, msg.getFunnelValues().size());
Assert.assertTrue(CollectionUtils.isEqualCollection(msg.getFunnelValues(),
Arrays.asList(FUNNEL_VALUE, DIFFERENT_FUNNEL_VALUE)));
}
@Test
public void testFunnelValueGuaranteed_multiFunnelMessage() throws Exception {
mock.setExpectedMessageCount(1);
Message msgFirst = MsgFunnelComponentTest.createMessage(MULTI_FUNNEL_VALUE);
msgFirst.setMsgTimestamp(DateUtils.addSeconds(firstMsg.getMsgTimestamp(), 100)); // be after "first" message
em.persist(msgFirst);
em.flush();
//send first message
producerForGuaranteed.sendBodyAndHeader(MSG_BODY, AsynchConstants.MSG_HEADER, msgFirst);
Message msgSecond = MsgFunnelComponentTest.createMessage(FUNNEL_VALUE, DIFFERENT_FUNNEL_VALUE);
msgSecond.setMsgTimestamp(DateUtils.addSeconds(firstMsg.getMsgTimestamp(), 150)); // be after "first" message
em.persist(msgSecond);
em.flush();
//send second message
producerForGuaranteed.sendBodyAndHeader(MSG_BODY, AsynchConstants.MSG_HEADER, msgSecond);
Message msgThird = MsgFunnelComponentTest.createMessage(DIFFERENT_FUNNEL_VALUE);
msgThird.setMsgTimestamp(DateUtils.addSeconds(firstMsg.getMsgTimestamp(), 200)); // be after "first" message
em.persist(msgThird);
em.flush();
//send third message
producerForGuaranteed.sendBodyAndHeader(MSG_BODY, AsynchConstants.MSG_HEADER, msgThird);
Message msgFourth = MsgFunnelComponentTest.createMessage("777111222");
msgFourth.setMsgTimestamp(DateUtils.addSeconds(firstMsg.getMsgTimestamp(), 250)); // be after "first" message
em.persist(msgFourth);
em.flush();
//send fourth message
producerForGuaranteed.sendBodyAndHeader(MSG_BODY, AsynchConstants.MSG_HEADER, msgFourth);
assertIsSatisfied(mock);
msgFirst = em.find(Message.class, msgFirst.getMsgId());
Assert.assertNotNull(msgFirst);
Assert.assertThat(msgFirst.getState(), CoreMatchers.is(MsgStateEnum.POSTPONED));
Assert.assertEquals(1, msgFirst.getFunnelValues().size());
Assert.assertTrue(CollectionUtils.isEqualCollection(msgFirst.getFunnelValues(),
Collections.singletonList(MULTI_FUNNEL_VALUE)));
msgSecond = em.find(Message.class, msgSecond.getMsgId());
Assert.assertNotNull(msgSecond);
Assert.assertThat(msgSecond.getState(), CoreMatchers.is(MsgStateEnum.POSTPONED));
Assert.assertEquals(2, msgSecond.getFunnelValues().size());
Assert.assertTrue(CollectionUtils.isEqualCollection(msgSecond.getFunnelValues(),
Arrays.asList(FUNNEL_VALUE, DIFFERENT_FUNNEL_VALUE)));
msgThird = em.find(Message.class, msgThird.getMsgId());
Assert.assertNotNull(msgThird);
Assert.assertThat(msgThird.getState(), CoreMatchers.is(MsgStateEnum.POSTPONED));
Assert.assertEquals(1, msgThird.getFunnelValues().size());
Assert.assertTrue(CollectionUtils.isEqualCollection(msgThird.getFunnelValues(),
Collections.singletonList(DIFFERENT_FUNNEL_VALUE)));
msgFourth = em.find(Message.class, msgFourth.getMsgId());
Assert.assertNotNull(msgFourth);
Assert.assertThat(msgFourth.getState(), CoreMatchers.is(MsgStateEnum.PROCESSING));
Assert.assertEquals(1, msgFourth.getFunnelValues().size());
Assert.assertTrue(CollectionUtils.isEqualCollection(msgFourth.getFunnelValues(),
Collections.singletonList("777111222")));
}
}