from pathlib import Path ROOT = Path(__file__).resolve().parents[1] def _strip_inline_comment(line: str) -> str: return line.split("#", 1)[0].strip() def _requirement_name(line: str) -> str: line = _strip_inline_comment(line) for operator in ("==", ">=", "<=", "~=", "!=", ">"): if operator in line: return line.split(operator, 1)[0].strip().lower() return line.lower() def _requirement_names() -> set[str]: names = set() for raw_line in (ROOT / "requirements.txt").read_text(encoding="utf-8").splitlines(): line = _strip_inline_comment(raw_line) if not line or line.startswith(("-", "--")): continue names.add(_requirement_name(line)) return names def test_requirements_have_version_specifiers(): unpinned = [] for raw_line in (ROOT / "requirements.txt").read_text(encoding="utf-8").splitlines(): line = _strip_inline_comment(raw_line) if not line: continue if line.startswith(("-", "--")): continue if "@" in line: continue if not any(op in line for op in ("==", ">=", "<=", "~=", "!=", ">")): unpinned.append(line) assert unpinned == [] def test_audit_flagged_runtime_dependencies_have_import_evidence(): requirements = _requirement_names() runtime_evidence = { "beautifulsoup4": [ ("services/cosme_crawler.py", "from bs4 import BeautifulSoup"), ("services/momo_crawler.py", "from bs4 import BeautifulSoup"), ("services/trend_crawler.py", "from bs4 import BeautifulSoup"), ], "google-api-python-client": [ ("services/google_drive_service.py", "from googleapiclient.discovery import build"), ], "google-generativeai": [ ("services/gemini_service.py", "import google.generativeai as genai"), ("services/openclaw_strategist_service.py", "import google.generativeai as genai"), ], "python-pptx": [ ("services/ppt_generator.py", "from pptx import Presentation"), ], "matplotlib": [ ("routes/openclaw_bot_routes.py", "import matplotlib"), ("services/chart_generator_service.py", "import matplotlib"), ("services/ppt_generator.py", "import matplotlib"), ], } missing_packages = sorted(set(runtime_evidence) - requirements) assert missing_packages == [] missing_evidence = [] for package, locations in runtime_evidence.items(): if not any(pattern in (ROOT / path).read_text(encoding="utf-8") for path, pattern in locations): missing_evidence.append(package) assert missing_evidence == [] def test_cli_ssh_helper_replaces_legacy_paramiko_dependency(): requirements = _requirement_names() helper_source = (ROOT / "utils" / "ssh_helper.py").read_text(encoding="utf-8") assert "paramiko" not in requirements assert "subprocess.run" in helper_source assert '"ssh"' in helper_source assert "paramiko" not in helper_source def test_pgvector_extension_uses_local_sqlalchemy_type_not_python_package(): requirements = _requirement_names() ai_models_source = (ROOT / "database" / "ai_models.py").read_text(encoding="utf-8") migration_source = (ROOT / "migrations" / "009_pgvector_embedding.sql").read_text(encoding="utf-8") assert "pgvector" not in requirements assert "matplotlib-inline" not in requirements assert "class Vector(UserDefinedType)" in ai_models_source assert "CREATE EXTENSION IF NOT EXISTS vector" in migration_source