1
+ package software .amazon .lambda .powertools .sqs ;
2
+
3
+ import java .io .ByteArrayInputStream ;
4
+ import java .io .IOException ;
5
+ import java .util .HashMap ;
6
+ import java .util .Map ;
7
+ import java .util .stream .Stream ;
8
+
9
+ import com .amazonaws .AmazonServiceException ;
10
+ import com .amazonaws .SdkClientException ;
11
+ import com .amazonaws .services .lambda .runtime .events .SQSEvent ;
12
+ import com .amazonaws .services .s3 .AmazonS3 ;
13
+ import com .amazonaws .services .s3 .model .S3Object ;
14
+ import com .amazonaws .services .s3 .model .S3ObjectInputStream ;
15
+ import com .amazonaws .util .StringInputStream ;
16
+ import org .apache .http .client .methods .HttpRequestBase ;
17
+ import org .junit .jupiter .api .BeforeEach ;
18
+ import org .junit .jupiter .api .Test ;
19
+ import org .junit .jupiter .params .ParameterizedTest ;
20
+ import org .junit .jupiter .params .provider .Arguments ;
21
+ import org .junit .jupiter .params .provider .MethodSource ;
22
+ import org .junit .jupiter .params .provider .ValueSource ;
23
+ import org .mockito .Mock ;
24
+ import software .amazon .lambda .powertools .sqs .internal .SqsMessageAspect ;
25
+
26
+ import static com .amazonaws .services .lambda .runtime .events .SQSEvent .SQSMessage ;
27
+ import static java .util .Collections .singletonList ;
28
+ import static org .apache .commons .lang3 .reflect .FieldUtils .writeStaticField ;
29
+ import static org .assertj .core .api .Assertions .assertThat ;
30
+ import static org .assertj .core .api .Assertions .assertThatExceptionOfType ;
31
+ import static org .mockito .Mockito .mock ;
32
+ import static org .mockito .Mockito .never ;
33
+ import static org .mockito .Mockito .verify ;
34
+ import static org .mockito .Mockito .verifyNoInteractions ;
35
+ import static org .mockito .Mockito .when ;
36
+ import static org .mockito .MockitoAnnotations .initMocks ;
37
+
38
+ class PowertoolsSqsTest {
39
+
40
+ @ Mock
41
+ private AmazonS3 amazonS3 ;
42
+ private static final String BUCKET_NAME = "ms-extended-sqs-client" ;
43
+ private static final String BUCKET_KEY = "c71eb2ae-37e0-4265-8909-32f4153faddf" ;
44
+
45
+ @ BeforeEach
46
+ void setUp () throws IllegalAccessException {
47
+ initMocks (this );
48
+ writeStaticField (SqsMessageAspect .class , "amazonS3" , amazonS3 , true );
49
+ }
50
+
51
+ @ Test
52
+ public void testLargeMessage () {
53
+ S3Object s3Response = new S3Object ();
54
+ s3Response .setObjectContent (new ByteArrayInputStream ("A big message" .getBytes ()));
55
+
56
+ when (amazonS3 .getObject (BUCKET_NAME , BUCKET_KEY )).thenReturn (s3Response );
57
+ SQSEvent sqsEvent = messageWithBody ("[\" software.amazon.payloadoffloading.PayloadS3Pointer\" ,{\" s3BucketName\" :\" " + BUCKET_NAME + "\" ,\" s3Key\" :\" " + BUCKET_KEY + "\" }]" );
58
+
59
+ Map <String , String > sqsMessage = PowertoolsSqs .enrichedMessageFromS3 (sqsEvent , sqsMessages -> {
60
+ Map <String , String > someBusinessLogic = new HashMap <>();
61
+ someBusinessLogic .put ("Message" , sqsMessages .get (0 ).getBody ());
62
+ return someBusinessLogic ;
63
+ });
64
+
65
+ assertThat (sqsMessage )
66
+ .hasSize (1 )
67
+ .containsEntry ("Message" , "A big message" );
68
+
69
+ verify (amazonS3 ).deleteObject (BUCKET_NAME , BUCKET_KEY );
70
+ }
71
+
72
+ @ ParameterizedTest
73
+ @ ValueSource (booleans = {true , false })
74
+ public void testLargeMessageDeleteFromS3Toggle (boolean deleteS3Payload ) {
75
+ S3Object s3Response = new S3Object ();
76
+ s3Response .setObjectContent (new ByteArrayInputStream ("A big message" .getBytes ()));
77
+
78
+ when (amazonS3 .getObject (BUCKET_NAME , BUCKET_KEY )).thenReturn (s3Response );
79
+ SQSEvent sqsEvent = messageWithBody ("[\" software.amazon.payloadoffloading.PayloadS3Pointer\" ,{\" s3BucketName\" :\" " + BUCKET_NAME + "\" ,\" s3Key\" :\" " + BUCKET_KEY + "\" }]" );
80
+
81
+ Map <String , String > sqsMessage = PowertoolsSqs .enrichedMessageFromS3 (sqsEvent , deleteS3Payload , sqsMessages -> {
82
+ Map <String , String > someBusinessLogic = new HashMap <>();
83
+ someBusinessLogic .put ("Message" , sqsMessages .get (0 ).getBody ());
84
+ return someBusinessLogic ;
85
+ });
86
+
87
+ assertThat (sqsMessage )
88
+ .hasSize (1 )
89
+ .containsEntry ("Message" , "A big message" );
90
+ if (deleteS3Payload ) {
91
+ verify (amazonS3 ).deleteObject (BUCKET_NAME , BUCKET_KEY );
92
+ } else {
93
+ verify (amazonS3 , never ()).deleteObject (BUCKET_NAME , BUCKET_KEY );
94
+ }
95
+ }
96
+
97
+ @ Test
98
+ public void shouldNotProcessSmallMessageBody () {
99
+ S3Object s3Response = new S3Object ();
100
+ s3Response .setObjectContent (new ByteArrayInputStream ("A big message" .getBytes ()));
101
+
102
+ when (amazonS3 .getObject (BUCKET_NAME , BUCKET_KEY )).thenReturn (s3Response );
103
+ SQSEvent sqsEvent = messageWithBody ("This is small message" );
104
+
105
+ Map <String , String > sqsMessage = PowertoolsSqs .enrichedMessageFromS3 (sqsEvent , sqsMessages -> {
106
+ Map <String , String > someBusinessLogic = new HashMap <>();
107
+ someBusinessLogic .put ("Message" , sqsMessages .get (0 ).getBody ());
108
+ return someBusinessLogic ;
109
+ });
110
+
111
+ assertThat (sqsMessage )
112
+ .containsEntry ("Message" , "This is small message" );
113
+
114
+ verifyNoInteractions (amazonS3 );
115
+ }
116
+
117
+ @ ParameterizedTest
118
+ @ MethodSource ("exception" )
119
+ public void shouldFailEntireBatchIfFailedDownloadingFromS3 (RuntimeException exception ) {
120
+ when (amazonS3 .getObject (BUCKET_NAME , BUCKET_KEY )).thenThrow (exception );
121
+
122
+ String messageBody = "[\" software.amazon.payloadoffloading.PayloadS3Pointer\" ,{\" s3BucketName\" :\" " + BUCKET_NAME + "\" ,\" s3Key\" :\" " + BUCKET_KEY + "\" }]" ;
123
+ SQSEvent sqsEvent = messageWithBody (messageBody );
124
+
125
+ assertThatExceptionOfType (SqsMessageAspect .FailedProcessingLargePayloadException .class )
126
+ .isThrownBy (() -> PowertoolsSqs .enrichedMessageFromS3 (sqsEvent , sqsMessages -> sqsMessages .get (0 ).getBody ()))
127
+ .withCause (exception );
128
+
129
+ verify (amazonS3 , never ()).deleteObject (BUCKET_NAME , BUCKET_KEY );
130
+ }
131
+
132
+ @ Test
133
+ public void shouldFailEntireBatchIfFailedProcessingDownloadMessageFromS3 () throws IOException {
134
+ S3Object s3Response = new S3Object ();
135
+
136
+ s3Response .setObjectContent (new S3ObjectInputStream (new StringInputStream ("test" ) {
137
+ @ Override
138
+ public void close () throws IOException {
139
+ throw new IOException ("Failed" );
140
+ }
141
+ }, mock (HttpRequestBase .class )));
142
+
143
+ when (amazonS3 .getObject (BUCKET_NAME , BUCKET_KEY )).thenReturn (s3Response );
144
+
145
+ String messageBody = "[\" software.amazon.payloadoffloading.PayloadS3Pointer\" ,{\" s3BucketName\" :\" " + BUCKET_NAME + "\" ,\" s3Key\" :\" " + BUCKET_KEY + "\" }]" ;
146
+ SQSEvent sqsEvent = messageWithBody (messageBody );
147
+
148
+ assertThatExceptionOfType (SqsMessageAspect .FailedProcessingLargePayloadException .class )
149
+ .isThrownBy (() -> PowertoolsSqs .enrichedMessageFromS3 (sqsEvent , sqsMessages -> sqsMessages .get (0 ).getBody ()))
150
+ .withCauseInstanceOf (IOException .class );
151
+
152
+ verify (amazonS3 , never ()).deleteObject (BUCKET_NAME , BUCKET_KEY );
153
+ }
154
+
155
+ private static Stream <Arguments > exception () {
156
+ return Stream .of (Arguments .of (new AmazonServiceException ("Service Exception" )),
157
+ Arguments .of (new SdkClientException ("Client Exception" )));
158
+ }
159
+
160
+ private SQSEvent messageWithBody (String messageBody ) {
161
+ SQSMessage sqsMessage = new SQSMessage ();
162
+ sqsMessage .setBody (messageBody );
163
+ SQSEvent sqsEvent = new SQSEvent ();
164
+ sqsEvent .setRecords (singletonList (sqsMessage ));
165
+ return sqsEvent ;
166
+ }
167
+ }
0 commit comments