/* * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ package com.amazon.sqs.javamessaging; import com.amazon.sqs.javamessaging.AmazonSQSMessagingClientWrapper; import com.amazon.sqs.javamessaging.PrefetchManager; import com.amazon.sqs.javamessaging.SQSConnection; import com.amazon.sqs.javamessaging.SQSMessageConsumer; import com.amazon.sqs.javamessaging.SQSMessageConsumerPrefetch; import com.amazon.sqs.javamessaging.SQSSession; import com.amazon.sqs.javamessaging.SQSSessionCallbackScheduler; import com.amazon.sqs.javamessaging.acknowledge.AcknowledgeMode; import com.amazon.sqs.javamessaging.acknowledge.Acknowledger; import com.amazon.sqs.javamessaging.acknowledge.NegativeAcknowledger; import com.amazon.sqs.javamessaging.acknowledge.SQSMessageIdentifier; import com.amazon.sqs.javamessaging.message.SQSMessage; import com.amazon.sqs.javamessaging.message.SQSTextMessage; import com.amazonaws.services.sqs.model.Message; import javax.jms.JMSException; import javax.jms.MessageListener; import javax.jms.Session; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import static junit.framework.Assert.fail; import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyList; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** * Test the SQSSessionCallbackSchedulerTest class */ public class SQSSessionCallbackSchedulerTest { private static final String QUEUE_URL_PREFIX = "QueueUrl"; private static final String QUEUE_URL_1 = "QueueUrl1"; private static final String QUEUE_URL_2 = "QueueUrl2"; private SQSSession sqsSession; private NegativeAcknowledger negativeAcknowledger; private SQSSessionCallbackScheduler sqsSessionRunnable; private SQSConnection sqsConnection; private AmazonSQSMessagingClientWrapper sqsClient; private ArrayDeque<SQSSession.CallbackEntry> callbackQueue; private Acknowledger acknowledger; private SQSMessageConsumer consumer; @Before public void setup() { sqsClient = mock(AmazonSQSMessagingClientWrapper.class); sqsConnection = mock(SQSConnection.class); when(sqsConnection.getWrappedAmazonSQSClient()) .thenReturn(sqsClient); sqsSession = mock(SQSSession.class); when(sqsSession.getParentConnection()) .thenReturn(sqsConnection); negativeAcknowledger = mock(NegativeAcknowledger.class); callbackQueue = mock(ArrayDeque.class); acknowledger = mock(Acknowledger.class); consumer = mock(SQSMessageConsumer.class); sqsSessionRunnable = spy(new SQSSessionCallbackScheduler(sqsSession, AcknowledgeMode.ACK_AUTO.withOriginalAcknowledgeMode(Session.AUTO_ACKNOWLEDGE), acknowledger, negativeAcknowledger)); sqsSessionRunnable.callbackQueue = callbackQueue; } /** * Test nack queue messages when the queue is empty */ @Test public void testNackQueueMessageWhenEmpty() throws JMSException { when(callbackQueue.isEmpty()) .thenReturn(true); /* * Nack the messages */ sqsSessionRunnable.nackQueuedMessages(); /* * Verify results */ verify(negativeAcknowledger, never()).bulkAction(anyList(), anyInt()); } /** * Test nack queue messages does not propagate a JMS exception */ @Test public void testNackQueueMessageAcknowledgerThrowJMSException() throws JMSException { MessageListener msgListener = mock(MessageListener.class); SQSMessage sqsMessage = mock(SQSMessage.class); when(sqsMessage.getReceiptHandle()) .thenReturn("r1"); when(sqsMessage.getSQSMessageId()) .thenReturn("messageId1"); when(sqsMessage.getQueueUrl()) .thenReturn(QUEUE_URL_1); SQSMessageConsumerPrefetch.MessageManager msgManager = mock(SQSMessageConsumerPrefetch.MessageManager.class); when(msgManager.getMessage()) .thenReturn(sqsMessage); when(msgManager.getPrefetchManager()) .thenReturn(mock(PrefetchManager.class)); SQSSession.CallbackEntry entry1 = new SQSSession.CallbackEntry(msgListener, msgManager); when(callbackQueue.isEmpty()) .thenReturn(false) .thenReturn(true); when(callbackQueue.pollFirst()) .thenReturn(entry1); doThrow(new JMSException("Exception")) .when(negativeAcknowledger).bulkAction(anyList(), anyInt()); /* * Nack the messages, no exception expected */ sqsSessionRunnable.nackQueuedMessages(); } /** * Test nack queue messages does propagate Errors */ @Test public void testNackQueueMessageAcknowledgerThrowError() throws JMSException { MessageListener msgListener = mock(MessageListener.class); SQSMessage sqsMessage = mock(SQSMessage.class); when(sqsMessage.getReceiptHandle()) .thenReturn("r2"); when(sqsMessage.getSQSMessageId()) .thenReturn("messageId2"); when(sqsMessage.getQueueUrl()) .thenReturn(QUEUE_URL_2); SQSMessageConsumerPrefetch.MessageManager msgManager = mock(SQSMessageConsumerPrefetch.MessageManager.class); when(msgManager.getMessage()) .thenReturn(sqsMessage); when(msgManager.getPrefetchManager()) .thenReturn(mock(PrefetchManager.class)); SQSSession.CallbackEntry entry1 = new SQSSession.CallbackEntry(msgListener, msgManager); when(callbackQueue.isEmpty()) .thenReturn(false) .thenReturn(true); when(callbackQueue.pollFirst()) .thenReturn(entry1); doThrow(new Error("error")) .when(negativeAcknowledger).bulkAction(anyList(), anyInt()); /* * Nack the messages, exception expected */ try { sqsSessionRunnable.nackQueuedMessages(); fail(); } catch(Error e) { // expected error } } /** * Test nack Queue Message */ @Test public void testNackQueueMessage() throws JMSException { /* * Set up mocks */ MessageListener msgListener = mock(MessageListener.class); SQSMessage sqsMessage1 = mock(SQSMessage.class); when(sqsMessage1.getReceiptHandle()) .thenReturn("r1"); when(sqsMessage1.getSQSMessageId()) .thenReturn("messageId1"); when(sqsMessage1.getQueueUrl()) .thenReturn(QUEUE_URL_1); SQSMessageConsumerPrefetch.MessageManager msgManager1 = mock(SQSMessageConsumerPrefetch.MessageManager.class); when(msgManager1.getMessage()) .thenReturn(sqsMessage1); when(msgManager1.getPrefetchManager()) .thenReturn(mock(PrefetchManager.class)); SQSMessage sqsMessage2 = mock(SQSMessage.class); when(sqsMessage2.getReceiptHandle()) .thenReturn("r2"); when(sqsMessage2.getSQSMessageId()) .thenReturn("messageId2"); when(sqsMessage2.getQueueUrl()) .thenReturn(QUEUE_URL_2); SQSMessageConsumerPrefetch.MessageManager msgManager2 = mock(SQSMessageConsumerPrefetch.MessageManager.class); when(msgManager2.getMessage()) .thenReturn(sqsMessage2); when(msgManager2.getPrefetchManager()) .thenReturn(mock(PrefetchManager.class)); SQSSession.CallbackEntry entry1 = new SQSSession.CallbackEntry(msgListener, msgManager1); SQSSession.CallbackEntry entry2 = new SQSSession.CallbackEntry(msgListener, msgManager2); when(callbackQueue.isEmpty()) .thenReturn(false) .thenReturn(false) .thenReturn(true); when(callbackQueue.pollFirst()) .thenReturn(entry1) .thenReturn(entry2); List<SQSMessageIdentifier> nackMessageIdentifiers = new ArrayList<SQSMessageIdentifier>(); nackMessageIdentifiers.add(new SQSMessageIdentifier(QUEUE_URL_1, "r1", "messageId1")); nackMessageIdentifiers.add(new SQSMessageIdentifier(QUEUE_URL_2, "r2", "messageId2")); /* * Nack the messages */ sqsSessionRunnable.nackQueuedMessages(); /* * Verify results */ verify(negativeAcknowledger).bulkAction(eq(nackMessageIdentifiers), eq(2)); } /** * Test starting callback does not propagate Interrupted Exception */ @Test public void testStartingCallbackThrowJMSException() throws JMSException, InterruptedException { /* * Set up mocks */ doThrow(new JMSException("closed")) .when(sqsSession).startingCallback(consumer); doNothing() .when(sqsSessionRunnable).nackQueuedMessages(); PrefetchManager prefetchManager = mock(PrefetchManager.class); when(prefetchManager.getMessageConsumer()) .thenReturn(consumer); SQSMessageConsumerPrefetch.MessageManager msgManager1 = mock(SQSMessageConsumerPrefetch.MessageManager.class); when(msgManager1.getMessage()) .thenReturn(mock(SQSMessage.class)); when(msgManager1.getPrefetchManager()) .thenReturn(prefetchManager); SQSSession.CallbackEntry entry1 = new SQSSession.CallbackEntry(null, msgManager1); when(callbackQueue.pollFirst()) .thenReturn(entry1); /* * Nack the messages, exit the loop */ sqsSessionRunnable.run(); /* * Verify results */ verify(sqsSession).startingCallback(consumer); verify(sqsSessionRunnable).nackQueuedMessages(); verify(sqsSession, never()).finishedCallback(); } /** * Test callback run execution when call back entry message listener is empty */ @Test public void testCallbackQueueEntryMessageListenerEmpty() throws JMSException, InterruptedException { /* * Set up mocks */ doNothing() .doThrow(new JMSException("Closing")) .when(sqsSession).startingCallback(any(SQSMessageConsumer.class)); SQSMessageConsumerPrefetch.MessageManager msgManager1 = createMessageManager(1); SQSMessageConsumerPrefetch.MessageManager msgManager2 = createMessageManager(2); SQSSession.CallbackEntry entry1 = new SQSSession.CallbackEntry(null, msgManager1); SQSSession.CallbackEntry entry2 = new SQSSession.CallbackEntry(null, msgManager2); when(callbackQueue.pollFirst()) .thenReturn(entry1) .thenReturn(entry2); when(callbackQueue.isEmpty()) .thenReturn(true); // Setup ConsumerCloseAfterCallback SQSMessageConsumer messageConsumer = mock(SQSMessageConsumer.class); sqsSessionRunnable.setConsumerCloseAfterCallback(messageConsumer); /* * Nack the messages, exception expected */ sqsSessionRunnable.run(); /* * Verify results */ verify(sqsSession, times(2)).startingCallback(consumer); verify(sqsSessionRunnable).nackQueuedMessages(); // Verify that we nack the message ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class); verify(negativeAcknowledger, times(2)).bulkAction(captor.capture(), eq(1)); List allCaptured = captor.getAllValues(); List<SQSMessageIdentifier> captured = (List<SQSMessageIdentifier>)allCaptured.get(0); assertEquals(QUEUE_URL_1, captured.get(0).getQueueUrl()); assertEquals("r1", captured.get(0).getReceiptHandle()); captured = (List<SQSMessageIdentifier>)allCaptured.get(1); assertEquals(QUEUE_URL_2, captured.get(0).getQueueUrl()); assertEquals("r2", captured.get(0).getReceiptHandle()); // Verify do close is called on set ConsumerCloseAfterCallback verify(messageConsumer).doClose(); verify(sqsSession).finishedCallback(); } /** * Test callback run execution when message ack throws a JMS exception */ @Test public void testCallbackQueueEntryMessageAckThrowsJMSException() throws JMSException, InterruptedException { /* * Set up mocks */ doNothing() .doThrow(new JMSException("Closing")) .when(sqsSession).startingCallback(consumer); SQSMessage sqsMessage1 = mock(SQSMessage.class); when(sqsMessage1.getReceiptHandle()) .thenReturn("r1"); when(sqsMessage1.getSQSMessageId()) .thenReturn("messageId1"); when(sqsMessage1.getQueueUrl()) .thenReturn(QUEUE_URL_1); PrefetchManager prefetchManager = mock(PrefetchManager.class); when(prefetchManager.getMessageConsumer()) .thenReturn(consumer); SQSMessageConsumerPrefetch.MessageManager msgManager1 = mock(SQSMessageConsumerPrefetch.MessageManager.class); when(msgManager1.getMessage()) .thenReturn(sqsMessage1); when(msgManager1.getPrefetchManager()) .thenReturn(prefetchManager); // Throw an exception when try to acknowledge the message doThrow(new JMSException("Exception")) .when(sqsMessage1).acknowledge(); MessageListener msgListener = mock(MessageListener.class); SQSSession.CallbackEntry entry1 = new SQSSession.CallbackEntry(msgListener, msgManager1); SQSMessageConsumerPrefetch.MessageManager msgManager2 = createMessageManager(2); SQSSession.CallbackEntry entry2 = new SQSSession.CallbackEntry(msgListener, msgManager2); when(callbackQueue.pollFirst()) .thenReturn(entry1) .thenReturn(entry2); when(callbackQueue.isEmpty()) .thenReturn(true); /* * Nack the messages, exception expected */ sqsSessionRunnable.run(); /* * Verify results */ verify(sqsSession, times(2)).startingCallback(consumer); verify(sqsSessionRunnable).nackQueuedMessages(); verify(sqsMessage1).acknowledge(); // Verify that we nack the message ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class); verify(negativeAcknowledger, times(2)).bulkAction(captor.capture(), eq(1)); List allCaptured = captor.getAllValues(); List<SQSMessageIdentifier> captured = (List<SQSMessageIdentifier>)allCaptured.get(0); assertEquals(QUEUE_URL_1, captured.get(0).getQueueUrl()); assertEquals("r1", captured.get(0).getReceiptHandle()); captured = (List<SQSMessageIdentifier>)allCaptured.get(1); assertEquals(QUEUE_URL_2, captured.get(0).getQueueUrl()); assertEquals("r2", captured.get(0).getReceiptHandle()); verify(sqsSession).finishedCallback(); } /** * Test callback run execution when message nack throws a JMS exception */ @Test public void testCallbackQueueEntryMessageNAckThrowsJMSException() throws JMSException, InterruptedException { /* * Set up mocks */ doNothing() .doThrow(new JMSException("Closing")) .when(sqsSession).startingCallback(consumer); SQSMessage sqsMessage1 = mock(SQSMessage.class); when(sqsMessage1.getReceiptHandle()) .thenReturn("r1"); when(sqsMessage1.getSQSMessageId()) .thenReturn("messageId1"); when(sqsMessage1.getQueueUrl()) .thenReturn(QUEUE_URL_1); PrefetchManager prefetchManager = mock(PrefetchManager.class); when(prefetchManager.getMessageConsumer()) .thenReturn(consumer); SQSMessageConsumerPrefetch.MessageManager msgManager1 = mock(SQSMessageConsumerPrefetch.MessageManager.class); when(msgManager1.getMessage()) .thenReturn(sqsMessage1); when(msgManager1.getPrefetchManager()) .thenReturn(prefetchManager); // Set message listener as null to force a nack SQSSession.CallbackEntry entry1 = new SQSSession.CallbackEntry(null, msgManager1); SQSMessageConsumerPrefetch.MessageManager msgManager2 = createMessageManager(2); SQSSession.CallbackEntry entry2 = new SQSSession.CallbackEntry(null, msgManager2); when(callbackQueue.pollFirst()) .thenReturn(entry1) .thenReturn(entry2); when(callbackQueue.isEmpty()) .thenReturn(true); // Throw an exception when try to negative acknowledge the message doThrow(new JMSException("Exception")) .when(negativeAcknowledger).action(QUEUE_URL_1, Collections.singletonList("r1")); /* * Nack the messages, exception expected */ sqsSessionRunnable.run(); /* * Verify results */ verify(sqsSession, times(2)).startingCallback(consumer); verify(sqsSessionRunnable).nackQueuedMessages(); ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class); verify(negativeAcknowledger, times(2)).bulkAction(captor.capture(), eq(1)); List allCaptured = captor.getAllValues(); List<SQSMessageIdentifier> captured = (List<SQSMessageIdentifier>)allCaptured.get(0); assertEquals(QUEUE_URL_1, captured.get(0).getQueueUrl()); assertEquals("r1", captured.get(0).getReceiptHandle()); captured = (List<SQSMessageIdentifier>)allCaptured.get(1); assertEquals(QUEUE_URL_2, captured.get(0).getQueueUrl()); assertEquals("r2", captured.get(0).getReceiptHandle()); verify(sqsSession).finishedCallback(); } /** * Test schedule callback */ @Test public void testScheduleCallBack() throws JMSException, InterruptedException { /* * Set up mocks */ sqsSessionRunnable.callbackQueue = new ArrayDeque<SQSSession.CallbackEntry>(); MessageListener msgListener = mock(MessageListener.class); SQSMessageConsumerPrefetch.MessageManager msgManager = mock(SQSMessageConsumerPrefetch.MessageManager.class); /* * Nack the messages, exception expected */ sqsSessionRunnable.scheduleCallBacks(msgListener, Collections.singletonList(msgManager)); assertEquals(1, sqsSessionRunnable.callbackQueue.size()); SQSSession.CallbackEntry entry = sqsSessionRunnable.callbackQueue.pollFirst(); assertEquals(msgListener, entry.getMessageListener()); assertEquals(msgManager, entry.getMessageManager()); } /** * Test that no auto ack messages occurs when client acknowledge is set */ @Test public void testMessageNotAckWithClientAckMode() throws JMSException, InterruptedException { /** * Set up mocks */ sqsSessionRunnable = spy(new SQSSessionCallbackScheduler(sqsSession, AcknowledgeMode.ACK_AUTO.withOriginalAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE), acknowledger, negativeAcknowledger)); sqsSessionRunnable.callbackQueue = callbackQueue; doNothing() .doThrow(new JMSException("Closing")) .when(sqsSession).startingCallback(consumer); SQSMessage sqsMessage1 = mock(SQSMessage.class); when(sqsMessage1.getReceiptHandle()) .thenReturn("r1"); when(sqsMessage1.getSQSMessageId()) .thenReturn("messageId1"); when(sqsMessage1.getQueueUrl()) .thenReturn(QUEUE_URL_1); PrefetchManager prefetchManager = mock(PrefetchManager.class); when(prefetchManager.getMessageConsumer()) .thenReturn(consumer); SQSMessageConsumerPrefetch.MessageManager msgManager1 = mock(SQSMessageConsumerPrefetch.MessageManager.class); when(msgManager1.getMessage()) .thenReturn(sqsMessage1); when(msgManager1.getPrefetchManager()) .thenReturn(prefetchManager); MessageListener msgListener = mock(MessageListener.class); SQSSession.CallbackEntry entry1 = new SQSSession.CallbackEntry(msgListener, msgManager1); when(callbackQueue.pollFirst()) .thenReturn(entry1); when(callbackQueue.isEmpty()) .thenReturn(true); /* * Start the callback */ sqsSessionRunnable.run(); /* * Verify results */ verify(sqsSession, times(2)).startingCallback(consumer); verify(sqsSessionRunnable).nackQueuedMessages(); // Verify that do not ack the message verify(sqsMessage1, never()).acknowledge(); verify(negativeAcknowledger, never()).action(QUEUE_URL_1, Collections.singletonList("r1")); verify(sqsSession).finishedCallback(); } /** * Test that no auto ack messages occurs when client acknowledge is set */ @Test public void testWhenListenerThrowsWhenAutoAckThenCallbackQueuePurgedFromMessagesWithSameQueueAndGroup() throws JMSException, InterruptedException { /** * Set up mocks */ sqsSessionRunnable = spy(new SQSSessionCallbackScheduler(sqsSession, AcknowledgeMode.ACK_AUTO.withOriginalAcknowledgeMode(Session.AUTO_ACKNOWLEDGE), acknowledger, negativeAcknowledger)); MessageListener messageListener = mock(MessageListener.class); doThrow(RuntimeException.class) .when(messageListener).onMessage(any(javax.jms.Message.class)); List<SQSMessageConsumerPrefetch.MessageManager> messages = new ArrayList<SQSMessageConsumerPrefetch.MessageManager>(); messages.add(createFifoMessageManager("queue1", "group1", "message1", "handle1")); messages.add(createFifoMessageManager("queue1", "group1", "message2", "handle2")); messages.add(createFifoMessageManager("queue2", "group1", "message3", "handle3")); messages.add(createFifoMessageManager("queue1", "group2", "message4", "handle4")); messages.add(createFifoMessageManager("queue1", "group1", "message5", "handle5")); messages.add(createFifoMessageManager("queue2", "group2", "message6", "handle6")); sqsSessionRunnable.scheduleCallBacks(messageListener, messages); doNothing() .doThrow(new JMSException("Closing")) .when(sqsSession).startingCallback(consumer); sqsSessionRunnable.run(); ArgumentCaptor<List> messageIdentifierListCaptor = ArgumentCaptor.forClass(List.class); ArgumentCaptor<Integer> indexOfMessageCaptor = ArgumentCaptor.forClass(Integer.class); verify(negativeAcknowledger, times(3)).bulkAction(messageIdentifierListCaptor.capture(), indexOfMessageCaptor.capture()); List<SQSMessageIdentifier> nackedMessages = messageIdentifierListCaptor.getAllValues().get(0); int nackedMessagesSize = indexOfMessageCaptor.getAllValues().get(0).intValue(); //failing to process 'message1' should nack all messages for queue1 and group1, that is 'message1', 'message2' and 'message5' assertEquals(3, nackedMessagesSize); assertEquals("message1", nackedMessages.get(0).getSQSMessageID()); assertEquals("message2", nackedMessages.get(1).getSQSMessageID()); assertEquals("message5", nackedMessages.get(2).getSQSMessageID()); } private SQSMessageConsumerPrefetch.MessageManager createFifoMessageManager(String queueUrl, String groupId, String messageId, String receiptHandle) throws JMSException { Message message = new Message(); message.setBody("body"); message.setMessageId(messageId); message.setReceiptHandle(receiptHandle); Map<String, String> attributes = new HashMap<String, String>(); attributes.put(SQSMessagingClientConstants.SEQUENCE_NUMBER, "728374687246872364"); attributes.put(SQSMessagingClientConstants.MESSAGE_DEDUPLICATION_ID, messageId); attributes.put(SQSMessagingClientConstants.MESSAGE_GROUP_ID, groupId); attributes.put(SQSMessagingClientConstants.APPROXIMATE_RECEIVE_COUNT, "0"); message.setAttributes(attributes); SQSMessage sqsMessage = new SQSTextMessage(acknowledger, queueUrl, message); PrefetchManager prefetchManager = mock(PrefetchManager.class); when(prefetchManager.getMessageConsumer()) .thenReturn(consumer); SQSMessageConsumerPrefetch.MessageManager msgManager = new SQSMessageConsumerPrefetch.MessageManager(prefetchManager, sqsMessage); return msgManager; } private SQSMessageConsumerPrefetch.MessageManager createMessageManager(int index) { SQSMessage sqsMessage = mock(SQSMessage.class); when(sqsMessage.getReceiptHandle()) .thenReturn("r" + index); when(sqsMessage.getSQSMessageId()) .thenReturn("messageId" + index); when(sqsMessage.getQueueUrl()) .thenReturn(QUEUE_URL_PREFIX + index); PrefetchManager prefetchManager = mock(PrefetchManager.class); when(prefetchManager.getMessageConsumer()) .thenReturn(consumer); SQSMessageConsumerPrefetch.MessageManager msgManager = mock(SQSMessageConsumerPrefetch.MessageManager.class); when(msgManager.getMessage()) .thenReturn(sqsMessage); when(msgManager.getPrefetchManager()) .thenReturn(prefetchManager); return msgManager; } }