""" tests/test_backlog_generator.py — Auto-generation engine unit tests. """ import os import sys import pytest sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "services", "router")) from backlog_store import MemoryBacklogStore, _new_id from backlog_generator import ( generate_from_pressure_digest, generate_from_risk_digest, _match_rule, _build_item_from_rule, _make_dedupe_key, _builtin_backlog_defaults, _reload_backlog_policy, ) POLICY = _builtin_backlog_defaults() def _pressure_digest(services=None): """Build a minimal platform_priority_digest JSON.""" if services is None: services = [ { "service": "gateway", "score": 85, "band": "critical", "requires_arch_review": True, "signals_summary": ["High recurrence 30d", "Overdue follow-ups"], "components": {"followups_overdue": 3, "regressions_30d": 2}, "evidence_refs": {"incidents": ["inc_001", "inc_002"]}, }, { "service": "router", "score": 55, "band": "high", "requires_arch_review": False, "signals_summary": ["Escalations 30d"], "components": {"followups_overdue": 0}, "evidence_refs": {}, }, ] return {"week": "2026-W08", "top_pressure_services": services} def _risk_digest(services=None): if services is None: services = [ { "service": "gateway", "score": 72, "band": "high", "trend": {"delta_24h": 15}, "components": {"slo": {"violations": 2}}, "reasons": ["SLO violation detected"], "attribution": {"evidence_refs": {"alerts": ["alrt_001"]}}, } ] return {"top_services": services} class TestMatchRule: def test_arch_review_required_matches(self): rule = POLICY["generation"]["rules"][0] # arch_review_required ctx = {"pressure_requires_arch_review": True} assert _match_rule(rule, ctx) is True def test_arch_review_required_no_match(self): rule = POLICY["generation"]["rules"][0] ctx = {"pressure_requires_arch_review": False} assert _match_rule(rule, ctx) is False def test_high_pressure_refactor_both_high(self): rule = POLICY["generation"]["rules"][1] ctx = {"pressure_band": "critical", "risk_band": "high"} assert _match_rule(rule, ctx) is True def test_high_pressure_refactor_only_one_high(self): rule = POLICY["generation"]["rules"][1] ctx = {"pressure_band": "low", "risk_band": "high"} assert _match_rule(rule, ctx) is False def test_slo_violations_match(self): rule = POLICY["generation"]["rules"][2] ctx = {"slo_violations": 2} assert _match_rule(rule, ctx) is True def test_slo_violations_zero_no_match(self): rule = POLICY["generation"]["rules"][2] ctx = {"slo_violations": 0} assert _match_rule(rule, ctx) is False def test_followup_backlog_match(self): rule = POLICY["generation"]["rules"][3] ctx = {"followups_overdue": 3} assert _match_rule(rule, ctx) is True def test_followup_backlog_zero_no_match(self): rule = POLICY["generation"]["rules"][3] ctx = {"followups_overdue": 0} assert _match_rule(rule, ctx) is False class TestBuildItemFromRule: def test_title_template(self): rule = POLICY["generation"]["rules"][0] ctx = {"pressure_requires_arch_review": True, "pressure_score": 80, "pressure_band": "critical", "followups_overdue": 2, "evidence_refs": {}} item = _build_item_from_rule("gateway", rule, ctx, POLICY, "2026-W08", "prod") assert item is not None assert "gateway" in item.title assert item.category == "arch_review" def test_priority_from_category(self): rule = POLICY["generation"]["rules"][0] ctx = {"evidence_refs": {}} item = _build_item_from_rule("svc", rule, ctx, POLICY, "2026-W08", "prod") assert item.priority == "P1" def test_due_date_is_set(self): rule = POLICY["generation"]["rules"][0] ctx = {"evidence_refs": {}} item = _build_item_from_rule("svc", rule, ctx, POLICY, "2026-W08", "prod") assert item.due_date != "" def test_owner_override_for_gateway(self): rule = POLICY["generation"]["rules"][0] ctx = {"evidence_refs": {}} item = _build_item_from_rule("gateway", rule, ctx, POLICY, "2026-W08", "prod") assert item.owner == "cto" def test_owner_default_for_other_service(self): rule = POLICY["generation"]["rules"][0] ctx = {"evidence_refs": {}} item = _build_item_from_rule("backend", rule, ctx, POLICY, "2026-W08", "prod") assert item.owner == "oncall" def test_dedupe_key_format(self): rule = POLICY["generation"]["rules"][0] ctx = {"evidence_refs": {}} item = _build_item_from_rule("gateway", rule, ctx, POLICY, "2026-W08", "prod") assert item.dedupe_key == "platform_backlog:2026-W08:prod:gateway:arch_review" def test_evidence_refs_propagated(self): rule = POLICY["generation"]["rules"][0] ctx = {"evidence_refs": {"incidents": ["inc_001"]}, "pressure_score": 80, "pressure_band": "critical", "followups_overdue": 2} item = _build_item_from_rule("gateway", rule, ctx, POLICY, "2026-W08", "prod") assert item.evidence_refs.get("incidents") == ["inc_001"] def test_description_includes_signals(self): rule = POLICY["generation"]["rules"][0] ctx = { "pressure_score": 80, "pressure_band": "critical", "signals_summary": ["High recurrence 30d"], "followups_overdue": 2, "evidence_refs": {}, } item = _build_item_from_rule("gateway", rule, ctx, POLICY, "2026-W08", "prod") assert "80" in item.description or "Pressure" in item.description class TestGenerateFromPressureDigest: def test_generates_items(self): store = MemoryBacklogStore() digest = _pressure_digest() result = generate_from_pressure_digest(digest, env="prod", store=store, policy=POLICY) assert result["created"] >= 1 assert len(result["items"]) >= 1 def test_idempotency_second_run_updates(self): store = MemoryBacklogStore() digest = _pressure_digest() r1 = generate_from_pressure_digest(digest, env="prod", store=store, policy=POLICY) r2 = generate_from_pressure_digest(digest, env="prod", store=store, policy=POLICY) # Second run should update, not create assert r1["created"] >= 1 assert r2["created"] == 0 assert r2["updated"] >= 1 def test_evidence_refs_propagated(self): store = MemoryBacklogStore() digest = _pressure_digest() generate_from_pressure_digest(digest, env="prod", store=store, policy=POLICY) items = store.list_items({"service": "gateway"}) # At least one item with incident evidence has_inc = any("incidents" in (it.evidence_refs or {}) for it in items) assert has_inc def test_risk_digest_enriches_context(self): store = MemoryBacklogStore() digest = _pressure_digest() risk = _risk_digest() result = generate_from_pressure_digest( digest, env="prod", store=store, policy=POLICY, risk_digest_data=risk, ) assert result["created"] >= 1 # SLO rule should also fire for gateway (slo_violations=2) items = store.list_items({"service": "gateway"}) cats = {it.category for it in items} assert "slo_hardening" in cats def test_max_items_per_run_capped(self): policy = dict(POLICY) policy["defaults"] = dict(POLICY["defaults"]) policy["defaults"]["max_items_per_run"] = 1 services = [ {"service": f"svc_{i}", "score": 80, "band": "critical", "requires_arch_review": True, "signals_summary": [], "components": {"followups_overdue": 0}, "evidence_refs": {}} for i in range(5) ] digest = _pressure_digest(services=services) store = MemoryBacklogStore() result = generate_from_pressure_digest(digest, env="prod", store=store, policy=policy) assert result["created"] + result["updated"] <= 1 def test_week_field_from_digest(self): store = MemoryBacklogStore() digest = _pressure_digest() result = generate_from_pressure_digest(digest, env="prod", store=store, policy=POLICY) assert result["week"] == "2026-W08" def test_weekly_disabled_skips(self): store = MemoryBacklogStore() policy = dict(POLICY) policy["generation"] = dict(POLICY["generation"]) policy["generation"]["weekly_from_pressure_digest"] = False digest = _pressure_digest() result = generate_from_pressure_digest(digest, env="prod", store=store, policy=policy) assert result["created"] == 0 assert "skipped_reason" in result class TestGenerateFromRiskDigest: def test_disabled_by_default(self): store = MemoryBacklogStore() result = generate_from_risk_digest( _risk_digest(), env="prod", store=store, policy=POLICY, ) assert result["created"] == 0 assert "skipped_reason" in result def test_enabled_creates_slo_item(self): policy = dict(POLICY) policy["generation"] = dict(POLICY["generation"]) policy["generation"]["daily_from_risk_digest"] = True store = MemoryBacklogStore() result = generate_from_risk_digest( _risk_digest(), env="prod", store=store, policy=policy, ) # SLO rule fires for gateway (2 violations) assert result["created"] >= 1 items = store.list_items({"service": "gateway"}) assert any(it.category == "slo_hardening" for it in items) class TestMakeDedupeKey: def test_format(self): key = _make_dedupe_key("platform_backlog", "2026-W08", "prod", "gateway", "arch_review") assert key == "platform_backlog:2026-W08:prod:gateway:arch_review" def test_different_services_different_keys(self): k1 = _make_dedupe_key("platform_backlog", "2026-W08", "prod", "svc_a", "arch_review") k2 = _make_dedupe_key("platform_backlog", "2026-W08", "prod", "svc_b", "arch_review") assert k1 != k2