mirror of
https://github.com/smittix/intercept.git
synced 2026-04-24 14:50:00 -07:00
Introduces an 'optionals' dependency group in pyproject.toml. There was a discrepancy because they had been added to requirements.txt at some point during the last few commits but not to .toml. Update on test_requirements.py to include and validate these optional dependencies. Enhances test logic to ensure all main, dev, and optional dependencies are checked for environment consistency.
117 lines
4.2 KiB
Python
117 lines
4.2 KiB
Python
import pytest
|
|
from pathlib import Path
|
|
import importlib.metadata
|
|
import tomllib
|
|
import re
|
|
|
|
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()
|
|
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 including optional sections."""
|
|
packages = set()
|
|
project = data.get("project", {})
|
|
|
|
if section_type == "main":
|
|
deps = project.get("dependencies", [])
|
|
elif section_type == "optional":
|
|
deps = project.get("optional-dependencies", {}).get("optionals", [])
|
|
elif section_type == "dev":
|
|
deps = 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 (Main + Optionals)
|
|
txt_main = parse_txt_requirements(root / "requirements.txt")
|
|
toml_main = parse_toml_section(toml_data, "main") | parse_toml_section(toml_data, "optional")
|
|
|
|
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, "optional") |
|
|
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
|
|
parts = re.split(r'==|>=|~=|<=|>|<', req)
|
|
raw_name = parts[0].strip()
|
|
|
|
# CLEAN EXTRAS: "qrcode[pil]" -> "qrcode"
|
|
clean_name = re.sub(r'\[.*\]', '', raw_name)
|
|
|
|
try:
|
|
installed_ver = importlib.metadata.version(clean_name)
|
|
if "==" in req:
|
|
expected_ver = req.split("==")[1].strip()
|
|
if installed_ver != expected_ver:
|
|
missing_or_wrong.append(f"{clean_name} (Installed: {installed_ver}, Expected: {expected_ver})")
|
|
except importlib.metadata.PackageNotFoundError:
|
|
missing_or_wrong.append(f"{clean_name} (Not installed)")
|
|
|
|
if missing_or_wrong:
|
|
pytest.fail(f"Environment out of sync with {source_name}:\n" + "\n".join(missing_or_wrong)) |