Skip to content

Commit 6de3ce2

Browse files
committed
refactor: dummy test using new explicit infra
1 parent 18dadc8 commit 6de3ce2

File tree

5 files changed

+86
-48
lines changed

5 files changed

+86
-48
lines changed

tests/e2e/metrics/conftest.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44

55

66
@pytest.fixture(autouse=True)
7-
def setup_infra():
7+
def infrastructure() -> MetricsStack:
8+
# Use request fixture to remove hardcode handler dir
89
try:
910
stack = MetricsStack()
10-
yield stack.deploy()
11+
stack.deploy()
12+
yield stack
1113
finally:
1214
stack.delete()
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
import os
2-
31
from aws_lambda_powertools import Metrics
42
from aws_lambda_powertools.metrics import MetricUnit
53

6-
METRIC_NAME = os.environ["METRIC_NAME"]
7-
84
metrics = Metrics()
95

106

117
@metrics.log_metrics
128
def lambda_handler(event, context):
13-
metrics.add_metric(name=METRIC_NAME, unit=MetricUnit.Count, value=1)
9+
metric = event.get("metric_name")
10+
metrics.namespace = "powertools-e2e-metric"
11+
metrics.service = event.get("service")
12+
13+
metrics.add_metric(name=metric, unit=MetricUnit.Count, value=1)
1414
return "success"

tests/e2e/metrics/infrastructure.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import boto3
66
import yaml
77
from aws_cdk import App, CfnOutput, RemovalPolicy, Stack, aws_logs
8-
from aws_cdk.aws_lambda import Code, Function, Runtime, Tracing
8+
from aws_cdk.aws_lambda import Code, Function, LayerVersion, Runtime, Tracing
99

1010
from tests.e2e.utils.asset import Assets
1111

@@ -24,6 +24,7 @@ def __init__(self) -> None:
2424
# NOTE: CDK stack account and region are tokens, we need to resolve earlier
2525
self.account_id = self.session.client("sts").get_caller_identity()["Account"]
2626
self.region = self.session.region_name
27+
self.stack_outputs: Dict[str, str] = {}
2728

2829
def create_functions(self):
2930
handlers = list(Path(self.handlers_dir).rglob("*.py"))
@@ -37,6 +38,13 @@ def create_functions(self):
3738
code=source,
3839
handler=f"{fn_name}.lambda_handler",
3940
tracing=Tracing.ACTIVE,
41+
layers=[
42+
LayerVersion.from_layer_version_arn(
43+
self.stack,
44+
"lambda-powertools",
45+
f"arn:aws:lambda:{self.region}:017000801446:layer:AWSLambdaPowertoolsPython:29",
46+
)
47+
],
4048
)
4149

4250
aws_logs.LogGroup(
@@ -86,4 +94,11 @@ def _deploy_stack(self, stack_name: str, template: Dict) -> Dict[str, str]:
8694

8795
stack_details = self.cf_client.describe_stacks(StackName=stack_name)
8896
stack_outputs = stack_details["Stacks"][0]["Outputs"]
89-
return {output["OutputKey"]: output["OutputValue"] for output in stack_outputs if output["OutputKey"]}
97+
self.stack_outputs = {
98+
output["OutputKey"]: output["OutputValue"] for output in stack_outputs if output["OutputKey"]
99+
}
100+
101+
return self.stack_outputs
102+
103+
def get_stack_outputs(self) -> Dict[str, str]:
104+
return self.stack_outputs

tests/e2e/metrics/test_metrics.py

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import datetime
2+
import json
23
import uuid
4+
from typing import Dict, Type
35

4-
import boto3
56
import pytest
67
from e2e import conftest
78
from e2e.utils import helpers
89

10+
from tests.e2e.metrics.infrastructure import MetricsStack
11+
912

1013
@pytest.fixture(scope="module")
1114
def config() -> conftest.LambdaConfig:
@@ -19,22 +22,37 @@ def config() -> conftest.LambdaConfig:
1922
}
2023

2124

22-
def test_basic_lambda_metric_visible(execute_lambda: conftest.InfrastructureOutput, config: conftest.LambdaConfig):
23-
# GIVEN
24-
start_date = execute_lambda.get_lambda_execution_time()
25-
end_date = start_date + datetime.timedelta(minutes=5)
25+
@pytest.fixture
26+
def infra_outputs(infrastructure: Type[MetricsStack]):
27+
return infrastructure.get_stack_outputs()
28+
29+
30+
def test_basic_lambda_metric_is_visible(infra_outputs: Dict[str, str]):
31+
# sourcery skip: aware-datetime-for-utc
32+
execution_time = datetime.datetime.utcnow()
33+
metric_name = "test"
34+
service = "test-metric-is-visible"
35+
namespace = "powertools-e2e-metric"
36+
event = json.dumps({"metric_name": metric_name, "service": service, "namespace": namespace})
37+
ret = helpers.trigger_lambda(lambda_arn=infra_outputs.get("basichandlerarn"), payload=event)
2638

27-
# WHEN
39+
assert ret is None # we could test in the actual response now
40+
41+
# NOTE: find out why we're getting empty metrics
2842
metrics = helpers.get_metrics(
29-
start_date=start_date,
30-
end_date=end_date,
31-
namespace=config["environment_variables"]["POWERTOOLS_METRICS_NAMESPACE"],
32-
metric_name=config["environment_variables"]["METRIC_NAME"],
33-
service_name=config["environment_variables"]["POWERTOOLS_SERVICE_NAME"],
34-
cw_client=boto3.client(service_name="cloudwatch"),
43+
start_date=execution_time,
44+
end_date=execution_time + datetime.timedelta(minutes=2),
45+
namespace=namespace,
46+
service_name=service,
47+
metric_name=metric_name,
3548
)
49+
assert metrics is not None
50+
3651

