Skip to content

Commit 07ef4da

Browse files
chore(ci): introduce tests with Nox (aws-powertools#4537)
1 parent 8a754a2 commit 07ef4da

10 files changed

+430
-233
lines changed

.github/workflows/quality_check.yml

+2
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ jobs:
6868
run: make mypy
6969
- name: Test with pytest
7070
run: make test
71+
- name: Test dependencies with Nox
72+
run: make test-dependencies
7173
- name: Security baseline
7274
run: make security-baseline
7375
- name: Complexity baseline

CONTRIBUTING.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ timeline
6767
Pre-Pull Request <br> (make pr) : Code linting
6868
: Docs linting
6969
: Static typing analysis
70-
: Tests (unit|functional|perf)
70+
: Tests (unit|functional|perf|dependencies)
7171
: Security baseline
7272
: Complexity baseline
7373
: +pre-commit checks

Makefile

+4-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ test:
3232
poetry run pytest -m "not perf" --ignore tests/e2e --cov=aws_lambda_powertools --cov-report=xml
3333
poetry run pytest --cache-clear tests/performance
3434

35+
test-dependencies:
36+
poetry run nox --error-on-external-run --reuse-venv=yes --non-interactive
37+
3538
test-pydanticv2:
3639
poetry run pytest -m "not perf" --ignore tests/e2e
3740

@@ -47,7 +50,7 @@ coverage-html:
4750
pre-commit:
4851
pre-commit run --show-diff-on-failure
4952

50-
pr: lint lint-docs mypy pre-commit test security-baseline complexity-baseline
53+
pr: lint lint-docs mypy pre-commit test test-dependencies security-baseline complexity-baseline
5154

5255
build: pr
5356
poetry build

noxfile.py

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Run nox tests
2+
#
3+
# usage:
4+
# poetry run nox --error-on-external-run --reuse-venv=yes --non-interactive
5+
#
6+
# If you want to target a specific Python version, add -p parameter
7+
8+
from typing import List, Optional
9+
10+
import nox
11+
12+
PREFIX_TESTS_FUNCTIONAL = "tests/functional"
13+
PREFIX_TESTS_UNIT = "tests/unit"
14+
15+
16+
def build_and_run_test(session: nox.Session, folders: List, extras: Optional[str] = "") -> None:
17+
"""
18+
This function is responsible for setting up the testing environment and running the test suite for specific feature.
19+
20+
The function performs the following tasks:
21+
1. Installs the required dependencies for executing any test
22+
2. If the `extras` parameter is provided, the function installs the additional dependencies
23+
3. the function runs the pytest command with the specified folders as arguments, executing the test suite.
24+
25+
Parameters
26+
----------
27+
session: nox.Session
28+
The current Nox session object, which is used to manage the virtual environment and execute commands.
29+
folders: List
30+
A list of folder paths that contain the test files to be executed.
31+
extras: Optional[str]
32+
A string representing additional dependencies that should be installed for the test environment.
33+
If not provided, the function will install the project with basic dependencies
34+
"""
35+
36+
# Required install to execute any test
37+
session.install("poetry", "pytest", "pytest-mock", "pytest_socket")
38+
39+
# Powertools project folder is in the root
40+
if extras:
41+
session.install(f"./[{extras}]")
42+
else:
43+
session.install("./")
44+
45+
# Execute test in specific folders
46+
session.run("pytest", *folders)
47+
48+
49+
@nox.session()
50+
def test_with_only_required_packages(session: nox.Session):
51+
"""Tests that only depends for required libraries"""
52+
# Logger
53+
build_and_run_test(
54+
session,
55+
folders=[
56+
f"{PREFIX_TESTS_FUNCTIONAL}/logger/",
57+
],
58+
)

poetry.lock

+250-168
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

+1
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ pytest-socket = ">=0.6,<0.8"
126126
types-redis = "^4.6.0.7"
127127
testcontainers = { extras = ["redis"], version = "^3.7.1" }
128128
multiprocess = "^0.70.16"
129+
nox = "^2024.4.15"
129130

130131
[tool.coverage.run]
131132
source = ["aws_lambda_powertools"]

tests/functional/test_logger.py renamed to tests/functional/logger/test_logger.py

+1-63
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,19 @@
88
import secrets
99
import string
1010
import sys
11-
import warnings
1211
from collections import namedtuple
1312
from datetime import datetime, timezone
1413
from typing import Any, Callable, Dict, Iterable, List, Optional, Union
1514

1615
import pytest
1716

18-
from aws_lambda_powertools import Logger, Tracer, set_package_logger_handler
17+
from aws_lambda_powertools import Logger
1918
from aws_lambda_powertools.logging import correlation_paths
2019
from aws_lambda_powertools.logging.exceptions import InvalidLoggerSamplingRateError
2120
from aws_lambda_powertools.logging.formatter import (
2221
BasePowertoolsFormatter,
2322
LambdaPowertoolsFormatter,
2423
)
25-
from aws_lambda_powertools.logging.logger import set_package_logger
2624
from aws_lambda_powertools.shared import constants
2725
from aws_lambda_powertools.utilities.data_classes import S3Event, event_source
2826

@@ -215,36 +213,6 @@ def handler(event, context):
215213
assert second_log["cold_start"] is False
216214

217215

218-
def test_package_logger_stream(stdout):
219-
# GIVEN package logger "aws_lambda_powertools" is explicitly set with no params
220-
set_package_logger(stream=stdout)
221-
222-
# WHEN Tracer is initialized in disabled mode
223-
Tracer(disabled=True)
224-
225-
# THEN Tracer debug log statement should be logged
226-
output = stdout.getvalue()
227-
logger = logging.getLogger("aws_lambda_powertools")
228-
assert "Tracing has been disabled" in output
229-
assert logger.level == logging.DEBUG
230-
231-
232-
def test_package_logger_format(capsys):
233-
# GIVEN package logger "aws_lambda_powertools" is explicitly
234-
# with a custom formatter
235-
formatter = logging.Formatter("message=%(message)s")
236-
set_package_logger(formatter=formatter)
237-
238-
# WHEN Tracer is initialized in disabled mode
239-
Tracer(disabled=True)
240-
241-
# THEN Tracer debug log statement should be logged using `message=` format
242-
output = capsys.readouterr().out
243-
logger = logging.getLogger("aws_lambda_powertools")
244-
assert "message=" in output
245-
assert logger.level == logging.DEBUG
246-
247-
248216
def test_logger_append_duplicated(stdout, service_name):
249217
# GIVEN Logger is initialized with request_id field
250218
logger = Logger(service=service_name, stream=stdout, request_id="value")
@@ -971,36 +939,6 @@ def handler(event, context, planet, str_end="."):
971939
assert log["message"] == "Hello World!"
972940

973941

974-
def test_set_package_logger_handler_with_powertools_debug_env_var(stdout, monkeypatch: pytest.MonkeyPatch):
975-
# GIVEN POWERTOOLS_DEBUG is set
976-
monkeypatch.setenv(constants.POWERTOOLS_DEBUG_ENV, "1")
977-
logger = logging.getLogger("aws_lambda_powertools")
978-
979-
# WHEN set_package_logger is used at initialization
980-
# and any Powertools for AWS Lambda (Python) operation is used (e.g., Tracer)
981-
set_package_logger_handler(stream=stdout)
982-
Tracer(disabled=True)
983-
984-
# THEN Tracer debug log statement should be logged
985-
output = stdout.getvalue()
986-
assert "Tracing has been disabled" in output
987-
assert logger.level == logging.DEBUG
988-
989-
990-
def test_powertools_debug_env_var_warning(monkeypatch: pytest.MonkeyPatch):
991-
# GIVEN POWERTOOLS_DEBUG is set
992-
monkeypatch.setenv(constants.POWERTOOLS_DEBUG_ENV, "1")
993-
warning_message = "POWERTOOLS_DEBUG environment variable is enabled. Setting logging level to DEBUG."
994-
995-
# WHEN set_package_logger is used at initialization
996-
# THEN a warning should be emitted
997-
with warnings.catch_warnings(record=True) as w:
998-
warnings.simplefilter("default")
999-
set_package_logger_handler()
1000-
assert len(w) == 1
1001-
assert str(w[0].message) == warning_message
1002-
1003-
1004942
def test_logger_log_uncaught_exceptions(service_name, stdout):
1005943
# GIVEN an initialized Logger is set with log_uncaught_exceptions
1006944
logger = Logger(service=service_name, stream=stdout, log_uncaught_exceptions=True)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import io
2+
import json
3+
import logging
4+
import random
5+
import string
6+
import warnings
7+
from collections import namedtuple
8+
9+
import pytest
10+
11+
from aws_lambda_powertools import Metrics, set_package_logger_handler
12+
from aws_lambda_powertools.logging.logger import set_package_logger
13+
from aws_lambda_powertools.shared import constants
14+
15+
16+
@pytest.fixture
17+
def stdout():
18+
return io.StringIO()
19+
20+
21+
@pytest.fixture
22+
def lambda_context():
23+
lambda_context = {
24+
"function_name": "test",
25+
"memory_limit_in_mb": 128,
26+
"invoked_function_arn": "arn:aws:lambda:eu-west-1:809313241:function:test",
27+
"aws_request_id": "52fdfc07-2182-154f-163f-5f0f9a621d72",
28+
}
29+
30+
return namedtuple("LambdaContext", lambda_context.keys())(*lambda_context.values())
31+
32+
33+
@pytest.fixture
34+
def lambda_event():
35+
return {"greeting": "hello"}
36+
37+
38+
@pytest.fixture
39+
def service_name():
40+
chars = string.ascii_letters + string.digits
41+
return "".join(random.SystemRandom().choice(chars) for _ in range(15))
42+
43+
44+
def capture_logging_output(stdout):
45+
return json.loads(stdout.getvalue().strip())
46+
47+
48+
def capture_multiple_logging_statements_output(stdout):
49+
return [json.loads(line.strip()) for line in stdout.getvalue().split("\n") if line]
50+
51+
52+
def test_package_logger_stream(stdout):
53+
# GIVEN package logger "aws_lambda_powertools" is explicitly set with no params
54+
set_package_logger(stream=stdout)
55+
56+
# WHEN we add a dimension in Metrics feature
57+
my_metrics = Metrics(namespace="powertools")
58+
my_metrics.add_dimension(name="dimension", value="test")
59+
60+
# THEN Metrics debug log statement should be logged
61+
output = stdout.getvalue()
62+
logger = logging.getLogger("aws_lambda_powertools")
63+
assert "Adding dimension:" in output
64+
assert logger.level == logging.DEBUG
65+
66+
67+
def test_package_logger_format(capsys):
68+
# GIVEN package logger "aws_lambda_powertools" is explicitly
69+
# with a custom formatter
70+
formatter = logging.Formatter("message=%(message)s")
71+
set_package_logger(formatter=formatter)
72+
73+
# WHEN we add a dimension in Metrics feature
74+
my_metrics = Metrics(namespace="powertools")
75+
my_metrics.add_dimension(name="dimension", value="test")
76+
77+
# THEN Metrics debug log statement should be logged using `message=` format
78+
output = capsys.readouterr().out
79+
logger = logging.getLogger("aws_lambda_powertools")
80+
assert "message=" in output
81+
assert logger.level == logging.DEBUG
82+
83+
84+
def test_set_package_logger_handler_with_powertools_debug_env_var(stdout, monkeypatch: pytest.MonkeyPatch):
85+
# GIVEN POWERTOOLS_DEBUG is set
86+
monkeypatch.setenv(constants.POWERTOOLS_DEBUG_ENV, "1")
87+
logger = logging.getLogger("aws_lambda_powertools")
88+
89+
# WHEN set_package_logger is used at initialization
90+
# and any Powertools for AWS Lambda (Python) operation is used (e.g., Metrics add_dimension)
91+
set_package_logger_handler(stream=stdout)
92+
93+
my_metrics = Metrics(namespace="powertools")
94+
my_metrics.add_dimension(name="dimension", value="test")
95+
96+
# THEN Metrics debug log statement should be logged
97+
output = stdout.getvalue()
98+
assert "Adding dimension:" in output
99+
assert logger.level == logging.DEBUG
100+
101+
102+
def test_powertools_debug_env_var_warning(monkeypatch: pytest.MonkeyPatch):
103+
# GIVEN POWERTOOLS_DEBUG is set
104+
monkeypatch.setenv(constants.POWERTOOLS_DEBUG_ENV, "1")
105+
warning_message = "POWERTOOLS_DEBUG environment variable is enabled. Setting logging level to DEBUG."
106+
107+
# WHEN set_package_logger is used at initialization
108+
# THEN a warning should be emitted
109+
with warnings.catch_warnings(record=True) as w:
110+
warnings.simplefilter("default")
111+
set_package_logger_handler()
112+
assert len(w) == 1
113+
assert str(w[0].message) == warning_message

0 commit comments

Comments
 (0)