package com.b2msolutions.reyna.messageProvider; import android.app.AlarmManager; import android.content.Context; import com.b2msolutions.reyna.*; import com.b2msolutions.reyna.system.Header; import com.b2msolutions.reyna.system.Message; import com.b2msolutions.reyna.system.PeriodicBackoutCheck; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.mockito.Mockito.*; import com.google.gson.Gson; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; @RunWith(RobolectricTestRunner.class) public class BatchProviderTest { private BatchProvider messageProvider; @Mock Repository repository; @Mock BatchConfiguration batchConfiguration; @Mock PeriodicBackoutCheck periodicBackoutCheck; private Context context; @Before public void setup() { MockitoAnnotations.initMocks(this); this.context = RuntimeEnvironment.application.getApplicationContext(); this.messageProvider = new BatchProvider(this.context, this.repository); this.messageProvider.batchConfiguration = this.batchConfiguration; this.messageProvider.periodicBackoutCheck = this.periodicBackoutCheck; doReturn(3).when(this.batchConfiguration).getBatchMessageCount(); doReturn(1000L).when(this.batchConfiguration).getBatchMessagesSize(); doReturn(URI.create("www.post.com/api/batch")).when(this.batchConfiguration).getBatchUrl(); doReturn(AlarmManager.INTERVAL_DAY).when(this.batchConfiguration).getSubmitInterval(); } @Test public void testConstruction() { this.messageProvider = new BatchProvider(this.context, this.repository); assertNotNull(this.messageProvider); assertNotNull(this.messageProvider.repository); assertNotNull(this.messageProvider.batchConfiguration); assertNotNull(this.messageProvider.periodicBackoutCheck); } @Test public void whenCallingCloseShouldCloseRepository() throws URISyntaxException { this.messageProvider.close(); verify(this.repository).close(); } @Test public void whenCallingCloseAndNeverSendSuccessfulBatchShouldNotRecord() throws URISyntaxException { this.messageProvider.close(); verify(this.repository).close(); verify(this.periodicBackoutCheck,never()).record("BatchProvider"); } @Test public void whenCallingCloseAndSuccessfullySentBatchShouldRecord() throws URISyntaxException { Message message = mock(Message.class); when(message.getId()).thenReturn(42L); this.messageProvider.delete(message); this.messageProvider.close(); verify(this.repository).close(); verify(this.periodicBackoutCheck).record("BatchProvider"); } @Test public void whenCallingDeleteShouldDeleteFromRepository() throws URISyntaxException { Message message = mock(Message.class); when(message.getId()).thenReturn(42L); this.messageProvider.delete(message); verify(this.repository).deleteMessagesFrom(message.getId()); } @Test public void whenCallingGetNextAndNoMessageShouldReturnNull() throws URISyntaxException { Message actual = this.messageProvider.getNext(); assertNull(actual); } @Test public void whenCallingGetNextShouldReturnCorrectFormat() throws URISyntaxException { ArrayList<Message> messages = this.getTestMessages(); when(this.repository.getNext()).thenReturn(messages.get(0)); when(this.repository.getNextMessageAfter(1L)).thenReturn(messages.get(1)); when(this.repository.getNextMessageAfter(2L)).thenReturn(messages.get(2)); when(this.repository.getNextMessageAfter(3L)).thenReturn(null); Message actual = this.messageProvider.getNext(); assertNotNull(actual); assertEquals("www.post.com/api/batch", actual.getUrl()); assertEquals("{\"events\":[" + "{\"url\":\"http://google.com\",\"reynaId\":1,\"payload\":{\"key01\":\"value01\",\"key02\":11}}," + "{\"url\":\"http://google2.com\",\"reynaId\":2,\"payload\":{\"key11\":\"value11\",\"key12\":12}}," + "{\"url\":\"http://google3.com\",\"reynaId\":3,\"payload\":{\"key21\":\"value21\",\"key22\":22}}" + "]}", actual.getBody()); this.assertHeaders(actual); Batch batch = new Gson().fromJson(actual.getBody(), Batch.class); assertEquals(1L, batch.getEvents().get(0).getReynaId()); assertEquals("http://google.com", batch.getEvents().get(0).getUrl()); assertEquals("{\"key01\":\"value01\",\"key02\":11}", batch.getEvents().get(0).getPayload().toString()); assertEquals(2L, batch.getEvents().get(1).getReynaId()); assertEquals("http://google2.com", batch.getEvents().get(1).getUrl()); assertEquals("{\"key11\":\"value11\",\"key12\":12}", batch.getEvents().get(1).getPayload().toString()); assertEquals(3L, batch.getEvents().get(2).getReynaId()); assertEquals("http://google3.com", batch.getEvents().get(2).getUrl()); assertEquals("{\"key21\":\"value21\",\"key22\":22}", batch.getEvents().get(2).getPayload().toString()); } @Test public void whenCallingGetNextAndThereIsCorruptedMessageShouldPostIt() throws URISyntaxException { Message message = new Message(2L, URI.create("http://google2.com"), "{\"key11\":", getTestMessageHeaders()); ArrayList<Message> messages = this.getTestMessages(); messages.remove(1); messages.add(1,message); when(this.repository.getNext()).thenReturn(messages.get(0)); when(this.repository.getNextMessageAfter(1L)).thenReturn(messages.get(1)); when(this.repository.getNextMessageAfter(2L)).thenReturn(messages.get(2)); when(this.repository.getNextMessageAfter(3L)).thenReturn(null); Message actual = this.messageProvider.getNext(); assertNotNull(actual); assertEquals("www.post.com/api/batch", actual.getUrl()); assertEquals("{\"events\":[" + "{\"url\":\"http://google.com\",\"reynaId\":1,\"payload\":{\"key01\":\"value01\",\"key02\":11}}," + "{\"url\":\"http://google2.com\",\"reynaId\":2,\"payload\":{\"body\":\"{\\\"key11\\\":\"}}," + "{\"url\":\"http://google3.com\",\"reynaId\":3,\"payload\":{\"key21\":\"value21\",\"key22\":22}}" + "]}", actual.getBody()); assertEquals(3L, actual.getId().longValue()); this.assertHeaders(actual); Batch batch = new Gson().fromJson(actual.getBody(), Batch.class); assertEquals(1L, batch.getEvents().get(0).getReynaId()); assertEquals("http://google.com", batch.getEvents().get(0).getUrl()); assertEquals("{\"key01\":\"value01\",\"key02\":11}", batch.getEvents().get(0).getPayload().toString()); assertEquals(3L, batch.getEvents().get(2).getReynaId()); assertEquals("http://google3.com", batch.getEvents().get(2).getUrl()); assertEquals("{\"key21\":\"value21\",\"key22\":22}", batch.getEvents().get(2).getPayload().toString()); } @Test public void whenCallingGetNextShouldReturnMessagesRelatedToMaximumLimit() throws URISyntaxException { doReturn(2).when(this.batchConfiguration).getBatchMessageCount(); ArrayList<Message> messages = this.getTestMessages(); when(this.repository.getNext()).thenReturn(messages.get(0)); when(this.repository.getNextMessageAfter(1L)).thenReturn(messages.get(1)); when(this.repository.getNextMessageAfter(2L)).thenReturn(messages.get(2)); when(this.repository.getNextMessageAfter(3L)).thenReturn(null); Message actual = this.messageProvider.getNext(); assertNotNull(actual); assertEquals("www.post.com/api/batch", actual.getUrl()); assertEquals("{\"events\":[" + "{\"url\":\"http://google.com\",\"reynaId\":1,\"payload\":{\"key01\":\"value01\",\"key02\":11}}," + "{\"url\":\"http://google2.com\",\"reynaId\":2,\"payload\":{\"key11\":\"value11\",\"key12\":12}}" + "]}", actual.getBody()); this.assertHeaders(actual); Batch batch = new Gson().fromJson(actual.getBody(), Batch.class); assertEquals(1L, batch.getEvents().get(0).getReynaId()); assertEquals("http://google.com", batch.getEvents().get(0).getUrl()); assertEquals("{\"key01\":\"value01\",\"key02\":11}", batch.getEvents().get(0).getPayload().toString()); assertEquals(2L, batch.getEvents().get(1).getReynaId()); assertEquals("http://google2.com", batch.getEvents().get(1).getUrl()); assertEquals("{\"key11\":\"value11\",\"key12\":12}", batch.getEvents().get(1).getPayload().toString()); } @Test public void whenCallingGetNextAndThereIsStringMessageShouldPostIt() throws URISyntaxException { Message message = new Message(2L, URI.create("http://google2.com"), "Message body", getTestMessageHeaders()); ArrayList<Message> messages = this.getTestMessages(); messages.remove(1); messages.add(1,message); when(this.repository.getNext()).thenReturn(messages.get(0)); when(this.repository.getNextMessageAfter(1L)).thenReturn(messages.get(1)); when(this.repository.getNextMessageAfter(2L)).thenReturn(messages.get(2)); when(this.repository.getNextMessageAfter(3L)).thenReturn(null); Message actual = this.messageProvider.getNext(); assertNotNull(actual); assertEquals("www.post.com/api/batch", actual.getUrl()); assertEquals("{\"events\":[" + "{\"url\":\"http://google.com\",\"reynaId\":1,\"payload\":{\"key01\":\"value01\",\"key02\":11}}," + "{\"url\":\"http://google2.com\",\"reynaId\":2,\"payload\":{\"body\":\"Message body\"}}," + "{\"url\":\"http://google3.com\",\"reynaId\":3,\"payload\":{\"key21\":\"value21\",\"key22\":22}}" + "]}", actual.getBody()); this.assertHeaders(actual); Batch batch = new Gson().fromJson(actual.getBody(), Batch.class); assertEquals(1L, batch.getEvents().get(0).getReynaId()); assertEquals("http://google.com", batch.getEvents().get(0).getUrl()); assertEquals("{\"key01\":\"value01\",\"key02\":11}", batch.getEvents().get(0).getPayload().toString()); assertEquals(2L, batch.getEvents().get(1).getReynaId()); assertEquals("http://google2.com", batch.getEvents().get(1).getUrl()); assertEquals("{\"body\":\"Message body\"}", batch.getEvents().get(1).getPayload().toString()); assertEquals(3L, batch.getEvents().get(2).getReynaId()); assertEquals("http://google3.com", batch.getEvents().get(2).getUrl()); assertEquals("{\"key21\":\"value21\",\"key22\":22}", batch.getEvents().get(2).getPayload().toString()); } @Test public void whenCallingGetNextShouldReturnMessagesRelatedToMaximumSize() throws URISyntaxException { doReturn(95L).when(this.batchConfiguration).getBatchMessagesSize(); ArrayList<Message> messages = this.getTestMessages(); when(this.repository.getNext()).thenReturn(messages.get(0)); when(this.repository.getNextMessageAfter(1L)).thenReturn(messages.get(1)); when(this.repository.getNextMessageAfter(2L)).thenReturn(messages.get(2)); when(this.repository.getNextMessageAfter(3L)).thenReturn(null); Message actual = this.messageProvider.getNext(); assertNotNull(actual); assertEquals("www.post.com/api/batch", actual.getUrl()); assertEquals("{\"events\":[" + "{\"url\":\"http://google.com\",\"reynaId\":1,\"payload\":{\"key01\":\"value01\",\"key02\":11}}" + "]}", actual.getBody()); this.assertHeaders(actual); Batch batch = new Gson().fromJson(actual.getBody(), Batch.class); assertEquals(1L, batch.getEvents().get(0).getReynaId()); assertEquals("http://google.com", batch.getEvents().get(0).getUrl()); assertEquals("{\"key01\":\"value01\",\"key02\":11}", batch.getEvents().get(0).getPayload().toString()); } @Test public void whenCallingGetNextAndNoUrlConfiguredShouldReturnUrlWithBatchAppended() throws URISyntaxException { doReturn(95L).when(this.batchConfiguration).getBatchMessagesSize(); doReturn(null).when(this.batchConfiguration).getBatchUrl(); Message message1 = new Message(1L, URI.create("http://www.post.com"), "{\"key01\":\"value01\", \"key02\": 11}", getTestMessageHeaders()); Message message2 = new Message(2L, URI.create("http://www.post.com"), "{\"key11\":\"value11\", \"key12\": 12}", getTestMessageHeaders()); Message message3 = new Message(3L, URI.create("http://www.post.com"), "{\"key21\":\"value21\", \"key22\": 22}", getTestMessageHeaders()); when(this.repository.getNext()).thenReturn(message1); when(this.repository.getNextMessageAfter(1L)).thenReturn(message2); when(this.repository.getNextMessageAfter(2L)).thenReturn(message3); when(this.repository.getNextMessageAfter(3L)).thenReturn(null); Message actual = this.messageProvider.getNext(); assertNotNull(actual); assertEquals("http://www.post.com/api/1/batch", actual.getUrl()); assertEquals("{\"events\":[" + "{\"url\":\"http://www.post.com\",\"reynaId\":1,\"payload\":{\"key01\":\"value01\",\"key02\":11}}" + "]}", actual.getBody()); } @Test public void whenCallingGetNextAndNoUrlConfiguredAndHTTPSShouldReturnUrlWithBatchAppended() throws URISyntaxException { doReturn(95L).when(this.batchConfiguration).getBatchMessagesSize(); doReturn(null).when(this.batchConfiguration).getBatchUrl(); Message message1 = new Message(1L, URI.create("https://www.post.com/1/2/req"), "{\"key01\":\"value01\", \"key02\": 11}", getTestMessageHeaders()); Message message2 = new Message(2L, URI.create("https://www.post.com/1/2/req"), "{\"key11\":\"value11\", \"key12\": 12}", getTestMessageHeaders()); Message message3 = new Message(3L, URI.create("https://www.post.com/1/2/req"), "{\"key21\":\"value21\", \"key22\": 22}", getTestMessageHeaders()); when(this.repository.getNext()).thenReturn(message1).thenReturn(message2).thenReturn(message3).thenReturn(null); Message actual = this.messageProvider.getNext(); assertNotNull(actual); assertEquals("https://www.post.com/api/1/batch", actual.getUrl()); } @Test public void whenCallingCanSendAndTimeNotElapsedShouldReturnFalse() throws URISyntaxException { long interval = (long)(AlarmManager.INTERVAL_DAY * 0.9); doReturn(false).when(this.periodicBackoutCheck).timeElapsed("BatchProvider", interval); boolean actual = this.messageProvider.canSend(); assertFalse(actual); } @Test public void whenCallingCanSendAndTimeElapsedShouldReturnTrue() throws URISyntaxException { long interval = (long)(AlarmManager.INTERVAL_DAY * 0.9); doReturn(true).when(this.periodicBackoutCheck).timeElapsed("BatchProvider", interval); boolean actual = this.messageProvider.canSend(); assertTrue(actual); } @Test public void whenCallingCanSendThereAreMoreMessagesThanMaxMessagesCountShouldSend() throws URISyntaxException { doReturn(false).when(this.periodicBackoutCheck).timeElapsed("BatchProvider", AlarmManager.INTERVAL_DAY); doReturn(100).when(this.batchConfiguration).getBatchMessageCount(); doReturn(100L).when(this.repository).getAvailableMessagesCount(); boolean actual = this.messageProvider.canSend(); assertTrue(actual); } @Test public void whenCallingCanSendThereAreLessMessagesThanMaxMessagesCountShouldSend() throws URISyntaxException { doReturn(false).when(this.periodicBackoutCheck).timeElapsed("BatchProvider", AlarmManager.INTERVAL_DAY); doReturn(100).when(this.batchConfiguration).getBatchMessageCount(); doReturn(99L).when(this.repository).getAvailableMessagesCount(); boolean actual = this.messageProvider.canSend(); assertFalse(actual); } @Test public void whenCallingGetNextAndPreviousBatchWasLessThanMaximumessagesCountShouldReturnNull() throws URISyntaxException { doReturn(2).when(this.batchConfiguration).getBatchMessageCount(); ArrayList<Message> messages = this.getTestMessages(); when(this.repository.getNext()).thenReturn(messages.get(0)); when(this.repository.getNextMessageAfter(1L)).thenReturn(messages.get(1)); when(this.repository.getNextMessageAfter(2L)).thenReturn(messages.get(2)); when(this.repository.getNextMessageAfter(3L)).thenReturn(null); Message actual = this.messageProvider.getNext(); assertNotNull(actual); assertEquals("www.post.com/api/batch", actual.getUrl()); assertEquals("{\"events\":[" + "{\"url\":\"http://google.com\",\"reynaId\":1,\"payload\":{\"key01\":\"value01\",\"key02\":11}}," + "{\"url\":\"http://google2.com\",\"reynaId\":2,\"payload\":{\"key11\":\"value11\",\"key12\":12}}" + "]}", actual.getBody()); when(this.repository.getNext()).thenReturn(messages.get(2)); when(this.repository.getNextMessageAfter(3L)).thenReturn(null); actual = this.messageProvider.getNext(); assertNotNull(actual); assertEquals("www.post.com/api/batch", actual.getUrl()); assertEquals("{\"events\":[" + "{\"url\":\"http://google3.com\",\"reynaId\":3,\"payload\":{\"key21\":\"value21\",\"key22\":22}}" + "]}", actual.getBody()); when(this.repository.getNext()).thenReturn(messages.get(0)); when(this.repository.getNextMessageAfter(1L)).thenReturn(messages.get(1)); when(this.repository.getNextMessageAfter(2L)).thenReturn(messages.get(2)); when(this.repository.getNextMessageAfter(3L)).thenReturn(null); actual = this.messageProvider.getNext(); assertNull(actual); } @Test public void whenCallingGetNextAndPreviousBatchWasLessThanMaximumMessagesButSizeIsReachedShouldReturnBatch() throws URISyntaxException { doReturn(95L).when(this.batchConfiguration).getBatchMessagesSize(); doReturn(3).when(this.batchConfiguration).getBatchMessageCount(); ArrayList<Message> messages = this.getTestMessages(); when(this.repository.getNext()).thenReturn(messages.get(0)); when(this.repository.getNextMessageAfter(1L)).thenReturn(messages.get(1)); when(this.repository.getNextMessageAfter(2L)).thenReturn(messages.get(2)); when(this.repository.getNextMessageAfter(3L)).thenReturn(null); /*batch due to size limit*/ Message actual = this.messageProvider.getNext(); assertNotNull(actual); assertEquals("www.post.com/api/batch", actual.getUrl()); assertEquals("{\"events\":[" + "{\"url\":\"http://google.com\",\"reynaId\":1,\"payload\":{\"key01\":\"value01\",\"key02\":11}}" + "]}", actual.getBody()); /*batch due to number of max messages limit*/ doReturn(10000L).when(this.batchConfiguration).getBatchMessagesSize(); doReturn(2).when(this.batchConfiguration).getBatchMessageCount(); when(this.repository.getNext()).thenReturn(messages.get(0)); when(this.repository.getNextMessageAfter(1L)).thenReturn(messages.get(1)); when(this.repository.getNextMessageAfter(2L)).thenReturn(messages.get(2)); when(this.repository.getNextMessageAfter(3L)).thenReturn(null); actual = this.messageProvider.getNext(); assertNotNull(actual); assertEquals("{\"events\":[" + "{\"url\":\"http://google.com\",\"reynaId\":1,\"payload\":{\"key01\":\"value01\",\"key02\":11}}," + "{\"url\":\"http://google2.com\",\"reynaId\":2,\"payload\":{\"key11\":\"value11\",\"key12\":12}}" + "]}", actual.getBody()); /*batch include rest of the messages*/ when(this.repository.getNext()).thenReturn(messages.get(2)); when(this.repository.getNextMessageAfter(3L)).thenReturn(null); actual = this.messageProvider.getNext(); assertNotNull(actual); assertEquals("www.post.com/api/batch", actual.getUrl()); assertEquals("{\"events\":[" + "{\"url\":\"http://google3.com\",\"reynaId\":3,\"payload\":{\"key21\":\"value21\",\"key22\":22}}" + "]}", actual.getBody()); /*should return null as last batch has only 1 message*/ when(this.repository.getNext()).thenReturn(messages.get(0)); when(this.repository.getNextMessageAfter(1L)).thenReturn(messages.get(1)); when(this.repository.getNextMessageAfter(2L)).thenReturn(messages.get(2)); when(this.repository.getNextMessageAfter(3L)).thenReturn(null); actual = this.messageProvider.getNext(); assertNull(actual); } private ArrayList<Message> getTestMessages() { Message message1 = new Message(1L, URI.create("http://google.com"), "{\"key01\":\"value01\", \"key02\": 11}", getTestMessageHeaders()); Message message2 = new Message(2L, URI.create("http://google2.com"), "{\"key11\":\"value11\", \"key12\": 12}", getTestMessageHeaders()); Message message3 = new Message(3L, URI.create("http://google3.com"), "{\"key21\":\"value21\", \"key22\": 22}", getTestMessageHeaders()); ArrayList<Message> messages = new ArrayList<Message>(3); messages.add(message1); messages.add(message2); messages.add(message3); return messages; } private void assertHeaders(Message actual) { assertEquals(3, actual.getHeaders().length); assertEquals("key1", actual.getHeaders()[0].getKey()); assertEquals("value1", actual.getHeaders()[0].getValue()); assertEquals("key2", actual.getHeaders()[1].getKey()); assertEquals("value2", actual.getHeaders()[1].getValue()); assertEquals("key4", actual.getHeaders()[2].getKey()); assertEquals("value4", actual.getHeaders()[2].getValue()); } private Header[] getTestMessageHeaders() { ArrayList<Header> headers = new ArrayList<Header>(); headers.add(new Header("key1", "value1")); headers.add(new Header("key2", "value2")); headers.add(new Header("key4", "value4")); Header[] headersForMessage = new Header[headers.size()]; return headers.toArray(headersForMessage); } }