Skip to content

Commit f1e9acb

Browse files
committed
Add unit tests.
1 parent 9961072 commit f1e9acb

File tree

10 files changed

+1593
-20
lines changed

10 files changed

+1593
-20
lines changed

pom.xml

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
-->
1515

1616
<project xmlns="http://maven.apache.org/POM/4.0.0"
17-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
18-
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
17+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
18+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
1919
<modelVersion>4.0.0</modelVersion>
2020

2121
<groupId>software.amazon.lambda</groupId>
@@ -101,7 +101,7 @@
101101
<aws.sdk.v1.version>1.12.781</aws.sdk.v1.version>
102102
<versions-maven-plugin.version>2.18.0</versions-maven-plugin.version>
103103
<elastic.version>1.6.0</elastic.version>
104-
<mockito.version>5.12.0</mockito.version>
104+
<mockito.version>5.17.0</mockito.version>
105105

106106
<!-- As we have a .mvn directory at the root of the project, this will evaluate to the root directory
107107
regardless of where maven is run - sub-module, or root. -->
@@ -341,6 +341,18 @@
341341
<version>${mockito.version}</version>
342342
<scope>test</scope>
343343
</dependency>
344+
<dependency>
345+
<groupId>org.mockito</groupId>
346+
<artifactId>mockito-junit-jupiter</artifactId>
347+
<version>${mockito.version}</version>
348+
<scope>test</scope>
349+
</dependency>
350+
<dependency>
351+
<groupId>org.mockito</groupId>
352+
<artifactId>mockito-subclass</artifactId>
353+
<version>${mockito.version}</version>
354+
<scope>test</scope>
355+
</dependency>
344356
<dependency>
345357
<groupId>com.amazonaws</groupId>
346358
<artifactId>aws-lambda-java-tests</artifactId>

powertools-metrics/pom.xml

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
-->
1515

1616
<project xmlns="http://maven.apache.org/POM/4.0.0"
17-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
18-
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
17+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
18+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
1919
<modelVersion>4.0.0</modelVersion>
2020

