#!/usr/bin/env python3 """Deployment invariant tests (D001). Proves that running a scan never creates or modifies git-tracked runtime data, which is what makes ``git pull`` conflict-free on a deployed server. Run with: python -m unittest discover -s tests -p 'test_*.py' or (if pytest is installed): python -m pytest tests/ """ from __future__ import annotations import os import subprocess import sys import unittest ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # The paloalto scanner-ips.txt seed is deliberately tracked (read-only); every # other file under the runtime dirs must remain untracked. ALLOWED_TRACKED_RUNTIME = {os.path.join("app", "data", "paloalto", "scanner-ips.txt")} def _git(*args: str) -> str: return subprocess.run(["git", *args], cwd=ROOT, check=True, capture_output=True, text=True).stdout class RuntimeGitTest(unittest.TestCase): def test_no_runtime_data_tracked(self): """No runtime file (other than the read-only paloalto seed) is tracked.""" tracked = _git("ls-files", "app/data", "app/reports").splitlines() unexpected = [f for f in tracked if f not in ALLOWED_TRACKED_RUNTIME] self.assertEqual( unexpected, [], "git-tracked runtime files would make `git pull` conflict; " "add them to .gitignore: %s" % unexpected) def test_runtime_write_paths_ignored(self): """Every runtime write path is ignored by git (won't dirty the tree).""" stats = subprocess.run( ["git", "check-ignore", "-v", "app/data/scan_state.json", "app/data/scan_jobs.json", "app/data/whois_cache.json", "app/data/ids.db", "app/data/ids.db-wal", "app/reports/app/data/blocklist_ipv4.txt", ], cwd=ROOT, capture_output=True, text=True).stdout self.assertIn("scan_state.json", stats) self.assertIn("ids.db", stats) self.assertIn("blocklist_ipv4.txt", stats) if __name__ == "__main__": unittest.main()