"""L1: `tht cte next` — the first plan CTE not yet approved (drives the F6 gate).""" import json from typer.testing import CliRunner from tht.cli.cte_cmd import cte_app from tht.config import DatabaseConfig from tht.decisions import append_decision from tht.session.store import create_session def _db(): return DatabaseConfig(database="testdb", user="u", password="p", **{"schema": "public"}) # noqa: S106 def _patch_cfg(monkeypatch, tmp_path): from tht.cli import cte_cmd class FakePaths: sessions = tmp_path class FakeCfg: paths = FakePaths() database = _db() monkeypatch.setattr(cte_cmd, "_load_config_or_exit", lambda _: FakeCfg()) def test_cte_next_first_unapproved(tmp_path, monkeypatch): m = create_session("q", _db(), tmp_path) (tmp_path / m.id / "cte_plan.json").write_text(json.dumps(["a", "b", "c"])) _patch_cfg(monkeypatch, tmp_path) res = CliRunner().invoke(cte_app, ["next", "--session", m.id]) assert res.exit_code == 0, res.output assert res.output.strip() == "a" def test_cte_next_after_one_approval(tmp_path, monkeypatch): m = create_session("q", _db(), tmp_path) (tmp_path / m.id / "cte_plan.json").write_text(json.dumps(["a", "b", "c"])) append_decision(tmp_path / m.id, type="cte_approved", subject="a") _patch_cfg(monkeypatch, tmp_path) res = CliRunner().invoke(cte_app, ["next", "--session", m.id]) assert res.exit_code == 0, res.output assert res.output.strip() == "b" def test_cte_next_empty_when_all_approved(tmp_path, monkeypatch): m = create_session("q", _db(), tmp_path) (tmp_path / m.id / "cte_plan.json").write_text(json.dumps(["a"])) append_decision(tmp_path / m.id, type="cte_approved", subject="a") _patch_cfg(monkeypatch, tmp_path) res = CliRunner().invoke(cte_app, ["next", "--session", m.id]) assert res.exit_code == 0, res.output assert res.output.strip() == ""