11package software .amazon .lambda .powertools .sqs ;
22
33import java .io .IOException ;
4+ import java .util .Collection ;
45import java .util .HashMap ;
56import java .util .List ;
67import java .util .function .Consumer ;
7-
8+ import java . util . function . Function ;
89import com .amazonaws .services .lambda .runtime .events .SQSEvent ;
910import com .fasterxml .jackson .databind .ObjectMapper ;
1011import org .assertj .core .api .Assertions ;
1819import software .amazon .awssdk .services .sqs .model .GetQueueAttributesRequest ;
1920import software .amazon .awssdk .services .sqs .model .GetQueueAttributesResponse ;
2021import software .amazon .awssdk .services .sqs .model .QueueAttributeName ;
22+ import software .amazon .awssdk .services .sqs .model .SendMessageBatchRequest ;
23+ import software .amazon .awssdk .services .sqs .model .SendMessageBatchRequestEntry ;
2124
2225import static com .amazonaws .services .lambda .runtime .events .SQSEvent .SQSMessage ;
26+ import static org .assertj .core .api .Assertions .*;
2327import static org .assertj .core .api .Assertions .assertThat ;
2428import static org .assertj .core .api .Assertions .assertThatExceptionOfType ;
2529import static org .mockito .ArgumentMatchers .any ;
@@ -236,10 +240,10 @@ void shouldBatchProcessAndMoveNonRetryableExceptionToDlq() {
236240 return "Success" ;
237241 }, IllegalStateException .class , IllegalArgumentException .class );
238242
239- Assertions . assertThat (batchProcessor )
243+ assertThat (batchProcessor )
240244 .hasSize (1 );
241245
242- verify (sqsClient ).sendMessageBatch (any (Consumer .class ));
246+ verify (sqsClient ).sendMessageBatch (any (SendMessageBatchRequest .class ));
243247 }
244248
245249 @ Test
@@ -265,13 +269,84 @@ void shouldBatchProcessAndDeleteNonRetryableException() {
265269 return "Success" ;
266270 }, true , IllegalStateException .class , IllegalArgumentException .class );
267271
268- Assertions . assertThat (batchProcessor )
272+ assertThat (batchProcessor )
269273 .hasSize (1 );
270274
271- verify (sqsClient , times (0 )).sendMessageBatch (any (Consumer .class ));
275+ verify (sqsClient , times (0 )).sendMessageBatch (any (SendMessageBatchRequest .class ));
272276 verify (sqsClient ).deleteMessageBatch (any (DeleteMessageBatchRequest .class ));
273277 }
274278
279+ @ Test
280+ void shouldDeleteSuccessfulMessageInBatchesOfT10orLess () throws IOException {
281+ SQSEvent batch25Message = MAPPER .readValue (this .getClass ().getResource ("/sampleSqsBatchEventBatchSize25.json" ), SQSEvent .class );
282+
283+ assertThatExceptionOfType (SQSBatchProcessingException .class )
284+ .isThrownBy (() -> batchProcessor (batch25Message , FailureSampleInnerSqsHandler .class ))
285+ .satisfies (e -> {
286+
287+ assertThat (e .successMessageReturnValues ())
288+ .hasSize (24 )
289+ .contains ("Success" );
290+
291+ assertThat (e .getFailures ())
292+ .hasSize (1 )
293+ .extracting ("messageId" )
294+ .contains ("2e1424d4-f796-459a-8184-9c92662be6da" );
295+
296+ assertThat (e .getExceptions ())
297+ .hasSize (1 )
298+ .extracting ("detailMessage" )
299+ .contains ("Failed processing" );
300+ });
301+
302+ ArgumentCaptor <DeleteMessageBatchRequest > captor = ArgumentCaptor .forClass (DeleteMessageBatchRequest .class );
303+
304+ verify (sqsClient , times (3 )).deleteMessageBatch (captor .capture ());
305+
306+ assertThat (captor .getAllValues ())
307+ .hasSize (3 )
308+ .flatMap (DeleteMessageBatchRequest ::entries )
309+ .hasSize (24 );
310+ }
311+
312+ @ Test
313+ void shouldBatchProcessAndMoveNonRetryableExceptionToDlqInBatchesOfT10orLess () throws IOException {
314+ SQSEvent batch25Message = MAPPER .readValue (this .getClass ().getResource ("/sampleSqsBatchEventBatchSize25.json" ), SQSEvent .class );
315+
316+ HashMap <QueueAttributeName , String > attributes = new HashMap <>();
317+
318+ attributes .put (QueueAttributeName .REDRIVE_POLICY , "{\n " +
319+ " \" deadLetterTargetArn\" : \" arn:aws:sqs:us-east-2:123456789012:retry-queue\" ,\n " +
320+ " \" maxReceiveCount\" : 2\n " +
321+ "}" );
322+
323+ when (sqsClient .getQueueAttributes (any (GetQueueAttributesRequest .class ))).thenReturn (GetQueueAttributesResponse .builder ()
324+ .attributes (attributes )
325+ .build ());
326+
327+ List <String > batchProcessor = batchProcessor (batch25Message , (message ) -> {
328+ if ("2e1424d4-f796-459a-8184-9c92662be6da" .equals (message .getMessageId ())) {
329+ interactionClient .listQueues ();
330+ return "Success" ;
331+ }
332+
333+ throw new IllegalStateException ("Failed processing" );
334+ }, IllegalStateException .class , IllegalArgumentException .class );
335+
336+ assertThat (batchProcessor )
337+ .hasSize (1 );
338+
339+ ArgumentCaptor <SendMessageBatchRequest > captor = ArgumentCaptor .forClass (SendMessageBatchRequest .class );
340+
341+
342+ verify (sqsClient , times (3 )).sendMessageBatch (captor .capture ());
343+
344+ assertThat (captor .getAllValues ())
345+ .hasSize (3 )
346+ .flatMap (SendMessageBatchRequest ::entries )
347+ .hasSize (24 );
348+ }
349+
275350 public class FailureSampleInnerSqsHandler implements SqsMessageHandler <String > {
276351 @ Override
277352 public String process (SQSEvent .SQSMessage message ) {
0 commit comments