Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Continuous Integration

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
workflow_dispatch:

jobs:
test-reporting:
name: Test & Report
runs-on: ubuntu-latest

env:
SPOTIFY_API_BASE_URL: ${{ vars.SPOTIFY_API_BASE_URL }}
SPOTIFY_CLIENT_ID: ${{ vars.SPOTIFY_CLIENT_ID }}
SPOTIFY_CLIENT_SECRET: ${{ secrets.SPOTIFY_CLIENT_SECRET }}
steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Cache Poetry
uses: actions/cache@v3
with:
path: ~/.cache/pypoetry
key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }}
restore-keys: |
${{ runner.os }}-poetry-

- name: Set up Java (Required for Allure CLI)
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '11'

- name: Install Allure CLI
run: |
set -eux
ALLURE_VERSION=2.28.0
curl -Lo allure-commandline.zip \
https://repo.maven.apache.org/maven2/io/qameta/allure/allure-commandline/${ALLURE_VERSION}/allure-commandline-${ALLURE_VERSION}.zip
unzip -o allure-commandline.zip -d allure-cli
echo "${PWD}/allure-cli/allure-${ALLURE_VERSION}/bin" >> $GITHUB_PATH

- name: Setup Python 3.13
uses: actions/setup-python@v4
with:
python-version: '3.13'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install poetry
make install

- name: Run tests and generate Allure report
run: make all

- name: Upload Allure Report artifact
uses: actions/upload-artifact@v4
with:
name: allure-report
path: ./allure-report
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ __pycache__/
.pytest_cache/
.env
.vscode/settings.json

# Ignore Allure results and reports
allure-results/
allure-report/
31 changes: 31 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
.PHONY: install lint test report serve-report clean all

# Install project dependencies without installing the root package
install:
poetry install

# Run code style and lint checks
lint:
black --check src tests
isort --check-only src tests
flake8 src tests

# Run unit tests
test:
poetry run pytest

# Generate Allure results and HTML report
report:
poetry run pytest --alluredir=allure-results
allure generate allure-results --clean -o allure-report

# Serve the Allure report interactively
serve-report:
python scripts/allure_helper.py --serve

# Clean test artifacts and reports
clean:
rm -rf .pytest_cache/ allure-results/ allure-report/

# Full flow: clean state, install, lint, test, and report
all: clean install test report
54 changes: 53 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ authors = [
license = {text = "MIT"}
readme = "README.md"
requires-python = ">=3.13"
package-mode = false
dependencies = [
"httpx (>=0.28.1,<0.29.0)",
"python-dotenv (>=1.1.0,<2.0.0)",
"pydantic (>=2.11.5,<3.0.0)",
"pydantic-settings (>=2.9.1,<3.0.0)"
]


[tool.poetry]
packages = [{ include="api_testing_framework", from="src" }]
[build-system]
requires = ["poetry-core>=2.0.0,<3.0.0"]
build-backend = "poetry.core.masonry.api"
Expand All @@ -28,4 +28,5 @@ requests-mock = "^1.12.1"
black = "^25.1.0"
isort = "^6.0.1"
flake8 = "^7.2.0"
allure-pytest = "^2.14.2"

1 change: 1 addition & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[pytest]
addopts = --alluredir=allure-results
markers =
integration: mark test as an integration test (vs. unit)
Empty file added scripts/__init__.py
Empty file.
90 changes: 90 additions & 0 deletions scripts/allure_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#!/usr/bin/env python3
"""Script to clean, generate, and optionally serve an Allure report with parameterized paths"""
import argparse
import os
import shutil
import subprocess
import sys

from api_testing_framework.logger import logger


def clean_allure_report(report_path: str) -> None:
"""Clean the existing Allure report directory"""
if os.path.isdir(report_path):
logger.info("Cleaning old Allure report at: %s", report_path)
shutil.rmtree(report_path)


def generate_allure_report(results_dir: str, report_dir: str) -> None:
"""Generate the Allure report from results"""
logger.info("Generating Allure report from %s to %s...", results_dir, report_dir)
try:

subprocess.run(
["allure", "generate", results_dir, "--clean", "-o", report_dir],
check=True,
capture_output=True,
)
logger.info("Report generated at: %s/index.html", report_dir)
except subprocess.CalledProcessError as e:
stderr = e.stderr.decode().strip() if e.stderr else str(e)
logger.error("Allure generation failed: %s", stderr)
raise


def serve_allure_report(results_dir: str) -> None:
"""Serve the Allure report interactively"""
logger.info("Serving Allure report from results: %s...", results_dir)
try:
subprocess.run(
["allure", "serve", results_dir], check=True, capture_output=True
)
except subprocess.CalledProcessError as e:
stderr = e.stderr.decode().strip() if e.stderr else str(e)
print("Allure serving failed: %s", stderr)


def main():
project_root = os.path.dirname(os.path.abspath(__file__))
parser = argparse.ArgumentParser(
description="Clean, generate, and serve Allure reports."
)
parser.add_argument(
"--results-dir",
default=os.path.join(project_root, "allure-results"),
help="Directory containing pytest --alluredir output",
)
parser.add_argument(
"--report-dir",
default=os.path.join(project_root, "allure-report"),
help="Directory where the HTML report will be generated",
)
parser.add_argument(
"--serve",
action="store_true",
help="After generation, serve the report in a local web server",
)
args = parser.parse_args()

# Validate results dictionary
if not os.path.isdir(args.results_dir) or not os.listdir(args.results_dir):
logger.warning(
"No Allure results found in %s. Run pytest with --alluredir first.",
args.results_dir,
)
sys.exit(0)

# Clean, generate, and optionally serve
clean_allure_report(args.report_dir)
try:
generate_allure_report(args.results_dir, args.report_dir)
if args.serve:
serve_allure_report(args.results_dir)
except subprocess.CalledProcessError as e:
logger.error("Error during Allure operation: %s", str(e))
sys.exit(1)


if __name__ == "__main__":
main()
12 changes: 12 additions & 0 deletions src/api_testing_framework/logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# src/api_testing_framework/logger.py
import logging

# Configure the root logger for the project
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)

# Expose a pre-configured logger for convenience
logger = logging.getLogger("api_testing_framework")
2 changes: 1 addition & 1 deletion tests/spotify/test_integration_spotify.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pytest

from src.api_testing_framework.spotify.client import SpotifyClient
from api_testing_framework.spotify.client import SpotifyClient


@pytest.mark.integration
Expand Down