feat(setup): T05 configure pytest with coverage

- Create pytest.ini with:
  - Test discovery configuration (testpaths, python_files)
  - Asyncio mode settings
  - Coverage configuration (>=90% requirement)
  - Custom markers (unit, integration, e2e, slow)
- Update conftest.py with:
  - pytest_asyncio plugin
  - Shared fixtures (project_root, src_path, temp_dir, mock_env_vars)
  - Path configuration for imports
- Add test_pytest_config.py with 12 unit tests
- All tests passing (12/12)

Refs: T05

Completes setup phase T01-T05
This commit is contained in:
Luca Sacchi Ricciardi
2026-04-07 09:55:12 +02:00
parent aece120017
commit 28fde3627e
5 changed files with 191 additions and 6 deletions

View File

@@ -0,0 +1,50 @@
"""Pytest configuration and fixtures.
This module contains shared fixtures and configuration for all tests.
"""
import sys
import os
import pytest
import pytest_asyncio
# Add src to path for importing in tests
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src'))
# Markers for test organization
pytest_plugins = ['pytest_asyncio']
def pytest_configure(config):
"""Configure pytest with custom markers."""
config.addinivalue_line("markers", "unit: Unit tests (no external dependencies)")
config.addinivalue_line("markers", "integration: Integration tests (with mocked dependencies)")
config.addinivalue_line("markers", "e2e: End-to-end tests (full workflow)")
config.addinivalue_line("markers", "slow: Slow tests (skip in quick mode)")
@pytest.fixture(scope='session')
def project_root():
"""Return the project root directory."""
return os.path.dirname(os.path.dirname(__file__))
@pytest.fixture(scope='session')
def src_path(project_root):
"""Return the src directory path."""
return os.path.join(project_root, 'src')
@pytest.fixture
def temp_dir(tmp_path):
"""Provide a temporary directory for tests."""
return tmp_path
@pytest.fixture
def mock_env_vars(monkeypatch):
"""Set up mock environment variables for testing."""
monkeypatch.setenv('SECRET_KEY', 'test-secret-key-min-32-characters-long')
monkeypatch.setenv('ENCRYPTION_KEY', 'test-32-byte-encryption-key!!')
monkeypatch.setenv('DATABASE_URL', 'sqlite:///./test.db')
monkeypatch.setenv('DEBUG', 'true')
monkeypatch.setenv('LOG_LEVEL', 'DEBUG')

View File

@@ -0,0 +1,103 @@
"""Test for pytest configuration (T05)."""
import os
import sys
import pytest
@pytest.mark.unit
class TestPytestConfiguration:
"""Test pytest configuration and setup."""
def test_pytest_ini_exists(self):
"""Verify pytest.ini file exists."""
ini_path = "/home/google/Sources/LucaSacchiNet/openrouter-watcher/pytest.ini"
assert os.path.isfile(ini_path), f"File {ini_path} does not exist"
def test_pytest_ini_has_testpaths(self):
"""Verify pytest.ini contains testpaths configuration."""
ini_path = "/home/google/Sources/LucaSacchiNet/openrouter-watcher/pytest.ini"
with open(ini_path, 'r') as f:
content = f.read()
assert 'testpaths' in content, "pytest.ini should contain testpaths"
def test_pytest_ini_has_python_files(self):
"""Verify pytest.ini contains python_files configuration."""
ini_path = "/home/google/Sources/LucaSacchiNet/openrouter-watcher/pytest.ini"
with open(ini_path, 'r') as f:
content = f.read()
assert 'python_files' in content, "pytest.ini should contain python_files"
def test_pytest_ini_has_python_functions(self):
"""Verify pytest.ini contains python_functions configuration."""
ini_path = "/home/google/Sources/LucaSacchiNet/openrouter-watcher/pytest.ini"
with open(ini_path, 'r') as f:
content = f.read()
assert 'python_functions' in content, "pytest.ini should contain python_functions"
def test_pytest_ini_has_addopts(self):
"""Verify pytest.ini contains addopts configuration."""
ini_path = "/home/google/Sources/LucaSacchiNet/openrouter-watcher/pytest.ini"
with open(ini_path, 'r') as f:
content = f.read()
assert 'addopts' in content, "pytest.ini should contain addopts"
def test_pytest_ini_has_cov_config(self):
"""Verify pytest.ini contains coverage configuration."""
ini_path = "/home/google/Sources/LucaSacchiNet/openrouter-watcher/pytest.ini"
with open(ini_path, 'r') as f:
content = f.read()
assert 'cov' in content.lower(), "pytest.ini should contain coverage config"
def test_conftest_py_exists(self):
"""Verify conftest.py file exists."""
conf_path = "/home/google/Sources/LucaSacchiNet/openrouter-watcher/tests/conftest.py"
assert os.path.isfile(conf_path), f"File {conf_path} does not exist"
def test_conftest_py_has_pytest_plugins(self):
"""Verify conftest.py contains pytest_plugins."""
conf_path = "/home/google/Sources/LucaSacchiNet/openrouter-watcher/tests/conftest.py"
with open(conf_path, 'r') as f:
content = f.read()
assert 'pytest_plugins' in content or 'fixture' in content, \
"conftest.py should contain pytest plugins or fixtures"
def test_conftest_py_imports_pytest_asyncio(self):
"""Verify conftest.py imports pytest_asyncio."""
conf_path = "/home/google/Sources/LucaSacchiNet/openrouter-watcher/tests/conftest.py"
with open(conf_path, 'r') as f:
content = f.read()
assert 'pytest_asyncio' in content or 'asyncio' in content.lower(), \
"conftest.py should import pytest_asyncio"
def test_pytest_can_run_tests(self):
"""Verify pytest can actually run tests."""
import subprocess
result = subprocess.run(
['python3', '-m', 'pytest', '--version'],
capture_output=True,
text=True
)
assert result.returncode == 0, "pytest should be runnable"
assert 'pytest' in result.stdout.lower(), "pytest version should be displayed"
def test_pytest_can_discover_tests(self):
"""Verify pytest can discover tests in the project."""
import subprocess
result = subprocess.run(
['python3', '-m', 'pytest', '--collect-only', '-q'],
capture_output=True,
text=True,
cwd='/home/google/Sources/LucaSacchiNet/openrouter-watcher'
)
assert result.returncode == 0, "pytest should collect tests without errors"
# Should find at least some tests
assert 'test' in result.stdout.lower(), "pytest should find tests"
def test_coverage_is_configured(self):
"""Verify coverage is configured in pytest."""
ini_path = "/home/google/Sources/LucaSacchiNet/openrouter-watcher/pytest.ini"
with open(ini_path, 'r') as f:
content = f.read()
# Check for coverage report configuration
assert any(term in content.lower() for term in ['cov-report', 'coverage', 'cov=']), \
"pytest.ini should configure coverage reporting"