# tests/test_prevent_contract.py
"""contract.py — the frozen Finding/emit JSON shape every detector speaks. Status auto-degrades when
coverage is incomplete (no-false-clean), so a partial scan can NEVER serialize as a clean 'ok'."""
import importlib.util, io, json, os, sys

ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

def _load(name, path):
    spec = importlib.util.spec_from_file_location(name, path)
    mod = importlib.util.module_from_spec(spec); spec.loader.exec_module(mod); return mod

contract = _load("sg_contract", os.path.join(ROOT, "prevent", "contract.py"))

def test_finding_shape_has_all_contract_keys():
    f = contract.finding("R1", "warning", "S9", "msg", "/a/b.ts", 12, "owner|referee")
    assert set(f) == {"ruleId", "level", "class", "message", "file", "line", "symbol"}
    assert f["level"] == "warning" and f["symbol"] == "owner|referee" and f["line"] == 12

def test_bad_level_rejected():
    # ValueError (not assert) so it survives `python3 -O`
    try:
        contract.finding("R1", "blocker", "S9", "m", "/a", 0, "")
        assert False, "bad level must raise"
    except ValueError as e:
        assert "level" in str(e)

def test_emit_ok_when_no_unresolved(capsys):
    contract.emit("deps", [], ["/repo"])
    out = json.loads(capsys.readouterr().out)
    assert out["status"] == "ok" and out["coverage"]["unresolved"] == []

def test_emit_degraded_when_unresolved_nonempty(capsys):
    # no-false-clean: any unresolved coverage forces degraded, regardless of findings
    contract.emit("oracle", [], ["/a.ts"], ["/a.ts"])
    out = json.loads(capsys.readouterr().out)
    assert out["status"] == "degraded" and out["coverage"]["unresolved"] == ["/a.ts"]
