Skip to content

Commit 9fbef24

Browse files
committed
add cw tests for lambda INVOCATIONS and ERRORS metrics
1 parent 177773b commit 9fbef24

File tree

2 files changed

+222
-0
lines changed

2 files changed

+222
-0
lines changed
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
from datetime import datetime, timedelta
2+
from typing import TYPE_CHECKING
3+
4+
import pytest
5+
6+
from localstack.testing.pytest import markers
7+
from localstack.testing.pytest.snapshot import is_aws
8+
from localstack.utils.strings import short_uid
9+
10+
if TYPE_CHECKING:
11+
from mypy_boto3_cloudwatch import CloudWatchClient
12+
from localstack.utils.sync import retry
13+
14+
TEST_SUCCESSFUL_LAMBDA = """
15+
def handler(event, context):
16+
return {"success": "ok"}
17+
"""
18+
19+
TEST_FAILING_LAMBDA = """
20+
def handler(event, context):
21+
raise Exception('fail on purpose')
22+
"""
23+
24+
25+
class TestCloudWatchLambdaMetrics:
26+
"""
27+
Tests for metrics that are reported automatically by Lambda
28+
see also https://docs.aws.amazon.com/lambda/latest/dg/monitoring-metrics.html
29+
"""
30+
31+
@markers.aws.validated
32+
def test_lambda_invoke_successful(self, aws_client, create_lambda_function, snapshot):
33+
"""
34+
successful invocation of lambda should report "Invocations" metric
35+
"""
36+
fn_name = f"fn-cw-{short_uid()}"
37+
create_lambda_function(
38+
func_name=fn_name,
39+
handler_file=TEST_SUCCESSFUL_LAMBDA,
40+
runtime="python3.9",
41+
)
42+
result = aws_client.lambda_.invoke(FunctionName=fn_name)
43+
assert result["StatusCode"] == 200
44+
snapshot.match("invoke", result)
45+
46+
# wait for metrics
47+
result = retry(
48+
lambda: self._wait_for_lambda_metric(
49+
aws_client.cloudwatch,
50+
fn_name=fn_name,
51+
metric_name="Invocations",
52+
expected_return=[1.0],
53+
),
54+
retries=200 if is_aws() else 20,
55+
sleep=10 if is_aws() else 1,
56+
)
57+
snapshot.match("get-metric-data", result)
58+
59+
@pytest.mark.skipif(not is_aws(), reason="'Errors' metrics not reported by LS")
60+
@markers.aws.validated
61+
def test_lambda_invoke_error(self, aws_client, create_lambda_function, snapshot):
62+
"""
63+
Unsuccessful Invocation -> resulting in error, should report
64+
"Errors" and "Invocations" metrics
65+
"""
66+
fn_name = f"fn-cw-{short_uid()}"
67+
create_lambda_function(
68+
func_name=fn_name,
69+
handler_file=TEST_FAILING_LAMBDA,
70+
runtime="python3.9",
71+
)
72+
result = aws_client.lambda_.invoke(FunctionName=fn_name)
73+
snapshot.match("invoke", result)
74+
75+
# wait for metrics
76+
invocation_res = retry(
77+
lambda: self._wait_for_lambda_metric(
78+
aws_client.cloudwatch,
79+
fn_name=fn_name,
80+
metric_name="Invocations",
81+
expected_return=[1.0],
82+
),
83+
retries=200 if is_aws() else 20,
84+
sleep=10 if is_aws() else 1,
85+
)
86+
snapshot.match("get-metric-data-invocations", invocation_res)
87+
88+
# wait for "Errors"
89+
error_res = retry(
90+
lambda: self._wait_for_lambda_metric(
91+
aws_client.cloudwatch,
92+
fn_name=fn_name,
93+
metric_name="Errors",
94+
expected_return=[1.0],
95+
),
96+
retries=200 if is_aws() else 20,
97+
sleep=10 if is_aws() else 1,
98+
)
99+
snapshot.match("get-metric-data-errors", error_res)
100+
101+
def _wait_for_lambda_metric(
102+
self,
103+
cloudwatch_client: "CloudWatchClient",
104+
fn_name: str,
105+
metric_name: str,
106+
expected_return: list[float],
107+
):
108+
namespace = "AWS/Lambda"
109+
dimension = [{"Name": "FunctionName", "Value": fn_name}]
110+
metric_query = {
111+
"Id": "m1",
112+
"MetricStat": {
113+
"Metric": {
114+
"Namespace": namespace,
115+
"MetricName": metric_name,
116+
"Dimensions": dimension,
117+
},
118+
"Period": 3600,
119+
"Stat": "Sum",
120+
},
121+
}
122+
res = cloudwatch_client.get_metric_data(
123+
MetricDataQueries=[metric_query],
124+
StartTime=datetime.utcnow() - timedelta(hours=1),
125+
EndTime=datetime.utcnow(),
126+
)
127+
assert res["MetricDataResults"][0]["Values"] == expected_return
128+
return res
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
{
2+
"tests/aws/services/cloudwatch/test_cloudwatch_metrics.py::TestCloudWatchLambdaMetrics::test_lambda_invoke_successful": {
3+
"recorded-date": "15-11-2023, 19:46:04",
4+
"recorded-content": {
5+
"invoke": {
6+
"ExecutedVersion": "$LATEST",
7+
"Payload": {
8+
"success": "ok"
9+
},
10+
"StatusCode": 200,
11+
"ResponseMetadata": {
12+
"HTTPHeaders": {},
13+
"HTTPStatusCode": 200
14+
}
15+
},
16+
"get-metric-data": {
17+
"Messages": [],
18+
"MetricDataResults": [
19+
{
20+
"Id": "m1",
21+
"Label": "Invocations",
22+
"StatusCode": "Complete",
23+
"Timestamps": "timestamp",
24+
"Values": [
25+
1.0
26+
]
27+
}
28+
],
29+
"ResponseMetadata": {
30+
"HTTPHeaders": {},
31+
"HTTPStatusCode": 200
32+
}
33+
}
34+
}
35+
},
36+
"tests/aws/services/cloudwatch/test_cloudwatch_metrics.py::TestCloudWatchLambdaMetrics::test_lambda_invoke_error": {
37+
"recorded-date": "15-11-2023, 19:49:06",
38+
"recorded-content": {
39+
"invoke": {
40+
"ExecutedVersion": "$LATEST",
41+
"FunctionError": "Unhandled",
42+
"Payload": {
43+
"errorMessage": "fail on purpose",
44+
"errorType": "Exception",
45+
"requestId": "<uuid:1>",
46+
"stackTrace": [
47+
" File \"/var/task/handler.py\", line 3, in handler\n raise Exception('fail on purpose')\n"
48+
]
49+
},
50+
"StatusCode": 200,
51+
"ResponseMetadata": {
52+
"HTTPHeaders": {},
53+
"HTTPStatusCode": 200
54+
}
55+
},
56+
"get-metric-data-invocations": {
57+
"Messages": [],
58+
"MetricDataResults": [
59+
{
60+
"Id": "m1",
61+
"Label": "Invocations",
62+
"StatusCode": "Complete",
63+
"Timestamps": "timestamp",
64+
"Values": [
65+
1.0
66+
]
67+
}
68+
],
69+
"ResponseMetadata": {
70+
"HTTPHeaders": {},
71+
"HTTPStatusCode": 200
72+
}
73+
},
74+
"get-metric-data-errors": {
75+
"Messages": [],
76+
"MetricDataResults": [
77+
{
78+
"Id": "m1",
79+
"Label": "Errors",
80+
"StatusCode": "Complete",
81+
"Timestamps": "timestamp",
82+
"Values": [
83+
1.0
84+
]
85+
}
86+
],
87+
"ResponseMetadata": {
88+
"HTTPHeaders": {},
89+
"HTTPStatusCode": 200
90+
}
91+
}
92+
}
93+
}
94+
}

0 commit comments

Comments
 (0)