import hashlib import json import os import sqlite3 import subprocess import sys import threading import unittest from http.client import HTTPConnection from pathlib import Path from tempfile import TemporaryDirectory from app.main import create_server from app.config import ConfigError, load_config ROOT = Path(__file__).resolve().parents[3] SCRIPTS = ROOT / "scripts" class Phase15OpsTests(unittest.TestCase): def test_config_defaults_are_safe_and_production_requires_explicit_secret(self): with TemporaryDirectory() as tmp: cfg = load_config({"DATA_DIR": tmp}) self.assertFalse(cfg.outreach_enabled) self.assertEqual(cfg.app_env, "development") with self.assertRaises(ConfigError): load_config({"APP_ENV": "production", "DATA_DIR": tmp}) cfg = load_config({"APP_ENV": "production", "DATA_DIR": tmp, "SESSION_SECRET": "a" * 32}) self.assertEqual(cfg.session_secret, "a" * 32) def test_health_and_readiness_report_database_state(self): with TemporaryDirectory() as tmp: db_path = str(Path(tmp) / "state.db") server = create_server("127.0.0.1", 0, db_path) thread = threading.Thread(target=server.serve_forever, daemon=True) thread.start() try: conn = HTTPConnection("127.0.0.1", server.server_port, timeout=3) for path in ("/api/v1/health/live", "/api/v1/health/ready"): conn.request("GET", path) response = conn.getresponse() payload = json.loads(response.read()) self.assertEqual(response.status, 200) self.assertEqual(payload["status"], "ok") self.assertFalse(payload["outreach_enabled"]) finally: server.shutdown(); server.server_close(); thread.join(timeout=2) def test_web_healthcheck_uses_image_runtime(self): compose = (ROOT / "docker-compose.yml").read_text() self.assertIn("python", compose) self.assertIn("urllib.request.urlopen('http://127.0.0.1:8080/healthz'", compose) self.assertNotIn('test: ["CMD", "wget", "--spider"', compose) def test_backup_restore_integrity_and_checksum(self): with TemporaryDirectory() as tmp: root = Path(tmp); db = root / "prospects.db"; backups = root / "backups" conn = sqlite3.connect(db); conn.execute("CREATE TABLE facts (value TEXT)"); conn.execute("INSERT INTO facts VALUES ('before')"); conn.commit(); conn.close() result = subprocess.run([str(SCRIPTS / "backup_sqlite.sh"), str(db), str(backups), "2"], text=True, capture_output=True, check=True) backup = Path(result.stdout.strip().splitlines()[-1]) self.assertTrue(backup.exists()); self.assertTrue(Path(str(backup) + ".sha256").exists()) self.assertEqual(hashlib.sha256(backup.read_bytes()).hexdigest(), Path(str(backup) + ".sha256").read_text().split()[0]) conn = sqlite3.connect(db); conn.execute("UPDATE facts SET value='after'"); conn.commit(); conn.close() subprocess.run([str(SCRIPTS / "restore_sqlite.sh"), str(backup), str(db), "--confirm-restore"], text=True, capture_output=True, check=True) conn = sqlite3.connect(db); self.assertEqual(conn.execute("SELECT value FROM facts").fetchone()[0], "before"); conn.close() self.assertTrue(any(p.name.startswith("pre-restore-") for p in backups.glob("*.db"))) def test_backup_output_does_not_contain_secret_values(self): with TemporaryDirectory() as tmp: root = Path(tmp); db = root / "prospects.db"; backups = root / "backups" conn = sqlite3.connect(db); conn.execute("CREATE TABLE logs (message TEXT)"); conn.execute("INSERT INTO logs VALUES ('password=should-not-be-logged')"); conn.commit(); conn.close() result = subprocess.run([str(SCRIPTS / "backup_sqlite.sh"), str(db), str(backups), "1"], text=True, capture_output=True, check=True) combined = result.stdout + result.stderr self.assertNotIn("should-not-be-logged", combined) self.assertNotIn("password=", combined) if __name__ == "__main__": unittest.main()