"""Tests for Architecture Linter v2. Validates that the linter correctly detects violations and passes clean repos. Refs: #437 — test-backed linter. """ import json import sys import tempfile from pathlib import Path # Add scripts/ to path sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "scripts")) from architecture_linter_v2 import Linter, LinterResult # ── helpers ─────────────────────────────────────────────────────────── def _make_repo(tmpdir: str, files: dict[str, str], name: str = "test-repo") -> Path: """Create a fake repo with given files and return its path.""" repo = Path(tmpdir) / name repo.mkdir() for relpath, content in files.items(): p = repo / relpath p.parent.mkdir(parents=True, exist_ok=True) p.write_text(content) return repo def _run(tmpdir, files, name="test-repo"): repo = _make_repo(tmpdir, files, name) return Linter(str(repo)).run() # ── clean repo passes ───────────────────────────────────────────────── def test_clean_repo_passes(): with tempfile.TemporaryDirectory() as tmp: result = _run(tmp, { "README.md": "# Test Repo\n\nThis is a clean test repo with sufficient content to pass.", "main.py": "print('hello world')\n", }) assert result.passed, f"Expected pass but got: {result.errors}" assert result.violation_count == 0 # ── missing README ──────────────────────────────────────────────────── def test_missing_readme_fails(): with tempfile.TemporaryDirectory() as tmp: result = _run(tmp, {"main.py": "x = 1\n"}) assert not result.passed assert any("README" in e for e in result.errors) def test_short_readme_warns(): with tempfile.TemporaryDirectory() as tmp: result = _run(tmp, {"README.md": "hi\n"}) # Warnings don't fail the build assert result.passed assert any("short" in w.lower() for w in result.warnings) # ── hardcoded IPs ───────────────────────────────────────────────────── def test_hardcoded_public_ip_detected(): with tempfile.TemporaryDirectory() as tmp: result = _run(tmp, { "README.md": "# R\n\nGood repo.", "server.py": "HOST = '203.0.113.42'\n", }) assert not result.passed assert any("203.0.113.42" in e for e in result.errors) def test_localhost_ip_ignored(): with tempfile.TemporaryDirectory() as tmp: result = _run(tmp, { "README.md": "# R\n\nGood repo.", "server.py": "HOST = '127.0.0.1'\n", }) ip_errors = [e for e in result.errors if "IP" in e] assert len(ip_errors) == 0 # ── API keys ────────────────────────────────────────────────────────── def test_openai_key_detected(): with tempfile.TemporaryDirectory() as tmp: result = _run(tmp, { "README.md": "# R\n\nGood repo.", "config.py": 'key = "sk-abcdefghijklmnopqrstuvwx"\n', }) assert not result.passed assert any("secret" in e.lower() or "key" in e.lower() for e in result.errors) def test_aws_key_detected(): with tempfile.TemporaryDirectory() as tmp: result = _run(tmp, { "README.md": "# R\n\nGood repo.", "deploy.yaml": 'aws_key: AKIAIOSFODNN7EXAMPLE\n', }) assert not result.passed assert any("secret" in e.lower() for e in result.errors) def test_env_example_skipped(): with tempfile.TemporaryDirectory() as tmp: result = _run(tmp, { "README.md": "# R\n\nGood repo.", ".env.example": 'OPENAI_KEY=sk-placeholder\n', }) secret_errors = [e for e in result.errors if "secret" in e.lower()] assert len(secret_errors) == 0 # ── sovereignty rules (v1 cloud API checks) ─────────────────────────── def test_openai_url_detected(): with tempfile.TemporaryDirectory() as tmp: result = _run(tmp, { "README.md": "# R\n\nGood repo.", "app.py": 'url = "https://api.openai.com/v1/chat"\n', }) assert not result.passed assert any("openai" in e.lower() for e in result.errors) def test_cloud_provider_detected(): with tempfile.TemporaryDirectory() as tmp: result = _run(tmp, { "README.md": "# R\n\nGood repo.", "config.yaml": "provider: openai\n", }) assert not result.passed assert any("provider" in e.lower() for e in result.errors) # ── sidecar boundary ────────────────────────────────────────────────── def test_sovereign_keyword_in_hermes_agent_fails(): with tempfile.TemporaryDirectory() as tmp: result = _run(tmp, { "README.md": "# R\n\nGood repo.", "index.py": "import mempalace\n", }, name="hermes-agent") assert not result.passed assert any("sidecar" in e.lower() or "mempalace" in e.lower() for e in result.errors) def test_sovereign_keyword_in_other_repo_ok(): with tempfile.TemporaryDirectory() as tmp: result = _run(tmp, { "README.md": "# R\n\nGood repo.", "index.py": "import mempalace\n", }, name="some-other-repo") sidecar_errors = [e for e in result.errors if "sidecar" in e.lower()] assert len(sidecar_errors) == 0 # ── SOUL.md canonical location ──────────────────────────────────────── def test_soul_md_required_in_timmy_config(): with tempfile.TemporaryDirectory() as tmp: result = _run(tmp, { "README.md": "# timmy-config\n\nConfig repo.", }, name="timmy-config") assert not result.passed assert any("SOUL.md" in e for e in result.errors) def test_soul_md_present_in_timmy_config_ok(): with tempfile.TemporaryDirectory() as tmp: result = _run(tmp, { "README.md": "# timmy-config\n\nConfig repo.", "SOUL.md": "# Soul\n\nCanonical identity document.", }, name="timmy-config") soul_errors = [e for e in result.errors if "SOUL" in e] assert len(soul_errors) == 0 def test_soul_md_in_wrong_repo_fails(): with tempfile.TemporaryDirectory() as tmp: result = _run(tmp, { "README.md": "# R\n\nGood repo.", "SOUL.md": "# Soul\n\nShould not be here.", }, name="other-repo") assert any("canonical" in e.lower() for e in result.errors) # ── LinterResult structure ──────────────────────────────────────────── def test_result_summary_is_string(): with tempfile.TemporaryDirectory() as tmp: result = _run(tmp, {"README.md": "# OK repo with enough text here\n"}) assert isinstance(result.summary(), str) assert "PASSED" in result.summary() or "FAILED" in result.summary() def test_result_repo_name(): with tempfile.TemporaryDirectory() as tmp: result = _run(tmp, {"README.md": "# OK\n"}, name="my-repo") assert result.repo_name == "my-repo" # ── invalid path ────────────────────────────────────────────────────── def test_invalid_path_raises(): try: Linter("/nonexistent/path/xyz") assert False, "Should have raised FileNotFoundError" except FileNotFoundError: pass # ── skip dirs ────────────────────────────────────────────────────────── def test_git_dir_skipped(): with tempfile.TemporaryDirectory() as tmp: repo = _make_repo(tmp, { "README.md": "# R\n\nGood repo.", "main.py": "x = 1\n", }) # Create a .git/ dir with a bad file git_dir = repo / ".git" git_dir.mkdir() (git_dir / "bad.py").write_text("HOST = '203.0.113.1'\n") result = Linter(str(repo)).run() git_errors = [e for e in result.errors if ".git" in e] assert len(git_errors) == 0