1
+ package software .amazon .lambda .powertools .testsuite ;
2
+
3
+
4
+ import java .io .ByteArrayInputStream ;
5
+ import java .io .ByteArrayOutputStream ;
6
+ import java .io .IOException ;
7
+ import java .lang .reflect .InvocationTargetException ;
8
+ import java .lang .reflect .Method ;
9
+ import java .nio .channels .FileChannel ;
10
+ import java .nio .charset .StandardCharsets ;
11
+ import java .nio .file .Files ;
12
+ import java .nio .file .Paths ;
13
+ import java .nio .file .StandardOpenOption ;
14
+ import java .util .Map ;
15
+
16
+ import com .amazonaws .services .lambda .runtime .Context ;
17
+ import com .amazonaws .services .lambda .runtime .events .SQSEvent ;
18
+ import com .amazonaws .services .lambda .runtime .events .models .s3 .S3EventNotification ;
19
+ import com .amazonaws .services .s3 .AmazonS3 ;
20
+ import com .amazonaws .services .s3 .model .S3Object ;
21
+ import com .amazonaws .xray .AWSXRay ;
22
+ import com .fasterxml .jackson .core .JsonProcessingException ;
23
+ import com .fasterxml .jackson .databind .ObjectMapper ;
24
+ import org .apache .logging .log4j .Level ;
25
+ import org .apache .logging .log4j .ThreadContext ;
26
+ import org .junit .jupiter .api .BeforeEach ;
27
+ import org .junit .jupiter .api .Test ;
28
+ import org .mockito .Mock ;
29
+ import software .amazon .lambda .powertools .core .internal .LambdaHandlerProcessor ;
30
+ import software .amazon .lambda .powertools .logging .internal .LambdaLoggingAspect ;
31
+ import software .amazon .lambda .powertools .sqs .internal .SqsLargeMessageAspect ;
32
+ import software .amazon .lambda .powertools .testsuite .handler .LoggingOrderMessageHandler ;
33
+ import software .amazon .lambda .powertools .testsuite .handler .TracingLoggingStreamMessageHandler ;
34
+
35
+ import static java .util .Collections .emptyMap ;
36
+ import static java .util .Collections .singletonList ;
37
+ import static org .apache .commons .lang3 .reflect .FieldUtils .writeStaticField ;
38
+ import static org .assertj .core .api .Assertions .assertThat ;
39
+ import static org .assertj .core .api .Assertions .fail ;
40
+ import static org .mockito .Mockito .when ;
41
+ import static org .mockito .MockitoAnnotations .openMocks ;
42
+
43
+ public class LoggingOrderTest {
44
+
45
+ private static final String BUCKET_NAME = "ms-extended-sqs-client" ;
46
+ private static final String BUCKET_KEY = "c71eb2ae-37e0-4265-8909-32f4153faddf" ;
47
+
48
+ @ Mock
49
+ private Context context ;
50
+
51
+ @ Mock
52
+ private AmazonS3 amazonS3 ;
53
+
54
+ @ BeforeEach
55
+ void setUp () throws IllegalAccessException , IOException , NoSuchMethodException , InvocationTargetException {
56
+ openMocks (this );
57
+ writeStaticField (SqsLargeMessageAspect .class , "amazonS3" , amazonS3 , true );
58
+ ThreadContext .clearAll ();
59
+ writeStaticField (LambdaHandlerProcessor .class , "IS_COLD_START" , null , true );
60
+ setupContext ();
61
+ //Make sure file is cleaned up before running full stack logging regression
62
+ FileChannel .open (Paths .get ("target/logfile.json" ), StandardOpenOption .WRITE ).truncate (0 ).close ();
63
+ resetLogLevel (Level .INFO );
64
+ AWSXRay .beginSegment (LoggingOrderTest .class .getName ());
65
+ }
66
+
67
+ /**
68
+ * The SQSEvent payload will be altered by the @SqsLargeMessage annotation. Logging of the event should happen
69
+ * after the event has been altered
70
+ */
71
+ @ Test
72
+ public void testThatLoggingAnnotationActsLast () throws IOException {
73
+ S3Object s3Response = new S3Object ();
74
+ s3Response .setObjectContent (new ByteArrayInputStream ("A big message" .getBytes ()));
75
+
76
+ when (amazonS3 .getObject (BUCKET_NAME , BUCKET_KEY )).thenReturn (s3Response );
77
+ SQSEvent sqsEvent = messageWithBody ("[\" software.amazon.payloadoffloading.PayloadS3Pointer\" ,{\" s3BucketName\" :\" " + BUCKET_NAME + "\" ,\" s3Key\" :\" " + BUCKET_KEY + "\" }]" );
78
+
79
+ LoggingOrderMessageHandler requestHandler = new LoggingOrderMessageHandler ();
80
+ requestHandler .handleRequest (sqsEvent , context );
81
+
82
+ assertThat (Files .lines (Paths .get ("target/logfile.json" )))
83
+ .hasSize (2 )
84
+ .satisfies (line -> {
85
+ Map <String , Object > actual = parseToMap (line .get (0 ));
86
+
87
+ String message = actual .get ("message" ).toString ();
88
+
89
+ assertThat (message )
90
+ .contains ("A big message" );
91
+ });
92
+ }
93
+
94
+ @ Test
95
+ public void testLoggingAnnotationActsAfterTracingForStreamingHandler () throws IOException {
96
+
97
+ ByteArrayOutputStream output = new ByteArrayOutputStream ();
98
+ S3EventNotification s3EventNotification = s3EventNotification ();
99
+
100
+ TracingLoggingStreamMessageHandler handler = new TracingLoggingStreamMessageHandler ();
101
+ handler .handleRequest (new ByteArrayInputStream (new ObjectMapper ().writeValueAsBytes (s3EventNotification )), output , context );
102
+
103
+ assertThat (new String (output .toByteArray (), StandardCharsets .UTF_8 ))
104
+ .isNotEmpty ();
105
+ }
106
+
107
+ private void setupContext () {
108
+ when (context .getFunctionName ()).thenReturn ("testFunction" );
109
+ when (context .getInvokedFunctionArn ()).thenReturn ("testArn" );
110
+ when (context .getFunctionVersion ()).thenReturn ("1" );
111
+ when (context .getMemoryLimitInMB ()).thenReturn (10 );
112
+ when (context .getAwsRequestId ()).thenReturn ("RequestId" );
113
+ }
114
+
115
+ private void resetLogLevel (Level level ) throws NoSuchMethodException , IllegalAccessException , InvocationTargetException {
116
+ Method resetLogLevels = LambdaLoggingAspect .class .getDeclaredMethod ("resetLogLevels" , Level .class );
117
+ resetLogLevels .setAccessible (true );
118
+ resetLogLevels .invoke (null , level );
119
+ writeStaticField (LambdaLoggingAspect .class , "LEVEL_AT_INITIALISATION" , level , true );
120
+ }
121
+
122
+ private Map <String , Object > parseToMap (String stringAsJson ) {
123
+ try {
124
+ return new ObjectMapper ().readValue (stringAsJson , Map .class );
125
+ } catch (JsonProcessingException e ) {
126
+ fail ("Failed parsing logger line " + stringAsJson );
127
+ return emptyMap ();
128
+ }
129
+ }
130
+
131
+ private S3EventNotification s3EventNotification () {
132
+ S3EventNotification .S3EventNotificationRecord record = new S3EventNotification .S3EventNotificationRecord ("us-west-2" ,
133
+ "ObjectCreated:Put" ,
134
+ "aws:s3" ,
135
+ null ,
136
+ "2.1" ,
137
+ new S3EventNotification .RequestParametersEntity ("127.0.0.1" ),
138
+ new S3EventNotification .ResponseElementsEntity ("C3D13FE58DE4C810" , "FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD" ),
139
+ new S3EventNotification .S3Entity ("testConfigRule" ,
140
+ new S3EventNotification .S3BucketEntity ("mybucket" ,
141
+ new S3EventNotification .UserIdentityEntity ("A3NL1KOZZKExample" ),
142
+ "arn:aws:s3:::mybucket" ),
143
+ new S3EventNotification .S3ObjectEntity ("HappyFace.jpg" ,
144
+ 1024L ,
145
+ "d41d8cd98f00b204e9800998ecf8427e" ,
146
+ "096fKKXTRTtl3on89fVO.nfljtsv6qko" ,
147
+ "0055AED6DCD90281E5" ),
148
+ "1.0" ),
149
+ new S3EventNotification .UserIdentityEntity ("AIDAJDPLRKLG7UEXAMPLE" )
150
+ );
151
+
152
+ return new S3EventNotification (singletonList (record ));
153
+ }
154
+
155
+ private SQSEvent messageWithBody (String messageBody ) {
156
+ SQSEvent .SQSMessage sqsMessage = new SQSEvent .SQSMessage ();
157
+ sqsMessage .setBody (messageBody );
158
+ SQSEvent sqsEvent = new SQSEvent ();
159
+ sqsEvent .setRecords (singletonList (sqsMessage ));
160
+ return sqsEvent ;
161
+ }
162
+ }
0 commit comments