"""Tests for the FastAPI backend.""" from __future__ import annotations import pytest from fastapi.testclient import TestClient from spinor_os.config import EventType, LoopStage from spinor_os.server import app, get_os from spinor_os.assignment import AssignmentProviderRouter, LocalConstrainedAllocationProvider from spinor_os.engine import ExperimentationOS from spinor_os.models import PredictedEffect, Strategy @pytest.fixture def client(): """Yield a TestClient with a fresh OS instance.""" with TestClient(app) as c: yield c def test_health(client): response = client.get("/health") assert response.status_code == 200 data = response.json() assert data["status"] == "healthy" assert data["server"] == "ok" def test_ladder(client): response = client.get("/ladder") assert response.status_code == 200 data = response.json() assert "maturity_ladder" in data assert "canonical_loop" in data def test_schema(client): response = client.get("/schema") assert response.status_code == 200 data = response.json() assert "experiment" in data assert "rl_action" in data def test_full_experiment_flow(client): os = get_os() # Register employee. r = client.post("/employees", json={ "employee_id": "api-001", "role": "rep", "territory": "northeast", "skills": {"enterprise": 0.8}, }) assert r.status_code == 200 # Propose hypothesis. r = client.post("/hypotheses", json={ "statement": "X increases Y", "causal_claim": "X causes Y to increase.", "predicted_effect": { "metric": "y_rate", "direction": "increase", "magnitude": 0.1, "unit": "pp", "timing": "7d", }, "employee_owner": "api-001", "falsification_criteria": ["Y does not increase"], "customer_segment": "enterprise", "territory": "northeast", "modification": "mod-api", "required_replications": 1, }) assert r.status_code == 200 hypothesis_id = r.json()["hypothesis_id"] # Start experiment. r = client.post("/experiments", json={"hypothesis_id": hypothesis_id}) assert r.status_code == 200 experiment_id = r.json()["experiment_id"] # Allocate mission. r = client.post(f"/experiments/{experiment_id}/missions", json={ "employee_id": "api-001", "candidates": [{ "hypothesis_id": hypothesis_id, "modification": "mod-api", "customer_segment": "enterprise", "territory": "northeast", "timing": "2026-W31", "resource_allocation": 1.0, }], }) assert r.status_code == 200 mission_id = r.json()["mission_id"] # Record outcome event. r = client.post(f"/experiments/{experiment_id}/events", json={ "mission_id": mission_id, "event_type": "outcome_observed", "actor_id": "api-001", "outcome_value": 0.2, "metric": "y_rate", "execution_quality": 0.85, }) assert r.status_code == 200 # Attribute. r = client.post(f"/experiments/{experiment_id}/attributes", json={ "outcome_metric": "y_rate", "outcome_value": 0.2, "counterfactual_estimate": 0.05, "method": "rct", "confidence": 0.95, "falsification_survived": True, }) assert r.status_code == 200 claim = r.json() assert claim["causal_effect"] == 0.15 # Advance to systemize. r = client.post(f"/experiments/{experiment_id}/advance", json={"target": "systemize"}) assert r.status_code == 200 assert r.json()["current_stage"] == "systemize" # Systemize and promote. r = client.post("/systemize", json={ "experiment_id": experiment_id, "name": "API Strategy", "description": "Validated via API", "automation_ready": True, }) assert r.status_code == 200 strategy_id = r.json()["strategy_id"] # Lower the registry threshold so this single experiment can promote. os.golden_registry.rules.required_replications = 1 r = client.post("/promote", json={ "strategy_id": strategy_id, "mechanism": "Validated via API test", "automation_layer_id": "auto-api", "adverse_retests": 1, }) assert r.status_code == 200 assert r.json()["success"] is True # ────────────────────────────── snapshot endpoints ────────────────────────────── def test_snapshot_capture(client): r = client.post("/snapshots/capture") assert r.status_code == 200 data = r.json() assert "snapshot_id" in data assert "engine_state" in data def test_snapshot_list(client): client.post("/snapshots/capture") r = client.get("/snapshots") assert r.status_code == 200 data = r.json() assert isinstance(data, list) assert len(data) >= 1 def test_snapshot_latest(client): client.post("/snapshots/capture") r = client.get("/snapshots/latest") assert r.status_code == 200 assert "snapshot_id" in r.json() def test_snapshot_get_by_id(client): cap = client.post("/snapshots/capture").json() sid = cap["snapshot_id"] r = client.get(f"/snapshots/{sid}") assert r.status_code == 200 assert r.json()["snapshot_id"] == sid def test_snapshot_ingest(client): payload = { "snapshot_id": "ingest-test-001", "created_at": "2026-01-01T00:00:00Z", "machine": "test-machine", "sequence": 1, "engine_state": {"employees": {}}, "working_tree": {"branch": "main", "head_sha": "abc", "status_short": "", "diff_stat": "", "file_hashes": {}}, "transcript": [], } r = client.post("/snapshots/ingest", json=payload) assert r.status_code == 200 assert r.json()["status"] == "ok" # ────────────────────────────── oracle endpoints ────────────────────────────── def test_oracle_provenance(client): r = client.get("/oracle/provenance") assert r.status_code == 200 data = r.json() assert "files" in data assert "attestations" in data assert data["file_count"] >= 1 def test_oracle_seo(client): r = client.get("/oracle/seo") assert r.status_code == 200 data = r.json() assert "keywords" in data assert "title_suggestion" in data assert "sitemap_entry" in data def test_oracle_full(client): r = client.post("/oracle", json={"base_url": "https://test.local"}) assert r.status_code == 200 data = r.json() assert "provenance" in data assert "attribution_audit" in data assert "seo" in data def test_sitemap_xml(client): r = client.get("/oracle/sitemap.xml") assert r.status_code == 200 assert "urlset" in r.text def test_robots_txt(client): r = client.get("/robots.txt") assert r.status_code == 200 assert "User-agent" in r.text # ────────────────────────────── resume endpoint ────────────────────────────── def test_resume_no_snapshot(client): # Clear any existing snapshots by using a fresh manager dir is hard, # so just test that resume works when a snapshot exists. client.post("/snapshots/capture") r = client.post("/resume", json={"steps": 1}) assert r.status_code == 200 data = r.json() assert "engine_health" in data assert "transitions" in data def test_resume_with_steps(client): client.post("/snapshots/capture") r = client.post("/resume", json={"steps": 3}) assert r.status_code == 200 assert "steps_attempted" in r.json() # ────────────────────────────── UI endpoint ────────────────────────────── def test_ui_dashboard(client): r = client.get("/ui") assert r.status_code == 200 assert "