Skip to content

Commit d7f62e0

Browse files
committed
test(metrics): add DurableContext support tests for log_metrics decorator
- Add tests for capture_cold_start_metric with DurableContext - Add regression test for standard LambdaContext compatibility - Verify function_name extraction from unwrapped context Relates to #7763
1 parent 4257761 commit d7f62e0

File tree

1 file changed

+160
-0
lines changed

1 file changed

+160
-0
lines changed
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
"""Tests for Metrics with DurableContext support."""
2+
3+
import json
4+
from collections import namedtuple
5+
from unittest.mock import Mock
6+
7+
import pytest
8+
9+
from aws_lambda_powertools import Metrics
10+
from aws_lambda_powertools.utilities.typing import DurableContextProtocol
11+
12+
# Reset cold start flag before each test
13+
from aws_lambda_powertools.metrics.provider import cold_start
14+
15+
16+
def capture_metrics_output(capsys):
17+
return json.loads(capsys.readouterr().out.strip())
18+
19+
20+
def reset_cold_start_flag():
21+
cold_start.is_cold_start = True
22+
23+
24+
@pytest.fixture
25+
def durable_context(lambda_context):
26+
"""Create a mock DurableContext with embedded Lambda context."""
27+
durable_ctx = Mock(spec=DurableContextProtocol)
28+
durable_ctx.lambda_context = lambda_context
29+
durable_ctx.state = Mock(operations=[{"id": "op1"}])
30+
return durable_ctx
31+
32+
33+
@pytest.fixture
34+
def lambda_context_with_function_name():
35+
"""Create a simple lambda context with function_name."""
36+
LambdaContext = namedtuple("LambdaContext", "function_name")
37+
return LambdaContext("test_function")
38+
39+
40+
def test_log_metrics_with_durable_context_basic(capsys, namespace, service, durable_context):
41+
"""Test that log_metrics works with DurableContext."""
42+
# GIVEN Metrics is initialized
43+
my_metrics = Metrics(service=service, namespace=namespace)
44+
45+
# WHEN log_metrics decorator is used with a handler that receives DurableContext
46+
@my_metrics.log_metrics
47+
def lambda_handler(evt, context):
48+
my_metrics.add_metric(name="test_metric", value=1.0, unit="Count")
49+
50+
lambda_handler({}, durable_context)
51+
52+
# THEN metrics should be emitted successfully
53+
output = capture_metrics_output(capsys)
54+
55+
assert output["test_metric"] == [1.0]
56+
assert output["service"] == service
57+
58+
59+
def test_log_metrics_capture_cold_start_with_durable_context(capsys, namespace, service):
60+
"""Test that capture_cold_start_metric works with DurableContext."""
61+
reset_cold_start_flag()
62+
63+
# GIVEN Metrics is initialized
64+
my_metrics = Metrics(service=service, namespace=namespace)
65+
66+
# Create a DurableContext with embedded Lambda context
67+
LambdaContext = namedtuple("LambdaContext", "function_name")
68+
lambda_ctx = LambdaContext("durable_test_function")
69+
70+
durable_ctx = Mock(spec=DurableContextProtocol)
71+
durable_ctx.lambda_context = lambda_ctx
72+
durable_ctx.state = Mock(operations=[{"id": "op1"}])
73+
74+
# WHEN log_metrics is used with capture_cold_start_metric and DurableContext
75+
@my_metrics.log_metrics(capture_cold_start_metric=True)
76+
def lambda_handler(evt, context):
77+
my_metrics.add_metric(name="test_metric", value=1.0, unit="Count")
78+
79+
lambda_handler({}, durable_ctx)
80+
81+
# THEN ColdStart metric should be captured with the function name from unwrapped context
82+
output = capture_metrics_output(capsys)
83+
84+
assert "ColdStart" in output or output.get("ColdStart") == [1.0]
85+
# The function_name should come from the unwrapped lambda_context
86+
assert output.get("function_name") == "durable_test_function"
87+
assert output["service"] == service
88+
89+
90+
def test_log_metrics_capture_cold_start_with_durable_context_explicit_function_name(
91+
capsys, namespace, service
92+
):
93+
"""Test capture_cold_start_metric with explicit function_name and DurableContext."""
94+
reset_cold_start_flag()
95+
96+
# GIVEN Metrics is initialized with explicit function_name
97+
my_metrics = Metrics(service=service, namespace=namespace, function_name="explicit_function")
98+
99+
# Create a DurableContext
100+
LambdaContext = namedtuple("LambdaContext", "function_name")
101+
lambda_ctx = LambdaContext("context_function")
102+
103+
durable_ctx = Mock(spec=DurableContextProtocol)
104+
durable_ctx.lambda_context = lambda_ctx
105+
durable_ctx.state = Mock(operations=[{"id": "op1"}])
106+
107+
# WHEN log_metrics is used with capture_cold_start_metric
108+
@my_metrics.log_metrics(capture_cold_start_metric=True)
109+
def lambda_handler(evt, context):
110+
pass
111+
112+
lambda_handler({}, durable_ctx)
113+
114+
# THEN explicit function_name should take priority
115+
output = capture_metrics_output(capsys)
116+
117+
assert output.get("function_name") == "explicit_function"
118+
119+
120+
def test_log_metrics_with_standard_context_still_works(capsys, namespace, service, lambda_context):
121+
"""Test that standard Lambda context still works (regression test)."""
122+
# GIVEN Metrics is initialized
123+
my_metrics = Metrics(service=service, namespace=namespace)
124+
125+
# WHEN log_metrics decorator is used with standard LambdaContext
126+
@my_metrics.log_metrics
127+
def lambda_handler(evt, context):
128+
my_metrics.add_metric(name="regression_test", value=42.0, unit="Count")
129+
130+
lambda_handler({}, lambda_context)
131+
132+
# THEN metrics should be emitted successfully
133+
output = capture_metrics_output(capsys)
134+
135+
assert output["regression_test"] == [42.0]
136+
assert output["service"] == service
137+
138+
139+
def test_log_metrics_capture_cold_start_standard_context_still_works(capsys, namespace, service):
140+
"""Test that capture_cold_start_metric with standard context still works (regression test)."""
141+
reset_cold_start_flag()
142+
143+
# GIVEN Metrics is initialized
144+
my_metrics = Metrics(service=service, namespace=namespace)
145+
146+
LambdaContext = namedtuple("LambdaContext", "function_name")
147+
standard_context = LambdaContext("standard_function")
148+
149+
# WHEN log_metrics is used with capture_cold_start_metric and standard context
150+
@my_metrics.log_metrics(capture_cold_start_metric=True)
151+
def lambda_handler(evt, context):
152+
pass
153+
154+
lambda_handler({}, standard_context)
155+
156+
# THEN ColdStart metric should be captured
157+
output = capture_metrics_output(capsys)
158+
159+
assert "ColdStart" in output or output.get("ColdStart") == [1.0]
160+
assert output.get("function_name") == "standard_function"

0 commit comments

Comments
 (0)