37-
# THEN
38-
assert metrics.get("Timestamps") and len(metrics.get("Timestamps")) == 1
39-
assert metrics.get("Values") and len(metrics.get("Values")) == 1
40-
assert metrics.get("Values") and metrics.get("Values")[0] == 1
52+
# helpers: create client on the fly if not passed
53+
# accept payload to be sent as part of invocation
54+
# helpers: adjust retries and wait to be much smaller
55+
# Infra: Create dynamic Enum/DataClass to reduce guessing on outputs
56+
# Infra: Fix outputs
57+
# Infra: Add temporary Powertools Layer
58+
# Powertools: should have a method to set namespace at runtime

tests/e2e/utils/helpers.py

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import json
22
from datetime import datetime
33
from functools import lru_cache
4-
from typing import Dict, List, Optional, Union
4+
from typing import TYPE_CHECKING, Dict, List, Optional, Union
55

6-
from mypy_boto3_cloudwatch import type_defs
7-
from mypy_boto3_cloudwatch.client import CloudWatchClient
8-
from mypy_boto3_lambda.client import LambdaClient
9-
from mypy_boto3_xray.client import XRayClient
6+
if TYPE_CHECKING:
7+
from mypy_boto3_cloudwatch import MetricDataResultTypeDef
8+
from mypy_boto3_cloudwatch.client import CloudWatchClient
9+
from mypy_boto3_lambda.client import LambdaClient
10+
from mypy_boto3_xray.client import XRayClient
11+
12+
import boto3
1013
from pydantic import BaseModel
1114
from retry import retry
1215

@@ -33,14 +36,14 @@ class TraceSegment(BaseModel):
3336
annotations: Dict = {}
3437

3538

36-
def trigger_lambda(lambda_arn: str, client: LambdaClient):
37-
response = client.invoke(FunctionName=lambda_arn, InvocationType="RequestResponse")
38-
return response
39+
def trigger_lambda(lambda_arn: str, payload: str, client: Optional["LambdaClient"] = None):
40+
client = client or boto3.client("lambda")
41+
return client.invoke(FunctionName=lambda_arn, InvocationType="RequestResponse", Payload=payload)
3942

4043

4144
@lru_cache(maxsize=10, typed=False)
4245
@retry(ValueError, delay=1, jitter=1, tries=20)
43-
def get_logs(lambda_function_name: str, log_client: CloudWatchClient, start_time: int, **kwargs: dict) -> List[Log]:
46+
def get_logs(lambda_function_name: str, log_client: "CloudWatchClient", start_time: int, **kwargs: dict) -> List[Log]:
4447
response = log_client.filter_log_events(logGroupName=f"/aws/lambda/{lambda_function_name}", startTime=start_time)
4548
if not response["events"]:
4649
raise ValueError("Empty response from Cloudwatch Logs. Repeating...")
@@ -56,27 +59,27 @@ def get_logs(lambda_function_name: str, log_client: CloudWatchClient, start_time
5659

5760

5861
@lru_cache(maxsize=10, typed=False)
59-
@retry(ValueError, delay=1, jitter=1, tries=20)
62+
@retry(ValueError, delay=1, jitter=1, tries=5)
6063
def get_metrics(
6164
namespace: str,
62-
cw_client: CloudWatchClient,
6365
start_date: datetime,
6466
metric_name: str,
6567
service_name: str,
68+
cw_client: Optional["CloudWatchClient"] = None,
6669
end_date: Optional[datetime] = None,
67-
) -> type_defs.MetricDataResultTypeDef:
68-
response = cw_client.get_metric_data(
69-
MetricDataQueries=[
70-
{
71-
"Id": "m1",
72-
"Expression": f'SELECT MAX("{metric_name}") from SCHEMA("{namespace}",service) \
70+
) -> "MetricDataResultTypeDef":
71+
cw_client = cw_client or boto3.client("cloudwatch")
72+
metric_query = {
73+
"Id": "m1",
74+
"Expression": f'SELECT MAX("{metric_name}") from SCHEMA("{namespace}",service) \
7375
where service=\'{service_name}\'',
74-
"ReturnData": True,
75-
"Period": 600,
76-
},
77-
],
76+
"ReturnData": True,
77+
"Period": 600,
78+
}
79+
response = cw_client.get_metric_data(
80+
MetricDataQueries=[metric_query],
7881
StartTime=start_date,
79-
EndTime=end_date if end_date else datetime.utcnow(),
82+
EndTime=end_date or datetime.utcnow(),
8083
)
8184
result = response["MetricDataResults"][0]
8285
if not result["Values"]:
@@ -85,7 +88,7 @@ def get_metrics(
8588

8689

8790
@retry(ValueError, delay=1, jitter=1, tries=10)
88-
def get_traces(filter_expression: str, xray_client: XRayClient, start_date: datetime, end_date: datetime) -> Dict:
91+
def get_traces(filter_expression: str, xray_client: "XRayClient", start_date: datetime, end_date: datetime) -> Dict:
8992
paginator = xray_client.get_paginator("get_trace_summaries")
9093
response_iterator = paginator.paginate(
9194
StartTime=start_date,

0 commit comments

Comments
 (0)