Skip to content

Commit

Permalink
chore(ci): introduce tests with Nox (#4537)
Browse files Browse the repository at this point in the history
  • Loading branch information
leandrodamascena authored Jun 18, 2024
1 parent 8a754a2 commit 07ef4da
Show file tree
Hide file tree
Showing 10 changed files with 430 additions and 233 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/quality_check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ jobs:
run: make mypy
- name: Test with pytest
run: make test
- name: Test dependencies with Nox
run: make test-dependencies
- name: Security baseline
run: make security-baseline
- name: Complexity baseline
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ timeline
Pre-Pull Request <br> (make pr) : Code linting
: Docs linting
: Static typing analysis
: Tests (unit|functional|perf)
: Tests (unit|functional|perf|dependencies)
: Security baseline
: Complexity baseline
: +pre-commit checks
Expand Down
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ test:
poetry run pytest -m "not perf" --ignore tests/e2e --cov=aws_lambda_powertools --cov-report=xml
poetry run pytest --cache-clear tests/performance

test-dependencies:
poetry run nox --error-on-external-run --reuse-venv=yes --non-interactive

test-pydanticv2:
poetry run pytest -m "not perf" --ignore tests/e2e

Expand All @@ -47,7 +50,7 @@ coverage-html:
pre-commit:
pre-commit run --show-diff-on-failure

pr: lint lint-docs mypy pre-commit test security-baseline complexity-baseline
pr: lint lint-docs mypy pre-commit test test-dependencies security-baseline complexity-baseline

build: pr
poetry build
Expand Down
58 changes: 58 additions & 0 deletions noxfile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Run nox tests
#
# usage:
# poetry run nox --error-on-external-run --reuse-venv=yes --non-interactive
#
# If you want to target a specific Python version, add -p parameter

from typing import List, Optional

import nox

PREFIX_TESTS_FUNCTIONAL = "tests/functional"
PREFIX_TESTS_UNIT = "tests/unit"


def build_and_run_test(session: nox.Session, folders: List, extras: Optional[str] = "") -> None:
"""
This function is responsible for setting up the testing environment and running the test suite for specific feature.
The function performs the following tasks:
1. Installs the required dependencies for executing any test
2. If the `extras` parameter is provided, the function installs the additional dependencies
3. the function runs the pytest command with the specified folders as arguments, executing the test suite.
Parameters
----------
session: nox.Session
The current Nox session object, which is used to manage the virtual environment and execute commands.
folders: List
A list of folder paths that contain the test files to be executed.
extras: Optional[str]
A string representing additional dependencies that should be installed for the test environment.
If not provided, the function will install the project with basic dependencies
"""

# Required install to execute any test
session.install("poetry", "pytest", "pytest-mock", "pytest_socket")

# Powertools project folder is in the root
if extras:
session.install(f"./[{extras}]")
else:
session.install("./")

# Execute test in specific folders
session.run("pytest", *folders)


@nox.session()
def test_with_only_required_packages(session: nox.Session):
"""Tests that only depends for required libraries"""
# Logger
build_and_run_test(
session,
folders=[
f"{PREFIX_TESTS_FUNCTIONAL}/logger/",
],
)
418 changes: 250 additions & 168 deletions poetry.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ pytest-socket = ">=0.6,<0.8"
types-redis = "^4.6.0.7"
testcontainers = { extras = ["redis"], version = "^3.7.1" }
multiprocess = "^0.70.16"
nox = "^2024.4.15"

[tool.coverage.run]
source = ["aws_lambda_powertools"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,19 @@
import secrets
import string
import sys
import warnings
from collections import namedtuple
from datetime import datetime, timezone
from typing import Any, Callable, Dict, Iterable, List, Optional, Union

import pytest

from aws_lambda_powertools import Logger, Tracer, set_package_logger_handler
from aws_lambda_powertools import Logger
from aws_lambda_powertools.logging import correlation_paths
from aws_lambda_powertools.logging.exceptions import InvalidLoggerSamplingRateError
from aws_lambda_powertools.logging.formatter import (
BasePowertoolsFormatter,
LambdaPowertoolsFormatter,
)
from aws_lambda_powertools.logging.logger import set_package_logger
from aws_lambda_powertools.shared import constants
from aws_lambda_powertools.utilities.data_classes import S3Event, event_source

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


def test_package_logger_stream(stdout):
# GIVEN package logger "aws_lambda_powertools" is explicitly set with no params
set_package_logger(stream=stdout)

# WHEN Tracer is initialized in disabled mode
Tracer(disabled=True)

# THEN Tracer debug log statement should be logged
output = stdout.getvalue()
logger = logging.getLogger("aws_lambda_powertools")
assert "Tracing has been disabled" in output
assert logger.level == logging.DEBUG


def test_package_logger_format(capsys):
# GIVEN package logger "aws_lambda_powertools" is explicitly
# with a custom formatter
formatter = logging.Formatter("message=%(message)s")
set_package_logger(formatter=formatter)

# WHEN Tracer is initialized in disabled mode
Tracer(disabled=True)

# THEN Tracer debug log statement should be logged using `message=` format
output = capsys.readouterr().out
logger = logging.getLogger("aws_lambda_powertools")
assert "message=" in output
assert logger.level == logging.DEBUG


def test_logger_append_duplicated(stdout, service_name):
# GIVEN Logger is initialized with request_id field
logger = Logger(service=service_name, stream=stdout, request_id="value")
Expand Down Expand Up @@ -971,36 +939,6 @@ def handler(event, context, planet, str_end="."):
assert log["message"] == "Hello World!"


def test_set_package_logger_handler_with_powertools_debug_env_var(stdout, monkeypatch: pytest.MonkeyPatch):
# GIVEN POWERTOOLS_DEBUG is set
monkeypatch.setenv(constants.POWERTOOLS_DEBUG_ENV, "1")
logger = logging.getLogger("aws_lambda_powertools")

# WHEN set_package_logger is used at initialization
# and any Powertools for AWS Lambda (Python) operation is used (e.g., Tracer)
set_package_logger_handler(stream=stdout)
Tracer(disabled=True)

# THEN Tracer debug log statement should be logged
output = stdout.getvalue()
assert "Tracing has been disabled" in output
assert logger.level == logging.DEBUG


def test_powertools_debug_env_var_warning(monkeypatch: pytest.MonkeyPatch):
# GIVEN POWERTOOLS_DEBUG is set
monkeypatch.setenv(constants.POWERTOOLS_DEBUG_ENV, "1")
warning_message = "POWERTOOLS_DEBUG environment variable is enabled. Setting logging level to DEBUG."

# WHEN set_package_logger is used at initialization
# THEN a warning should be emitted
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("default")
set_package_logger_handler()
assert len(w) == 1
assert str(w[0].message) == warning_message


def test_logger_log_uncaught_exceptions(service_name, stdout):
# GIVEN an initialized Logger is set with log_uncaught_exceptions
logger = Logger(service=service_name, stream=stdout, log_uncaught_exceptions=True)
Expand Down
File renamed without changes.
113 changes: 113 additions & 0 deletions tests/functional/logger/test_logger_with_package_logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import io
import json
import logging
import random
import string
import warnings
from collections import namedtuple

import pytest

from aws_lambda_powertools import Metrics, set_package_logger_handler
from aws_lambda_powertools.logging.logger import set_package_logger
from aws_lambda_powertools.shared import constants


@pytest.fixture
def stdout():
return io.StringIO()


@pytest.fixture
def lambda_context():
lambda_context = {
"function_name": "test",
"memory_limit_in_mb": 128,
"invoked_function_arn": "arn:aws:lambda:eu-west-1:809313241:function:test",
"aws_request_id": "52fdfc07-2182-154f-163f-5f0f9a621d72",
}

return namedtuple("LambdaContext", lambda_context.keys())(*lambda_context.values())


@pytest.fixture
def lambda_event():
return {"greeting": "hello"}


@pytest.fixture
def service_name():
chars = string.ascii_letters + string.digits
return "".join(random.SystemRandom().choice(chars) for _ in range(15))


def capture_logging_output(stdout):
return json.loads(stdout.getvalue().strip())


def capture_multiple_logging_statements_output(stdout):
return [json.loads(line.strip()) for line in stdout.getvalue().split("\n") if line]


def test_package_logger_stream(stdout):
# GIVEN package logger "aws_lambda_powertools" is explicitly set with no params
set_package_logger(stream=stdout)

# WHEN we add a dimension in Metrics feature
my_metrics = Metrics(namespace="powertools")
my_metrics.add_dimension(name="dimension", value="test")

# THEN Metrics debug log statement should be logged
output = stdout.getvalue()
logger = logging.getLogger("aws_lambda_powertools")
assert "Adding dimension:" in output
assert logger.level == logging.DEBUG


def test_package_logger_format(capsys):
# GIVEN package logger "aws_lambda_powertools" is explicitly
# with a custom formatter
formatter = logging.Formatter("message=%(message)s")
set_package_logger(formatter=formatter)

# WHEN we add a dimension in Metrics feature
my_metrics = Metrics(namespace="powertools")
my_metrics.add_dimension(name="dimension", value="test")

# THEN Metrics debug log statement should be logged using `message=` format
output = capsys.readouterr().out
logger = logging.getLogger("aws_lambda_powertools")
assert "message=" in output
assert logger.level == logging.DEBUG


def test_set_package_logger_handler_with_powertools_debug_env_var(stdout, monkeypatch: pytest.MonkeyPatch):
# GIVEN POWERTOOLS_DEBUG is set
monkeypatch.setenv(constants.POWERTOOLS_DEBUG_ENV, "1")
logger = logging.getLogger("aws_lambda_powertools")

# WHEN set_package_logger is used at initialization
# and any Powertools for AWS Lambda (Python) operation is used (e.g., Metrics add_dimension)
set_package_logger_handler(stream=stdout)

my_metrics = Metrics(namespace="powertools")
my_metrics.add_dimension(name="dimension", value="test")

# THEN Metrics debug log statement should be logged
output = stdout.getvalue()
assert "Adding dimension:" in output
assert logger.level == logging.DEBUG


def test_powertools_debug_env_var_warning(monkeypatch: pytest.MonkeyPatch):
# GIVEN POWERTOOLS_DEBUG is set
monkeypatch.setenv(constants.POWERTOOLS_DEBUG_ENV, "1")
warning_message = "POWERTOOLS_DEBUG environment variable is enabled. Setting logging level to DEBUG."

# WHEN set_package_logger is used at initialization
# THEN a warning should be emitted
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter("default")
set_package_logger_handler()
assert len(w) == 1
assert str(w[0].message) == warning_message

0 comments on commit 07ef4da

Please sign in to comment.