package net.frontlinesms.camel.smslib;
import static net.frontlinesms.camel.smslib.CamelSmslibTestUtils.*;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import net.frontlinesms.camel.smslib.SmslibService.ReceiveThread;
import org.apache.camel.spi.ExceptionHandler;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.smslib.CIncomingMessage;
import org.smslib.COutgoingMessage;
import org.smslib.CService;
import org.smslib.NotConnectedException;
import org.smslib.service.MessageClass;
import serial.mock.MockSerial;
import serial.NoSuchPortException;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/** Unit test for {@link SmslibService} */
public class SmslibServiceTest {
CService cServiceMock;
CService cServiceMock2;
CServiceFactory cServiceFactory;
Map<String, Object> parameters;
String remaining;
String uri;
/** Class under test */
SmslibService service;
@Before
public void setUp() {
MockSerial.init();
uri = "asdf";
remaining = "hjkl";
parameters = new HashMap<String, Object>();
cServiceMock = mock(CService.class);
cServiceMock2 = mock(CService.class);
cServiceFactory = mock(CServiceFactory.class);
when(cServiceFactory.create(uri, remaining, parameters)).thenReturn(cServiceMock, cServiceMock2);
service = new SmslibService(cServiceFactory, uri, remaining, parameters);
}
@After
public void tearDown() {
cServiceMock = null;
cServiceFactory = null;
parameters = null;
remaining = null;
uri = null;
service = null;
}
@Test
public void cServiceShouldBeInitialisedNull() {
assertNull(service.getCService());
}
@Test
public void startingProducerShouldInitialiseCService() throws Exception {
// when
service.setProducer(mockProducer());
service.startForProducer();
// then
assertEquals(cServiceMock, service.getCService());
}
@Test
public void startingConsumerShouldInitialiseCService() throws Exception {
// when
service.setConsumer(mockConsumer());
service.startForConsumer();
// then
assertEquals(cServiceMock, service.getCService());
}
@Test
public void startingConsumerWhenProducerAlreadyStartedShouldNotReinitialiseCservice() throws Exception {
// given
service.setProducer(mockProducer());
service.startForProducer();
// when
service.setConsumer(mockConsumer());
service.startForConsumer();
// then
verify(cServiceFactory).create(uri, remaining, parameters);
assertEquals(cServiceMock, service.getCService());
}
@Test
public void startingProducerWhenConsumerAlreadyStartedShouldNotReinitialiseCservice() throws Exception {
// given
service.setConsumer(mockConsumer());
service.startForConsumer();
// when
service.setProducer(mockProducer());
service.startForProducer();
// then
verify(cServiceFactory).create(uri, remaining, parameters);
assertEquals(cServiceMock, service.getCService());
}
@Test
public void startingProducerAfterCserviceDiscardShouldCreateNewCservice() throws Exception {
// given
SmslibProducer mockProducer = mockProducer();
service.setProducer(mockProducer);
service.startForProducer();
service.stopForProducer(mockProducer);
// when
service.setProducer(mockProducer());
service.startForProducer();
// then
verify(cServiceFactory, times(2)).create(uri, remaining, parameters);
assertEquals(cServiceMock2, service.getCService());
}
@Test
public void settingConsumerAfterCserviceDiscardShouldCreateNewCservice() throws Exception {
// given
SmslibConsumer mockConsumer = mockConsumer();
service.setConsumer(mockConsumer);
service.startForConsumer();
service.stopForConsumer(mockConsumer);
// when
service.setConsumer(mockConsumer());
service.startForConsumer();
// then
verify(cServiceFactory, times(2)).create(uri, remaining, parameters);
assertEquals(cServiceMock2, service.getCService());
}
@Test
public void receiveThreadShouldBeInitialisedNull() throws Exception {
assertNull(service.getReceiveThread());
}
public void settingConsumerShouldInitialiseReceiveThread() throws Exception {
// when
service.setConsumer(mockConsumer());
// then
assertNotNull(service.getReceiveThread());
}
@Test
public void stoppingConsumerShouldStopReceiveThread() throws Exception {
// given
SmslibConsumer mockConsumer = mockConsumer();
service.setConsumer(mockConsumer);
service.startForConsumer();
ReceiveThread receiveThread = service.getReceiveThread();
assertFalse(receiveThread.isFinished());
// when
service.stopForConsumer(mockConsumer);
// then
assertTrue(receiveThread.isFinished());
}
public void stoppingConsumerShouldDiscardOldReceiveThread() throws Exception {
// given
SmslibConsumer mockConsumer = mockConsumer();
service.setConsumer(mockConsumer);
service.startForConsumer();
// when
service.stopForConsumer(mockConsumer);
// then
assertNull(service.getReceiveThread());
}
@Test
public void stoppingConsumerShouldBlockUntilReceiveThreadDies() {
// TODO we should test this... when we've had more coffee
}
@Test
public void stoppingForConsumerShouldDiscardTheConsumer() throws Exception {
// given
SmslibConsumer mockConsumer = mockConsumer();
service.setConsumer(mockConsumer);
service.startForConsumer();
// when
service.stopForConsumer(mockConsumer);
// then
assertNull(service.getConsumer());
}
@Test
public void stoppingForProducerShouldDiscardTheProducer() throws Exception {
// given
SmslibProducer mockProducer = mockProducer();
service.setProducer(mockProducer);
service.startForProducer();
// when
service.stopForProducer(mockProducer);
// then
assertNull(service.getProducer());
}
@Test
public void testCServiceStart() throws Exception {
// given
SmslibProducer mockProducer = mockProducer();
service.setProducer(mockProducer);
// when
service.startForProducer();
// then
verify(cServiceMock).connect();
}
@Test
public void testCServiceNoStartIfAlreadyStarted() throws Exception {
// given
SmslibProducer mockProducer = mockProducer();
service.setProducer(mockProducer);
when(cServiceMock.isConnected()).thenReturn(true); // TODO this assumes that CService.isConnected() returns true while TRYING to conenct...
// when
service.startForProducer();
// then
verify(cServiceMock, never()).connect();
}
@Test
public void whenProducerAndConsumerBothStoppedCserviceShouldDisconnect() throws Exception {
// given
SmslibProducer mockProducer = mockProducer();
service.setProducer(mockProducer);
when(cServiceMock.isConnected()).thenReturn(true);
service.startForProducer();
// when
service.stopForProducer(mockProducer);
// verify
verify(cServiceMock).disconnect();
}
@Test
public void whenProducerAndConsumerBothStoppedCserviceShouldBeDiscarded() throws Exception {
// given
SmslibProducer mockProducer = mockProducer();
service.setProducer(mockProducer);
when(cServiceMock.isConnected()).thenReturn(true);
service.startForProducer();
// when
service.stopForProducer(mockProducer);
// then
assertNull(service.getCService());
}
@Test
public void testCServiceNoStopIfOtherUsers() throws Exception {
// given
SmslibProducer mockProducer = mockProducer();
service.setProducer(mockProducer);
service.startForProducer();
service.setConsumer(mockConsumer());
service.startForConsumer();
when(cServiceMock.isConnected()).thenReturn(true);
// when
service.stopForProducer(mockProducer);
// verify
verify(cServiceMock, never()).disconnect();
}
@Test
public void testCServiceStopIfNoMoreUsers() throws Exception {
// given
SmslibProducer producer = setupAndStartProducer();
SmslibConsumer consumer = setupAndStartConsumer();
when(cServiceMock.isConnected()).thenReturn(true);
// when
service.stopForProducer(producer);
verify(cServiceMock, never()).disconnect();
// and
service.stopForConsumer(consumer);
// verify
verify(cServiceMock).disconnect();
}
@Test
public void testMaxOneProducer() throws Exception {
// given
service.setProducer(mockProducer());
// when then
try {
service.setProducer(mockProducer());
fail("Should not be able to start for more than one producer.");
} catch(SmslibServiceException ex) {
// expected
}
}
@Test
public void testMaxOneConsumer() throws Exception {
// given
service.setConsumer(mockConsumer());
// when then
try {
service.setConsumer(mockConsumer());
fail("Should not be able to start for more than one producer.");
} catch(SmslibServiceException ex) {
// expected
}
}
@Test
public void testSend() throws Exception {
// given
COutgoingMessage cOutgoingMessageMock = mock(COutgoingMessage.class);
OutgoingSmslibCamelMessage camelMessage = new OutgoingSmslibCamelMessage(cOutgoingMessageMock);
setupAndStartProducer();
// when
service.send(camelMessage);
// then
verify(cServiceMock).sendMessage(cOutgoingMessageMock);
}
@Test
public void testSendFailureHandlingCmsError() throws Exception {
// given
COutgoingMessage smslibMessage = mock(COutgoingMessage.class);
when(smslibMessage.getRefNo()).thenReturn(-1);
OutgoingSmslibCamelMessage camelMessage = new OutgoingSmslibCamelMessage(smslibMessage);
setupAndStartProducer();
// when
try {
service.send(camelMessage);
fail();
} catch(MessageRejectedException expected) {
// then exception expected
}
}
@Test
public void testSendFailureHandlingForCserviceStopped() throws Exception {
// given
COutgoingMessage smslibMessage = mock(COutgoingMessage.class);
OutgoingSmslibCamelMessage camelMessage = new OutgoingSmslibCamelMessage(smslibMessage);
doThrow(new ConnectionFailedException()).when(cServiceMock).sendMessage(smslibMessage);
setupAndStartProducer();
// when then
try {
service.send(camelMessage);
} catch(ConnectionFailedException e) {
// expected
}
}
@Test
@SuppressWarnings({ "unchecked", "rawtypes" })
public void testReceive() throws Exception {
// given
SmslibConsumer consumerMock = mockConsumer();
service.setConsumer(consumerMock);
inject(service, "cService", cServiceMock);
doAnswer(new Answer() {
public Object answer(InvocationOnMock inv) {
LinkedList<CIncomingMessage> messageList =
(LinkedList<CIncomingMessage>) inv.getArguments()[0];
for(int i=0; i<3; ++i) messageList.add(mock(CIncomingMessage.class));
return null;
}
}).when(cServiceMock).readMessages(any(LinkedList.class), eq(MessageClass.ALL));
// when
service.doReceive();
// then
verify(consumerMock, times(3)).accept(any(IncomingSmslibCamelMessage.class));
// when
reset(consumerMock);
service.setReceiveMessageClass(MessageClass.UNREAD);
service.doReceive();
// then
verify(consumerMock, never()).accept(any(IncomingSmslibCamelMessage.class));
// when
reset(consumerMock);
service.setReceiveMessageClass(MessageClass.ALL);
service.doReceive();
// then
verify(consumerMock, times(3)).accept(any(IncomingSmslibCamelMessage.class));
}
@Test
@SuppressWarnings({ "unchecked", "rawtypes" })
public void testReceiveUnread() throws Exception {
// given
parameters.put("receiveMessageClass", MessageClass.UNREAD);
service = new SmslibService(cServiceFactory, uri, remaining, parameters);
// given
SmslibConsumer consumerMock = setupAndStartConsumer();;
doAnswer(new Answer() {
public Object answer(InvocationOnMock inv) {
LinkedList<CIncomingMessage> messageList =
(LinkedList<CIncomingMessage>) inv.getArguments()[0];
for(int i=0; i<3; ++i) messageList.add(mock(CIncomingMessage.class));
return null;
}
}).when(cServiceMock).readMessages(any(LinkedList.class), eq(MessageClass.UNREAD));
// when
service.doReceive();
// then
verify(consumerMock, times(3)).accept(any(IncomingSmslibCamelMessage.class));
}
@Test
public void ifStartForProducerFailsThenProducerShouldBeNull() throws Exception {
// given
SmslibProducer prod = mockProducer();
service.setProducer(prod);
doThrow(new NoSuchPortException(new RuntimeException())).when(cServiceMock).connect();
// when
try {
service.startForProducer();
fail(".startForProducer() should have thrown serial Exception");
} catch(NoSuchPortException expected) {}
// then
assertNull(service.getProducer());
assertFalse(service.isProducerRunning());
}
@Test
public void notConnectedExceptionShouldPropogateToExceptionHandler() throws Exception {
// given
SmslibConsumer consumer = mockConsumer();
service.setConsumer(consumer);
NotConnectedException notConnectedException = new NotConnectedException();
doThrow(notConnectedException)
.when(cServiceMock)
.readMessages(any(LinkedList.class), any(MessageClass.class));
inject(service, "cService", cServiceMock);
// when
service.doReceive();
// then
verify(consumer).handleDisconnection(notConnectedException);
}
//> TEST HELPER METHODS
private SmslibProducer mockProducer() {
return mock(SmslibProducer.class);
}
private SmslibConsumer mockConsumer() {
return mock(SmslibConsumer.class);
}
private SmslibProducer setupAndStartProducer() throws Exception {
SmslibProducer producer = mockProducer();
service.setProducer(producer);
service.startForProducer();
return producer;
}
private SmslibConsumer setupAndStartConsumer() throws Exception {
SmslibConsumer consumer = mockConsumer();
service.setConsumer(consumer);
service.startForConsumer();
return consumer;
}
}