Files
intercept/tests/test_requirements.py
Jon Ander Oribe 1ef3e367eb Add new dependencies and sync requirement files
Added 'bleak', 'flask-sock', and 'requests' to pyproject.toml and updated requirements.txt to include 'Werkzeug' and 'bleak'. Introduced tests/test_requirements.py to ensure consistency between requirements files and the installed environment.
2026-01-20 10:20:13 +01:00

109 lines
4.0 KiB
Python

import pytest
from pathlib import Path
import importlib.metadata
import tomllib # Standard in Python 3.11+
def get_root_path():
return Path(__file__).parent.parent
def _clean_string(req):
"""Normalizes a requirement string (lowercase and removes spaces)."""
return req.strip().lower().replace(" ", "")
def parse_txt_requirements(file_path):
"""Extracts full requirement strings (name + version) from a .txt file."""
if not file_path.exists():
return set()
packages = set()
with open(file_path, "r") as f:
for line in f:
line = line.strip()
# Ignore empty lines, comments, and recursive/local flags
if not line or line.startswith(("#", "-e", "git+", "-r")):
continue
packages.add(_clean_string(line))
return packages
def parse_toml_section(data, section_type="main"):
"""Extracts full requirement strings from pyproject.toml."""
packages = set()
if section_type == "main":
deps = data.get("project", {}).get("dependencies", [])
else:
# Check optional-dependencies or dependency-groups
deps = data.get("project", {}).get("optional-dependencies", {}).get("dev", [])
if not deps:
deps = data.get("dependency-groups", {}).get("dev", [])
for req in deps:
packages.add(_clean_string(req))
return packages
def test_dependency_files_integrity():
"""1. Verifies that .txt files and pyproject.toml have identical names AND versions."""
root = get_root_path()
toml_path = root / "pyproject.toml"
assert toml_path.exists(), "Missing pyproject.toml"
with open(toml_path, "rb") as f:
toml_data = tomllib.load(f)
# Validate Production Sync
txt_main = parse_txt_requirements(root / "requirements.txt")
toml_main = parse_toml_section(toml_data, "main")
assert txt_main == toml_main, (
f"Production version mismatch!\n"
f"Only in TXT: {txt_main - toml_main}\n"
f"Only in TOML: {toml_main - txt_main}"
)
# Validate Development Sync
txt_dev = parse_txt_requirements(root / "requirements-dev.txt")
toml_dev = parse_toml_section(toml_data, "dev")
assert txt_dev == toml_dev, (
f"Development version mismatch!\n"
f"Only in TXT: {txt_dev - toml_dev}\n"
f"Only in TOML: {toml_dev - txt_dev}"
)
def test_environment_vs_toml():
"""2. Verifies that installed packages satisfy TOML requirements."""
root = get_root_path()
with open(root / "pyproject.toml", "rb") as f:
data = tomllib.load(f)
all_declared = parse_toml_section(data, "main") | parse_toml_section(data, "dev")
_verify_installation(all_declared, "TOML")
def test_environment_vs_requirements():
"""3. Verifies that installed packages satisfy .txt requirements."""
root = get_root_path()
all_txt_deps = (
parse_txt_requirements(root / "requirements.txt") |
parse_txt_requirements(root / "requirements-dev.txt")
)
_verify_installation(all_txt_deps, "requirements.txt")
def _verify_installation(package_set, source_name):
"""Helper to check if declared versions match installed versions."""
missing_or_wrong = []
for req in package_set:
# Split name from version to check installation status
# handles ==, >=, ~=, <=, > , <
import re
parts = re.split(r'==|>=|~=|<=|>|<', req)
name = parts[0].strip()
try:
installed_ver = importlib.metadata.version(name)
# If the config uses exact versioning '==', we can do a strict check
if "==" in req:
expected_ver = req.split("==")[1].strip()
if installed_ver != expected_ver:
missing_or_wrong.append(f"{name} (Installed: {installed_ver}, Expected: {expected_ver})")
except importlib.metadata.PackageNotFoundError:
missing_or_wrong.append(f"{name} (Not installed)")
if missing_or_wrong:
pytest.fail(f"Environment out of sync with {source_name}:\n" + "\n".join(missing_or_wrong))