Skip to content

Commit 074ff4f

Browse files
committed
refactor(helpers): set good default on end_date metrics; fix yield
1 parent f37e988 commit 074ff4f

File tree

3 files changed

+61
-60
lines changed

3 files changed

+61
-60
lines changed

tests/e2e/metrics/conftest.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,20 @@ def infrastructure(request: fixtures.SubRequest, tmp_path_factory: pytest.TempPa
3333
if worker_id == "master":
3434
# no parallelization, deploy stack and let fixture be cached
3535
yield stack.deploy()
36-
37-
# tmp dir shared by all workers
38-
root_tmp_dir = tmp_path_factory.getbasetemp().parent
39-
40-
cache = root_tmp_dir / "cache.json"
41-
with FileLock(f"{cache}.lock"):
42-
# If cache exists, return stack outputs back
43-
# otherwise it's the first run by the main worker
44-
# deploy and return stack outputs so subsequent workers can reuse
45-
if cache.is_file():
46-
stack_outputs = json.loads(cache.read_text())
47-
else:
48-
stack_outputs: Dict = stack.deploy()
49-
cache.write_text(json.dumps(stack_outputs))
50-
yield stack_outputs
36+
else:
37+
# tmp dir shared by all workers
38+
root_tmp_dir = tmp_path_factory.getbasetemp().parent
39+
40+
cache = root_tmp_dir / "cache.json"
41+
with FileLock(f"{cache}.lock"):
42+
# If cache exists, return stack outputs back
43+
# otherwise it's the first run by the main worker
44+
# deploy and return stack outputs so subsequent workers can reuse
45+
if cache.is_file():
46+
stack_outputs = json.loads(cache.read_text())
47+
else:
48+
stack_outputs: Dict = stack.deploy()
49+
cache.write_text(json.dumps(stack_outputs))
50+
yield stack_outputs
5151
finally:
5252
stack.delete()

tests/e2e/metrics/test_metrics.py

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
import datetime
21
import json
32

43
import pytest
54

65
from tests.e2e.utils import helpers
76

87

8+
@pytest.fixture
9+
def basic_handler_fn(infrastructure: dict) -> str:
10+
return infrastructure.get("BasicHandler", "")
11+
12+
913
@pytest.fixture
1014
def basic_handler_fn_arn(infrastructure: dict) -> str:
1115
return infrastructure.get("BasicHandlerArn", "")
@@ -24,22 +28,19 @@ def cold_start_fn_arn(infrastructure: dict) -> str:
2428
METRIC_NAMESPACE = "powertools-e2e-metric"
2529

2630

27-
def test_basic_lambda_metric_is_visible(basic_handler_fn):
31+
def test_basic_lambda_metric_is_visible(basic_handler_fn: str, basic_handler_fn_arn: str):
2832
# GIVEN
2933
metric_name = helpers.build_metric_name()
3034
service = helpers.build_service_name()
35+
dimensions = helpers.build_add_dimensions_input(service=service)
3136
metrics = helpers.build_multiple_add_metric_input(metric_name=metric_name, value=1, quantity=3)
3237

3338
# WHEN
3439
event = json.dumps({"metrics": metrics, "service": service, "namespace": METRIC_NAMESPACE})
35-
_, execution_time = helpers.trigger_lambda(lambda_arn=basic_handler_fn, payload=event)
40+
_, execution_time = helpers.trigger_lambda(lambda_arn=basic_handler_fn_arn, payload=event)
3641

3742
metrics = helpers.get_metrics(
38-
start_date=execution_time,
39-
end_date=execution_time + datetime.timedelta(minutes=2),
40-
namespace=METRIC_NAMESPACE,
41-
service_name=service,
42-
metric_name=metric_name,
43+
namespace=METRIC_NAMESPACE, start_date=execution_time, metric_name=metric_name, dimensions=dimensions
4344
)
4445

4546
# THEN
@@ -58,11 +59,7 @@ def test_cold_start_metric(cold_start_fn_arn: str, cold_start_fn: str):
5859
_, execution_time = helpers.trigger_lambda(lambda_arn=cold_start_fn_arn, payload=event)
5960

6061
metrics = helpers.get_metrics(
61-
start_date=execution_time,
62-
end_date=execution_time + datetime.timedelta(minutes=2),
63-
namespace=METRIC_NAMESPACE,
64-
metric_name=metric_name,
65-
dimensions=dimensions,
62+
namespace=METRIC_NAMESPACE, start_date=execution_time, metric_name=metric_name, dimensions=dimensions
6663
)
6764

6865
# THEN
@@ -72,5 +69,4 @@ def test_cold_start_metric(cold_start_fn_arn: str, cold_start_fn: str):
7269

7370
# helpers: adjust retries and wait to be much smaller
7471
# helpers: make retry config adjustable
75-
# Infra: Add temporary Powertools Layer
7672
# Create separate Infra class so they can live side by side

tests/e2e/utils/helpers.py

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -68,21 +68,50 @@ def get_metrics(
6868
namespace: str,
6969
start_date: datetime,
7070
metric_name: str,
71-
service_name: str = "",
7271
dimensions: Optional[List[DimensionTypeDef]] = None,
7372
cw_client: Optional[CloudWatchClient] = None,
7473
end_date: Optional[datetime] = None,
7574
period: int = 60,
7675
stat: str = "Sum",
7776
) -> MetricDataResultTypeDef:
77+
"""Fetch CloudWatch Metrics
78+
79+
It takes into account eventual consistency with up to 10 retries and 1s jitter.
80+
81+
Parameters
82+
----------
83+
namespace : str
84+
Metric Namespace
85+
start_date : datetime
86+
Start window to fetch metrics
87+
metric_name : str
88+
Metric name
89+
dimensions : Optional[List[DimensionTypeDef]], optional
90+
List of Metric Dimension, by default None
91+
cw_client : Optional[CloudWatchClient], optional
92+
Boto3 CloudWatch low-level client (boto3.client("cloudwatch"), by default None
93+
end_date : Optional[datetime], optional
94+
End window to fetch metrics, by default start_date + 2 minutes window
95+
period : int, optional
96+
Time period to fetch metrics for, by default 60
97+
stat : str, optional
98+
Aggregation function to use when fetching metrics, by default "Sum"
99+
100+
Returns
101+
-------
102+
MetricDataResultTypeDef
103+
_description_
104+
105+
Raises
106+
------
107+
ValueError
108+
When no metric is found within retry window
109+
"""
78110
cw_client = cw_client or boto3.client("cloudwatch")
111+
end_date = end_date or start_date + datetime.timedelta(minutes=2)
112+
79113
metric_query = build_metric_query_data(
80-
namespace=namespace,
81-
metric_name=metric_name,
82-
service_name=service_name,
83-
period=period,
84-
stat=stat,
85-
dimensions=dimensions,
114+
namespace=namespace, metric_name=metric_name, period=period, stat=stat, dimensions=dimensions
86115
)
87116

88117
response = cw_client.get_metric_data(
@@ -162,17 +191,11 @@ def build_random_value(nbytes: int = 10) -> str:
162191
def build_metric_query_data(
163192
namespace: str,
164193
metric_name: str,
165-
service_name: str = "",
166194
period: int = 60,
167195
stat: str = "Sum",
168196
dimensions: Optional[List[DimensionTypeDef]] = None,
169197
) -> List[MetricDataQueryTypeDef]:
170198
dimensions = dimensions or []
171-
172-
# Maintenance: get rid of service_name param to avoid future mistakes
173-
if service_name:
174-
dimensions.append({"Name": "service", "Value": service_name})
175-
176199
data_query: List[MetricDataQueryTypeDef] = [
177200
{
178201
"Id": metric_name.lower(),
@@ -235,24 +258,6 @@ def build_multiple_add_metric_input(
235258
return [{"name": metric_name, "unit": unit, "value": value} for _ in range(quantity)]
236259

237260

238-
def build_add_dimension_input(name: str, value: str) -> DimensionTypeDef:
239-
"""Create a dimension input to be used with either Metrics.add_dimension()
240-
241-
Parameters
242-
----------
243-
name : str
244-
dimension name
245-
value : float
246-
dimension value
247-
248-
Returns
249-
-------
250-
Dict
251-
Metric dimension input
252-
"""
253-
return {"Name": name, "Value": value}
254-
255-
256261
def build_add_dimensions_input(**dimensions) -> List[DimensionTypeDef]:
257262
"""Create dimensions input to be used with either get_metrics or Metrics.add_dimension()
258263

0 commit comments

Comments
 (0)