"""Tests for the always-on ASUCB operating-contract injection.

Guards the fix for testers (Claude Code new-setup + Codex) not following the
ASUCB workflow because the orchestration contract lived only in CLAUDE.md,
which neither runtime auto-loaded at point of performance.

- AGENTS.md exists at repo root with the contract marker + key push-behavior
  sections (Codex reads AGENTS.md natively).
- hooks/inject_contract.sh emits valid SessionStart additionalContext JSON
  carrying the contract (Claude Code + cwd-independent delivery).
- hooks/hooks.json wires the inject hook into SessionStart alongside ensure_deps.
"""

import json
import os
import subprocess
import unittest
from pathlib import Path

REPO = Path(__file__).resolve().parents[1]
AGENTS = REPO / "AGENTS.md"
HOOK = REPO / "hooks" / "inject_contract.sh"
HOOKS_JSON = REPO / "hooks" / "hooks.json"

# Push-behavior sections that MUST reach every session, or testers regress to
# the "workflow not followed" state this fix addresses.
REQUIRED_SECTIONS = [
    "Action First",
    "Setup Gate",
    "Write Safety Guard",
    "HPI/ID Practitioner",
    "Routing",
    "Collaboration",
    "SDO JD template",
    "Script-Enforced",
    "push_to_canvas.py",
    "Batch",
    "course-audit",
    "Minimal-Diff",
    "Create an Artifact",
    "Codex Co-Implementation",
]


class AgentsMdTest(unittest.TestCase):
    def test_exists(self):
        self.assertTrue(AGENTS.is_file(), "AGENTS.md missing — Codex has no contract to auto-load")

    def test_has_marker(self):
        self.assertIn("ASUCB-CONTRACT", AGENTS.read_text(encoding="utf-8"))

    def test_has_required_sections(self):
        text = AGENTS.read_text(encoding="utf-8")
        for section in REQUIRED_SECTIONS:
            self.assertIn(section, text, f"AGENTS.md missing required section: {section}")

    def test_no_em_or_en_dash(self):
        text = AGENTS.read_text(encoding="utf-8")
        for token in ("—", "–", "&mdash;", "&ndash;"):
            # the token list inside the House-rules line is the only allowed mention
            stripped = text.replace("(—, –, `&mdash;`, `&ndash;`)", "")
            self.assertNotIn(token, stripped, f"AGENTS.md contains a stray {token!r}")


class HookScriptTest(unittest.TestCase):
    def test_hook_exists_and_executable(self):
        self.assertTrue(HOOK.is_file(), "inject_contract.sh missing")
        self.assertTrue(os.access(HOOK, os.X_OK), "inject_contract.sh not executable")

    def test_emits_valid_sessionstart_additional_context(self):
        env = dict(os.environ, ASUCB_PLUGIN_ROOT=str(REPO))
        out = subprocess.run(
            ["bash", str(HOOK)], capture_output=True, text=True, env=env, timeout=30
        )
        self.assertEqual(out.returncode, 0, out.stderr)
        payload = json.loads(out.stdout)
        ctx = payload["hookSpecificOutput"]["additionalContext"]
        self.assertEqual(payload["hookSpecificOutput"]["hookEventName"], "SessionStart")
        for section in REQUIRED_SECTIONS:
            self.assertIn(section, ctx)

    def test_silent_when_contract_absent(self):
        # Point at a root with no AGENTS.md: must exit 0 and emit nothing,
        # never break session start.
        env = dict(os.environ, ASUCB_PLUGIN_ROOT=str(REPO / "scripts"))
        out = subprocess.run(
            ["bash", str(HOOK)], capture_output=True, text=True, env=env, timeout=30
        )
        self.assertEqual(out.returncode, 0)
        self.assertEqual(out.stdout.strip(), "")


class HooksJsonWiringTest(unittest.TestCase):
    def test_inject_contract_wired_into_sessionstart(self):
        data = json.loads(HOOKS_JSON.read_text(encoding="utf-8"))
        commands = [
            h["command"]
            for group in data["hooks"]["SessionStart"]
            for h in group["hooks"]
        ]
        self.assertTrue(any("inject_contract.sh" in c for c in commands),
                        "SessionStart does not run inject_contract.sh")
        self.assertTrue(any("ensure_deps.sh" in c for c in commands),
                        "ensure_deps.sh wiring lost")


if __name__ == "__main__":
    unittest.main()
