"""ORCH-019 — Quality-Gate invariants on the bug-fast-track (root invariant NFR-1). Covers (04-test-plan.yaml): TC-07 The QG_CHECKS registry + the check_* signatures are NOT changed by the bug-fast-track; the machine verdict-keys (verdict / result / deploy_status / staging_status / security_status / coverage_status) are preserved by name and case. TC-12 check_analysis_complete does NOT special-case the bug track (ADR-001 D4): a bug lite-package that still emits all 4 analysis files passes; the same requirement holds for a non-bug task (no false block, no weakening). """ import os import tempfile os.environ.setdefault( "ORCH_DB_PATH", os.path.join(tempfile.gettempdir(), "test_bft_gates.db") ) os.environ.setdefault("ORCH_GITEA_TOKEN", "test-token") os.environ.setdefault("ORCH_PLANE_API_TOKEN", "test-token") from src.qg.checks import QG_CHECKS, check_analysis_complete # noqa: E402 # --- TC-07: registry + verdict-keys unchanged ------------------------------ def test_tc07_qg_checks_registry_unchanged(): # The exact registered gate set — a bug-fast-track must add/remove NOTHING. expected = { "check_analysis_complete", "check_analysis_approved", "check_architecture_done", "check_ci_green", "check_review_approved", "check_reviewer_verdict", "check_tests_local", "check_tests_passed", "check_staging_status", "check_staging_image_fresh", "check_deploy_status", "check_branch_mergeable", "check_security_gate", "check_coverage_gate", } assert set(QG_CHECKS.keys()) == expected def test_tc07_verdict_keys_preserved(): """The frontmatter machine verdict-keys are parsed by exact name/case. ORCH-019 touches none of the parsers, so the literal keys must still be present.""" import inspect from src.qg import checks as checks_mod src = inspect.getsource(checks_mod) for key in ("verdict:", "result:", "deploy_status:", "staging_status:"): assert key in src, f"verdict key '{key}' must be preserved in qg.checks" # security_status / coverage_status live in their own leaves but are read via # the same unified frontmatter contract — assert they survive there. import inspect as _i from src import security_gate, coverage_gate assert "security_status" in _i.getsource(security_gate) assert "coverage_status" in _i.getsource(coverage_gate) # --- TC-12: analysis gate not weakened, no false block --------------------- def _seed_analysis_docs(repo_root, work_item_id, files): d = os.path.join(repo_root, "docs", "work-items", work_item_id) os.makedirs(d, exist_ok=True) for fn in files: with open(os.path.join(d, fn), "w") as fh: fh.write("stub\n") def test_tc12_bug_lite_package_with_all_four_passes(monkeypatch, tmp_path): from src.qg import checks as checks_mod monkeypatch.setattr(checks_mod, "_repo_path", lambda repo, branch=None: str(tmp_path)) _seed_analysis_docs( str(tmp_path), "ORCH-bug", ["01-brd.md", "02-trz.md", "03-acceptance-criteria.md", "04-test-plan.yaml"], ) ok, reason = check_analysis_complete("orchestrator", "ORCH-bug", "feature/x") assert ok is True, reason def test_tc12_missing_file_still_fails_for_any_track(monkeypatch, tmp_path): """The gate is NOT weakened for bugs: a package missing 02/03 still fails — exactly as for a non-bug task (the gate never reads tasks.track).""" from src.qg import checks as checks_mod monkeypatch.setattr(checks_mod, "_repo_path", lambda repo, branch=None: str(tmp_path)) _seed_analysis_docs(str(tmp_path), "ORCH-bug", ["01-brd.md", "04-test-plan.yaml"]) ok, reason = check_analysis_complete("orchestrator", "ORCH-bug", "feature/x") assert ok is False assert "02-trz.md" in reason and "03-acceptance-criteria.md" in reason def test_tc12_signature_has_no_track_param(): import inspect params = list(inspect.signature(check_analysis_complete).parameters) # byte-for-byte signature: (repo, work_item_id, branch=None) — no track-awareness. assert params == ["repo", "work_item_id", "branch"]