Fastwhisper / tests /test_schedule_api.py
Mbonea's picture
Complete P1 schedule loop: seed day, live capacity, templates, API tests.
9af2b22
Raw
History Blame Contribute Delete
3.13 kB
"""API smoke tests for plan seed, feedback priors, agent auth, rules reschedule."""
from __future__ import annotations
from pathlib import Path
import pytest
from fastapi.testclient import TestClient
from app.config import get_settings
from app.schedule_math import ev_score
from app.schedule_reschedule import seed_starter_blocks
@pytest.fixture()
def client(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> TestClient:
monkeypatch.setenv("DATA_ROOT", str(tmp_path / "data"))
monkeypatch.setenv("AGENT_TOKEN", "test-agent-token")
monkeypatch.setenv("APP_SECRET_KEY", "test-secret-key")
monkeypatch.setenv("APP_PASSWORD", "test-password")
monkeypatch.setenv("ENV", "dev")
get_settings.cache_clear()
from app.main import create_app
c = TestClient(create_app())
assert c.post("/api/auth/login", json={"password": "test-password"}).status_code == 200
return c
def test_ev_score_locked_formula() -> None:
base = ev_score(p_done=0.8, utility=1.0, fun=0.5, d_hat_min=30, fse=0.2)
raised = ev_score(
p_done=0.8,
utility=1.0,
fun=0.5,
d_hat_min=30,
fse=0.2,
raise_eta=True,
raise_lambda_f=True,
)
assert isinstance(base, float) and isinstance(raised, float)
def test_seed_starter_keeps_p0() -> None:
blocks = seed_starter_blocks("2026-07-19", capacity_hint=0.8)
kinds = {b.kind for b in blocks}
assert "food_out" in kinds and "sleep_window" in kinds
assert all(b.locked for b in blocks if b.priority == "P0")
def test_plan_seed_and_feedback_priors(client: TestClient) -> None:
day = "2026-07-19"
r = client.post(f"/api/plan/{day}/seed")
assert r.status_code == 200, r.text
plan = r.json()["data"]
assert plan["blocks"]
assert "capacity_hint" in plan
bid = plan["blocks"][0]["id"]
fb = client.post(
f"/api/plan/{day}/blocks/{bid}/feedback",
json={
"did": "done",
"actual_min": 20,
"quality": 4,
"fun": 3,
"energy_after": 1,
"would_repeat": "yes",
},
)
assert fb.status_code == 200, fb.text
priors = client.get("/api/schedule/priors").json()["data"]["kinds"]
assert any(p.get("n", 0) >= 1 for p in priors)
def test_agent_requires_token(client: TestClient) -> None:
bare = TestClient(client.app)
r = bare.get("/api/agent/context?day=2026-07-19")
assert r.status_code in (401, 403)
ok = bare.get(
"/api/agent/context?day=2026-07-19",
headers={"Authorization": "Bearer test-agent-token"},
)
assert ok.status_code == 200
assert ok.json()["data"]["constraints"]["scope"] == "P1_thin_loop"
def test_reschedule_rules_source(client: TestClient) -> None:
day = "2026-07-20"
client.post(f"/api/plan/{day}/seed")
r = client.post(f"/api/plan/{day}/reschedule", json={"reason": "test", "force": True})
assert r.status_code == 200, r.text
body = r.json()["data"]
assert body["source"] in ("rules", "openrouter")
p0 = [b for b in body["plan"]["blocks"] if b["priority"] == "P0"]
assert p0