2121
<artifactId>powertools-metrics</artifactId>
@@ -82,6 +82,11 @@
8282
<artifactId>mockito-core</artifactId>
8383
<scope>test</scope>
8484
</dependency>
85+
<dependency>
86+
<groupId>org.mockito</groupId>
87+
<artifactId>mockito-junit-jupiter</artifactId>
88+
<scope>test</scope>
89+
</dependency>
8590
<dependency>
8691
<groupId>org.slf4j</groupId>
8792
<artifactId>slf4j-simple</artifactId>
@@ -116,7 +121,6 @@
116121
<dependency>
117122
<groupId>org.mockito</groupId>
118123
<artifactId>mockito-subclass</artifactId>
119-
<version>5.17.0</version>
120124
<scope>test</scope>
121125
</dependency>
122126
</dependencies>
@@ -125,9 +129,9 @@
125129
<plugin>
126130
<groupId>org.apache.maven.plugins</groupId>
127131
<artifactId>maven-surefire-plugin</artifactId>
128-
<version>3.2.3</version>
129132
<configuration>
130-
<argLine>-Dmockito.mock.maker=subclass -Dorg.graalvm.nativeimage.imagecode=agent -agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-metrics,experimental-class-define-support
133+
<argLine>-Dmockito.mock.maker=subclass -Dorg.graalvm.nativeimage.imagecode=agent
134+
-agentlib:native-image-agent=config-output-dir=src/main/resources/META-INF/native-image/software.amazon.lambda/powertools-metrics,experimental-class-define-support
131135
--add-opens java.base/java.util=ALL-UNNAMED
132136
--add-opens java.base/java.lang=ALL-UNNAMED
133137
</argLine>
@@ -142,7 +146,6 @@
142146
<dependency>
143147
<groupId>org.mockito</groupId>
144148
<artifactId>mockito-subclass</artifactId>
145-
<version>5.17.0</version>
146149
<scope>test</scope>
147150
</dependency>
148151
</dependencies>
@@ -151,7 +154,7 @@
151154
<plugin>
152155
<groupId>org.graalvm.buildtools</groupId>
153156
<artifactId>native-maven-plugin</artifactId>
154-
<version>0.10.2</version> <!-- or newer version -->
157+
<version>0.10.6</version>
155158
<extensions>true</extensions>
156159
<executions>
157160
<execution>
@@ -178,16 +181,26 @@
178181
<buildArg>--initialize-at-build-time=org.junit.Ignore</buildArg>
179182
<buildArg>--initialize-at-build-time=java.lang.annotation.Annotation</buildArg>
180183
<buildArg>--initialize-at-build-time=org.junit.runners.model.FrameworkField</buildArg>
181-
<buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg>
182-
<buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg>
183-
<buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg>
184-
<buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg>
185-
<buildArg>--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg>
186-
<buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg>
187-
<buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg>
188-
<buildArg>--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg>
189-
<buildArg>--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg>
190-
<buildArg>--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg>
184+
<buildArg>
185+
--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg>
186+
<buildArg>
187+
--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic</buildArg>
188+
<buildArg>
189+
--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$1</buildArg>
190+
<buildArg>
191+
--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Argument$BindingMechanic$2</buildArg>
192+
<buildArg>
193+
--initialize-at-build-time=net.bytebuddy.utility.dispatcher.JavaDispatcher$DynamicClassLoader</buildArg>
194+
<buildArg>
195+
--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable</buildArg>
196+
<buildArg>
197+
--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$AbstractBase</buildArg>
198+
<buildArg>
199+
--initialize-at-build-time=net.bytebuddy.description.type.TypeDescription$ForLoadedType</buildArg>
200+
<buildArg>
201+
--initialize-at-build-time=net.bytebuddy.description.method.MethodDescription$ForLoadedMethod</buildArg>
202+
<buildArg>
203+
--initialize-at-build-time=net.bytebuddy.implementation.bind.annotation.Super$Instantiation$2</buildArg>
191204
<buildArg>
192205
--trace-class-initialization=net.bytebuddy.description.type.TypeDescription$ForLoadedType,net.bytebuddy.description.method.MethodDescription$ForLoadedMethod,net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$ForLoadedExecutable
193206
</buildArg>
@@ -207,5 +220,16 @@
207220
<directory>src/main/resources</directory>
208221
</resource>
209222
</resources>
223+
<plugins>
224+
<plugin>
225+
<groupId>org.apache.maven.plugins</groupId>
226+
<artifactId>maven-surefire-plugin</artifactId>
227+
<configuration>
228+
<environmentVariables>
229+
<AWS_EMF_ENVIRONMENT>Lambda</AWS_EMF_ENVIRONMENT>
230+
</environmentVariables>
231+
</configuration>
232+
</plugin>
233+
</plugins>
210234
</build>
211235
</project>
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
/*
2+
* Copyright 2023 Amazon.com, Inc. or its affiliates.
3+
* Licensed under the Apache License, Version 2.0 (the
4+
* "License"); you may not use this file except in compliance
5+
* with the License. You may obtain a copy of the License at
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
* limitations under the License.
12+
*
13+
*/
14+
15+
package software.amazon.lambda.powertools.metrics;
16+
17+
import static org.assertj.core.api.Assertions.assertThat;
18+
19+
import java.io.ByteArrayOutputStream;
20+
import java.io.PrintStream;
21+
import java.lang.reflect.Method;
22+
import java.util.HashMap;
23+
import java.util.Map;
24+
25+
import org.junit.jupiter.api.AfterEach;
26+
import org.junit.jupiter.api.BeforeEach;
27+
import org.junit.jupiter.api.Test;
28+
import org.junitpioneer.jupiter.SetEnvironmentVariable;
29+
30+
import com.amazonaws.services.lambda.runtime.Context;
31+
import com.amazonaws.services.lambda.runtime.RequestHandler;
32+
import com.fasterxml.jackson.databind.JsonNode;
33+
import com.fasterxml.jackson.databind.ObjectMapper;
34+
35+
import software.amazon.lambda.powertools.common.internal.LambdaHandlerProcessor;
36+
import software.amazon.lambda.powertools.metrics.model.MetricUnit;
37+
import software.amazon.lambda.powertools.metrics.testutils.TestContext;
38+
39+
/**
40+
* Tests to verify the hierarchy of precedence for configuration:
41+
* 1. Metrics annotation
42+
* 2. MetricsLoggerBuilder
43+
* 3. Environment variables
44+
*/
45+
class ConfigurationPrecedenceTest {
46+
47+
private final PrintStream standardOut = System.out;
48+
private final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream();
49+
private final ObjectMapper objectMapper = new ObjectMapper();
50+
51+
@BeforeEach
52+
void setUp() throws Exception {
53+
System.setOut(new PrintStream(outputStreamCaptor));
54+
55+
// Reset LambdaHandlerProcessor's SERVICE_NAME
56+
Method resetServiceName = LambdaHandlerProcessor.class.getDeclaredMethod("resetServiceName");
57+
resetServiceName.setAccessible(true);
58+
resetServiceName.invoke(null);
59+
60+
// Reset IS_COLD_START
61+
java.lang.reflect.Field coldStartField = LambdaHandlerProcessor.class.getDeclaredField("IS_COLD_START");
62+
coldStartField.setAccessible(true);
63+
coldStartField.set(null, null);
64+
}
65+
66+
@AfterEach
67+
void tearDown() throws Exception {
68+
System.setOut(standardOut);
69+
70+
// Reset the singleton state between tests
71+
java.lang.reflect.Field field = MetricsLoggerFactory.class.getDeclaredField("metricsLogger");
72+
field.setAccessible(true);
73+
field.set(null, null);
74+
75+
field = MetricsLoggerFactory.class.getDeclaredField("provider");
76+
field.setAccessible(true);
77+
field.set(null, new software.amazon.lambda.powertools.metrics.provider.EmfMetricsProvider());
78+
}
79+
80+
@Test
81+
@SetEnvironmentVariable(key = "POWERTOOLS_METRICS_NAMESPACE", value = "EnvNamespace")
82+
@SetEnvironmentVariable(key = "POWERTOOLS_SERVICE_NAME", value = "EnvService")
83+
void annotationShouldOverrideBuilderAndEnvironment() throws Exception {
84+
// Given
85+
// Configure with builder first
86+
MetricsLoggerBuilder.builder()
87+
.withNamespace("BuilderNamespace")
88+
.withService("BuilderService")
89+
.build();
90+
91+
RequestHandler<Map<String, Object>, String> handler = new HandlerWithMetricsAnnotation();
92+
Context context = new TestContext();
93+
Map<String, Object> input = new HashMap<>();
94+
95+
// When
96+
handler.handleRequest(input, context);
97+
98+
// Then
99+
String emfOutput = outputStreamCaptor.toString().trim();
100+
JsonNode rootNode = objectMapper.readTree(emfOutput);
101+
102+
// Annotation values should take precedence
103+
assertThat(rootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Namespace").asText())
104+
.isEqualTo("AnnotationNamespace");
105+
assertThat(rootNode.has("Service")).isTrue();
106+
assertThat(rootNode.get("Service").asText()).isEqualTo("AnnotationService");
107+
}
108+
109+
@Test
110+
@SetEnvironmentVariable(key = "POWERTOOLS_METRICS_NAMESPACE", value = "EnvNamespace")
111+
@SetEnvironmentVariable(key = "POWERTOOLS_SERVICE_NAME", value = "EnvService")
112+
void builderShouldOverrideEnvironment() throws Exception {
113+
// Given
114+
// Configure with builder
115+
MetricsLoggerBuilder.builder()
116+
.withNamespace("BuilderNamespace")
117+
.withService("BuilderService")
118+
.build();
119+
120+
RequestHandler<Map<String, Object>, String> handler = new HandlerWithDefaultMetricsAnnotation();
121+
Context context = new TestContext();
122+
Map<String, Object> input = new HashMap<>();
123+
124+
// When
125+
handler.handleRequest(input, context);
126+
127+
// Then
128+
String emfOutput = outputStreamCaptor.toString().trim();
129+
JsonNode rootNode = objectMapper.readTree(emfOutput);
130+
131+
// Builder values should take precedence over environment variables
132+
assertThat(rootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Namespace").asText())
133+
.isEqualTo("BuilderNamespace");
134+
assertThat(rootNode.has("Service")).isTrue();
135+
assertThat(rootNode.get("Service").asText()).isEqualTo("BuilderService");
136+
}
137+
138+
@Test
139+
@SetEnvironmentVariable(key = "POWERTOOLS_METRICS_NAMESPACE", value = "EnvNamespace")
140+
@SetEnvironmentVariable(key = "POWERTOOLS_SERVICE_NAME", value = "EnvService")
141+
void environmentVariablesShouldBeUsedWhenNoOverrides() throws Exception {
142+
// Given
143+
RequestHandler<Map<String, Object>, String> handler = new HandlerWithDefaultMetricsAnnotation();
144+
Context context = new TestContext();
145+
Map<String, Object> input = new HashMap<>();
146+
147+
// When
148+
handler.handleRequest(input, context);
149+
150+
// Then
151+
String emfOutput = outputStreamCaptor.toString().trim();
152+
JsonNode rootNode = objectMapper.readTree(emfOutput);
153+
154+
// Environment variable values should be used
155+
assertThat(rootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Namespace").asText())
156+
.isEqualTo("EnvNamespace");
157+
assertThat(rootNode.has("Service")).isTrue();
158+
assertThat(rootNode.get("Service").asText()).isEqualTo("EnvService");
159+
}
160+
161+
@Test
162+
void shouldUseDefaultsWhenNoConfiguration() throws Exception {
163+
// Given
164+
RequestHandler<Map<String, Object>, String> handler = new HandlerWithDefaultMetricsAnnotation();
165+
Context context = new TestContext();
166+
Map<String, Object> input = new HashMap<>();
167+
168+
// When
169+
handler.handleRequest(input, context);
170+
171+
// Then
172+
String emfOutput = outputStreamCaptor.toString().trim();
173+
JsonNode rootNode = objectMapper.readTree(emfOutput);
174+
175+
// Default values should be used
176+
assertThat(rootNode.get("_aws").get("CloudWatchMetrics").get(0).get("Namespace").asText())
177+
.isEqualTo("aws-embedded-metrics");
178+
assertThat(rootNode.has("Service")).isTrue();
179+
assertThat(rootNode.get("Service").asText()).isEqualTo("service_undefined");
180+
}
181+
182+
private static class HandlerWithMetricsAnnotation implements RequestHandler<Map<String, Object>, String> {
183+
@Override
184+
@Metrics(namespace = "AnnotationNamespace", service = "AnnotationService")
185+
public String handleRequest(Map<String, Object> input, Context context) {
186+
MetricsLogger metricsLogger = MetricsLoggerFactory.getMetricsLogger();
187+
metricsLogger.addMetric("test-metric", 100, MetricUnit.COUNT);
188+
return "OK";
189+
}
190+
}
191+
192+
private static class HandlerWithDefaultMetricsAnnotation implements RequestHandler<Map<String, Object>, String> {
193+
@Override
194+
@Metrics
195+
public String handleRequest(Map<String, Object> input, Context context) {
196+
MetricsLogger metricsLogger = MetricsLoggerFactory.getMetricsLogger();
197+
metricsLogger.addMetric("test-metric", 100, MetricUnit.COUNT);
198+
return "OK";
199+
}
200+
}
201+
202+
}

0 commit comments

Comments
 (0)