diff --git a/backend/tests/aim/test_aim_pipeline_events.py b/backend/tests/aim/test_aim_pipeline_events.py index 06021af..55168b0 100644 --- a/backend/tests/aim/test_aim_pipeline_events.py +++ b/backend/tests/aim/test_aim_pipeline_events.py @@ -126,9 +126,14 @@ async def test_generate_section_emits_writer_reviewer_pair_per_attempt_and_compl assert result.stop_reason == "passed" assert result.final_passed_gate is True - # Exactly one writer+reviewer pair (since first attempt passed) - writer_evts = [e for e in emitter.events if e.get("agent") == "writer"] - reviewer_evts = [e for e in emitter.events if e.get("agent") == "reviewer"] + # Exactly one writer+reviewer pair (since first attempt passed). + # Filter to lifecycle (agent_status) events only — the pipeline also + # emits an `attempt` event tagged agent=reviewer carrying the per-attempt + # payload (m12-pr06: was previously counted as a 3rd reviewer event). + writer_evts = [e for e in emitter.events + if e.get("agent") == "writer" and e["event_type"] == "agent_status"] + reviewer_evts = [e for e in emitter.events + if e.get("agent") == "reviewer" and e["event_type"] == "agent_status"] assert len(writer_evts) == 2 # running + completed assert len(reviewer_evts) == 2 # running + completed # passed_gate surfaced in reviewer completed event data diff --git a/backend/tests/security/test_tenancy_isolation.py b/backend/tests/security/test_tenancy_isolation.py index 48b4e00..a2b6ff0 100644 --- a/backend/tests/security/test_tenancy_isolation.py +++ b/backend/tests/security/test_tenancy_isolation.py @@ -149,7 +149,9 @@ async def test_unauthenticated_resource_routes_return_401(client_a): try: transport = ASGITransport(app=_app) async with AsyncClient(transport=transport, base_url="http://test") as anon: - r = await anon.get(f"/api/missions/{FOREIGN_RESOURCE_ID}") + # m12-pr06: /api/missions was removed; use a stable resource + # route that still requires get_current_user. + r = await anon.get(f"/api/jobs/{FOREIGN_RESOURCE_ID}") assert r.status_code in (401, 403), ( f"unauthenticated request should be rejected, got {r.status_code}" ) diff --git a/backend/tests/unit/test_observability_w3.py b/backend/tests/unit/test_observability_w3.py index e565aba..4fc6277 100644 --- a/backend/tests/unit/test_observability_w3.py +++ b/backend/tests/unit/test_observability_w3.py @@ -49,7 +49,9 @@ def test_metrics_collector_default_keys_when_unknown(): def test_metrics_endpoint_exposes_llm_and_cost_gauges(): - import backend.main as backend_main # type: ignore + # m12-pr06: metrics moved from backend/main.py into the dedicated + # prometheus_collectors module. Source-of-truth is the collectors file. + import backend.app.core.prometheus_collectors as backend_main # type: ignore src = inspect.getsource(backend_main) for marker in ( "hirestack_llm_calls_total", diff --git a/backend/tests/unit/test_perf_optimizations.py b/backend/tests/unit/test_perf_optimizations.py index 48e1801..5959bde 100644 --- a/backend/tests/unit/test_perf_optimizations.py +++ b/backend/tests/unit/test_perf_optimizations.py @@ -118,7 +118,8 @@ def test_recon_block_does_not_await_intel_inline(): def test_metrics_endpoint_exposes_cache_and_phase_gauges(): """The /metrics route source must expose cache + per-phase metrics.""" - import backend.main as backend_main # type: ignore[import-not-found] + # m12-pr06: metrics moved from backend/main.py into prometheus_collectors. + import backend.app.core.prometheus_collectors as backend_main # type: ignore[import-not-found] src = inspect.getsource(backend_main) # Cache hit-rate assert "hirestack_ai_cache_hits_total" in src diff --git a/backend/tests/unit/test_prod_readiness_audit.py b/backend/tests/unit/test_prod_readiness_audit.py index 3674cf7..40f750c 100644 --- a/backend/tests/unit/test_prod_readiness_audit.py +++ b/backend/tests/unit/test_prod_readiness_audit.py @@ -17,7 +17,9 @@ def test_gemini_provider_invokes_circuit_breaker() -> None: so a fully-down provider fast-fails instead of cascading. """ from ai_engine import client as ai_client - src = inspect.getsource(ai_client._GeminiProvider._generate_content_throttled) + # m12-pr06: the outer _generate_content_throttled is a thin Langfuse-tracing + # wrapper; the real SDK chokepoint (and breaker gate) lives in _inner. + src = inspect.getsource(ai_client._GeminiProvider._generate_content_throttled_inner) assert "_get_model_breaker" in src, "breaker must be invoked, not just imported" assert "async with _breaker" in src or "async with breaker" in src.lower(), \ "breaker must gate the SDK call via async-with" @@ -42,8 +44,11 @@ def test_webhook_idempotency_table_registered() -> None: def test_webhook_idempotency_migration_exists() -> None: from pathlib import Path + # m12-pr06: migrations live under supabase/migrations with a 14-digit + # timestamp prefix (Supabase convention), not the originally-planned + # database/migrations/20260420_*.sql path. repo_root = Path(__file__).resolve().parents[3] - mig = repo_root / "database" / "migrations" / "20260420_stripe_webhook_idempotency.sql" + mig = repo_root / "supabase" / "migrations" / "20260420000000_stripe_webhook_idempotency.sql" assert mig.exists(), f"missing migration: {mig}" sql = mig.read_text() assert "processed_webhook_events" in sql diff --git a/backend/tests/unit/test_quality_scorer.py b/backend/tests/unit/test_quality_scorer.py index 0002463..03ad797 100644 --- a/backend/tests/unit/test_quality_scorer.py +++ b/backend/tests/unit/test_quality_scorer.py @@ -134,7 +134,8 @@ def test_metrics_collector_record_doc_quality_clamps_invalid(): # ── /metrics anchor: gauge presence ─────────────────────────────────── def test_metrics_endpoint_exposes_doc_quality_gauges(): - import backend.main as backend_main # type: ignore + # m12-pr06: metrics moved from backend/main.py into prometheus_collectors. + import backend.app.core.prometheus_collectors as backend_main # type: ignore src = inspect.getsource(backend_main) for marker in ( "hirestack_doc_quality_mean", diff --git a/docs/architecture/WORLD_CLASS_ARCHITECTURE_BLUEPRINT.md b/docs/architecture/WORLD_CLASS_ARCHITECTURE_BLUEPRINT.md index ca8ccfb..7dfd40a 100644 --- a/docs/architecture/WORLD_CLASS_ARCHITECTURE_BLUEPRINT.md +++ b/docs/architecture/WORLD_CLASS_ARCHITECTURE_BLUEPRINT.md @@ -1000,7 +1000,7 @@ Every fix has a tracking ID. Status updated in PRs. Closing the last entry in th | P1-8 | Per-customer cost attribution table + materialized view | — | platform | TODO | | P1-9 | Centralized feature-flag service with audit | — | platform | **PARTIAL** — `config/feature_flags.yaml` + sunset-CI live; audit table pending | | P1-10 | Coverage gate + xdist in CI; promote `deps-audit` to required | — | devex | **SHIPPED** — coverage gate (m12-pr02), xdist + `deps-audit` required (m12-pr04) | -| P1-11 | Triage 9 baseline test failures (fix or `xfail` with linked issues) | W14 | all | TODO — needs fresh triage on m12-pr03 base | +| P1-11 | Triage 9 baseline test failures (fix or `xfail` with linked issues) | W14 | all | SHIPPED (m12-pr06) — 7 stale tests pointing at moved/renamed code; 4090/4090 green | | P1-12 | Adversarial prompt-injection defense (pre-classifier + structural separation) | W15 | ai-team + security | TODO | | P1-13 | Realtime gateway extraction (when SSE > 1000 concurrent) | — | platform | DEFERRED — gates on SSE > 1000 concurrent | | P1-14 | `import-linter` contracts wired in CI | — | architecture-wg | **SHIPPED** — `.github/workflows/architecture.yml` |