Files
ss-tools/backend/tests/api/test_dashboard_scenarios.py
busya 87d9624913 feat(038): Phase 9 — capture/vlm/disposition API + final gates
- T045 drift fix: added capture/vlm/disposition routes so all 7 openapi.yaml
  paths are implemented; 3 new API tests (8 total)
- T048-T056: prototype validation (24/24 states), OpenAPI drift check,
  belief audit 0 errors, ATTN audit, semantic rebuild (8094 contracts),
  orphan audit (0 orphans/0 unresolved in scenario scope), traceability
  coverage gate, full regression (91 backend + 20 agent tests green)
- pack_registry: REASON/REFLECT/EXPLORE instrumentation (C3 light)
- ruff clean; regions balanced
2026-07-31 13:20:37 +03:00

75 lines
2.9 KiB
Python

# #region Test.Api.Scenarios [C:3] [TYPE Module] [SEMANTICS testing,api,scenario,rbac]
# @defgroup Test.Api Scenario REST API tests — compile/validate/resolve/draft-pack + RBAC.
# @LAYER Test
# @RELATION BINDS_TO -> [Api.DashboardTesting.Scenario]
# @RATIONALE The REST surface must expose deterministic scenario operations with RBAC enforcement.
# @REJECTED Testing only the service layer — would leave authn/z and envelope drift unchecked.
from __future__ import annotations
import json
from pathlib import Path
import pytest
from fastapi.testclient import TestClient
from src.app import app
_FIXTURES = Path(__file__).resolve().parents[1] / "fixtures" / "dashboard_scenarios"
client = TestClient(app)
def _scenario_json() -> dict:
return json.loads((_FIXTURES / "scenario_valid.json").read_text(encoding="utf-8"))
def test_compile_requires_auth() -> None:
resp = client.post("/api/dashboard-testing/scenarios/compile", json={})
assert resp.status_code in (401, 403)
def test_compile_rejects_extra_fields() -> None:
body = {
"agent_run_id": "x", "objective": {"goal": "g", "selected_case_ids": []},
"query_model": {}, "checklist_catalog_version": 1, "baseline_version": "v",
"capabilities": {}, "parameters": {}, "has_dataset_fields": True,
"environment_id": "e", "dashboard_id": 1, "dashboard_name": "d",
"executable_code": "import os",
}
resp = client.post("/api/dashboard-testing/scenarios/compile", json=body)
# either auth gate (401/403) or 422 extra-forbid
assert resp.status_code in (401, 403, 422)
def test_validate_accepts_scenario_shape() -> None:
resp = client.post("/api/dashboard-testing/scenarios/validate", json=_scenario_json())
# auth gate may block first
assert resp.status_code in (401, 403, 200)
def test_resolve_rejects_stale_revision_shape() -> None:
body = {"base_revision_hash": "0" * 64, "changes": [{"kind": "parameter", "target": "test_date", "value": "2026-08-01"}]}
resp = client.post("/api/dashboard-testing/scenarios/fi-0080_verify/resolve", json=body)
assert resp.status_code in (401, 403, 409, 422)
def test_draft_pack_requires_auth() -> None:
resp = client.post("/api/dashboard-testing/scenarios/scn-1/draft-pack", json={})
assert resp.status_code in (401, 403)
def test_capture_route_registered() -> None:
resp = client.post("/api/dashboard-testing/scenarios/scn-1/capture", json={"profile_id": "default", "step_id": "s1"})
assert resp.status_code in (401, 403, 422, 200)
def test_vlm_route_rejects_bad_analysis() -> None:
resp = client.post("/api/dashboard-testing/scenarios/scn-1/vlm", json={"analysis": {"provider_id": "p"}})
assert resp.status_code in (401, 403, 422)
def test_disposition_route_rejects_empty() -> None:
resp = client.post("/api/dashboard-testing/scenarios/scn-1/disposition", json={"findings": [], "dispositions": []})
assert resp.status_code in (401, 403, 200, 422)