220 lines
7.3 KiB
Python
220 lines
7.3 KiB
Python
"""Tests for Package 3.2 prompts router — HTTP endpoint integration tests.
|
|
|
|
Uses real sqlite3 with tmp_path. TestClient hits a minimal FastAPI app
|
|
wired with the prompts router. No mocks on the DB layer.
|
|
"""
|
|
|
|
import pytest
|
|
from fastapi import FastAPI
|
|
from fastapi.testclient import TestClient
|
|
|
|
from app.core.sqlite_db import init_prompts_db, seed_default_profiles, _get_db
|
|
from app.routers.prompts import router
|
|
|
|
|
|
# ── Fixture ────────────────────────────────────────────────────────────────
|
|
|
|
|
|
@pytest.fixture
|
|
def client(tmp_path, monkeypatch):
|
|
prompts_path = str(tmp_path / "prompts.db")
|
|
monkeypatch.setenv("PROMPTS_DB_PATH", prompts_path)
|
|
monkeypatch.setenv("HISTORY_DB_PATH", str(tmp_path / "history.db"))
|
|
|
|
from app.core.config import get_settings
|
|
get_settings.cache_clear()
|
|
from app.core.dependencies import get_settings_cached
|
|
get_settings_cached.cache_clear()
|
|
|
|
conn = _get_db(prompts_path)
|
|
init_prompts_db(conn)
|
|
seed_default_profiles(conn)
|
|
conn.close()
|
|
|
|
test_app = FastAPI()
|
|
test_app.include_router(router)
|
|
|
|
yield TestClient(test_app)
|
|
|
|
get_settings_cached.cache_clear()
|
|
get_settings.cache_clear()
|
|
|
|
|
|
# ── GET /profiles ──────────────────────────────────────────────────────────
|
|
|
|
|
|
def test_get_profiles_returns_200_with_three_items(client):
|
|
resp = client.get("/api/v1/prompts/profiles")
|
|
assert resp.status_code == 200
|
|
|
|
data = resp.json()
|
|
assert len(data["profiles"]) == 3
|
|
|
|
names = [p["name"] for p in data["profiles"]]
|
|
assert names == ["A", "B", "C"]
|
|
|
|
active_map = {p["name"]: p["is_active"] for p in data["profiles"]}
|
|
assert active_map["A"] is True
|
|
assert active_map["B"] is False
|
|
assert active_map["C"] is False
|
|
|
|
|
|
# ── GET /profiles/{name} ──────────────────────────────────────────────────
|
|
|
|
|
|
def test_get_profile_prompts_a_returns_200(client):
|
|
resp = client.get("/api/v1/prompts/profiles/A")
|
|
assert resp.status_code == 200
|
|
|
|
data = resp.json()
|
|
assert data["profile_name"] == "A"
|
|
assert set(data["prompts"].keys()) == {
|
|
"decompose", "filter", "generate",
|
|
"generate_per_subq", "filter_intro", "filter_section", "filter_outro",
|
|
}
|
|
|
|
|
|
def test_get_profile_prompts_invalid_returns_400(client):
|
|
resp = client.get("/api/v1/prompts/profiles/D")
|
|
assert resp.status_code == 400
|
|
|
|
|
|
# ── PUT /profiles/{name}/activate ─────────────────────────────────────────
|
|
|
|
|
|
def test_activate_profile_b_then_get_confirms(client):
|
|
resp = client.put("/api/v1/prompts/profiles/B/activate")
|
|
assert resp.status_code == 200
|
|
assert resp.json()["active_profile"] == "B"
|
|
|
|
resp = client.get("/api/v1/prompts/profiles")
|
|
active_map = {p["name"]: p["is_active"] for p in resp.json()["profiles"]}
|
|
assert active_map["B"] is True
|
|
assert active_map["A"] is False
|
|
assert active_map["C"] is False
|
|
|
|
|
|
# ── PUT /profiles/{name}/{step} ───────────────────────────────────────────
|
|
|
|
|
|
def test_update_prompt_returns_200_and_persists(client):
|
|
new_template = "Updated decompose for {question}"
|
|
|
|
resp = client.put(
|
|
"/api/v1/prompts/profiles/A/decompose",
|
|
json={"template": new_template},
|
|
)
|
|
assert resp.status_code == 200
|
|
assert resp.json()["step"] == "decompose"
|
|
|
|
resp = client.get("/api/v1/prompts/profiles/A")
|
|
assert resp.json()["prompts"]["decompose"] == new_template
|
|
|
|
|
|
# ── PUT /profiles/{name}/all ──────────────────────────────────────────────
|
|
|
|
|
|
def test_update_all_prompts_batch_returns_200_and_persists(client):
|
|
new_prompts = {
|
|
"decompose": "Batch decompose",
|
|
"filter": "Batch filter",
|
|
"generate": "Batch generate",
|
|
}
|
|
|
|
resp = client.put(
|
|
"/api/v1/prompts/profiles/A/all",
|
|
json={"prompts": new_prompts},
|
|
)
|
|
assert resp.status_code == 200
|
|
assert resp.json()["profile"] == "A"
|
|
|
|
resp = client.get("/api/v1/prompts/profiles/A")
|
|
prompts = resp.json()["prompts"]
|
|
assert prompts["decompose"] == "Batch decompose"
|
|
assert prompts["filter"] == "Batch filter"
|
|
assert prompts["generate"] == "Batch generate"
|
|
|
|
|
|
# ── PUT /profiles/{name}/reset ────────────────────────────────────────────
|
|
|
|
|
|
def test_reset_single_step_returns_200(client):
|
|
from app.core.sqlite_db import _SEED_TEMPLATES
|
|
|
|
client.put(
|
|
"/api/v1/prompts/profiles/A/decompose",
|
|
json={"template": "MODIFIED"},
|
|
)
|
|
|
|
resp = client.put(
|
|
"/api/v1/prompts/profiles/A/reset",
|
|
json={"step": "decompose"},
|
|
)
|
|
assert resp.status_code == 200
|
|
assert resp.json()["reset_step"] == "decompose"
|
|
|
|
resp = client.get("/api/v1/prompts/profiles/A")
|
|
assert resp.json()["prompts"]["decompose"] == _SEED_TEMPLATES["decompose"]
|
|
|
|
|
|
def test_reset_all_steps_returns_200(client):
|
|
from app.core.sqlite_db import _SEED_TEMPLATES
|
|
|
|
client.put("/api/v1/prompts/profiles/A/all", json={"prompts": {
|
|
"decompose": "MODIFIED decompose",
|
|
"filter": "MODIFIED filter",
|
|
"generate": "MODIFIED generate",
|
|
}})
|
|
|
|
resp = client.put(
|
|
"/api/v1/prompts/profiles/A/reset",
|
|
json={"step": None},
|
|
)
|
|
assert resp.status_code == 200
|
|
assert resp.json()["reset_step"] == "all"
|
|
|
|
resp = client.get("/api/v1/prompts/profiles/A")
|
|
prompts = resp.json()["prompts"]
|
|
assert prompts["decompose"] == _SEED_TEMPLATES["decompose"]
|
|
assert prompts["filter"] == _SEED_TEMPLATES["filter"]
|
|
assert prompts["generate"] == _SEED_TEMPLATES["generate"]
|
|
|
|
|
|
def test_reset_with_no_body_resets_all(client):
|
|
from app.core.sqlite_db import _SEED_TEMPLATES
|
|
|
|
client.put("/api/v1/prompts/profiles/A/all", json={"prompts": {
|
|
"decompose": "MODIFIED",
|
|
"filter": "MODIFIED",
|
|
"generate": "MODIFIED",
|
|
}})
|
|
|
|
resp = client.put("/api/v1/prompts/profiles/A/reset")
|
|
assert resp.status_code == 200
|
|
assert resp.json()["reset_step"] == "all"
|
|
|
|
resp = client.get("/api/v1/prompts/profiles/A")
|
|
prompts = resp.json()["prompts"]
|
|
assert prompts["decompose"] == _SEED_TEMPLATES["decompose"]
|
|
assert prompts["filter"] == _SEED_TEMPLATES["filter"]
|
|
assert prompts["generate"] == _SEED_TEMPLATES["generate"]
|
|
|
|
|
|
# ── Validation: invalid name and step ──────────────────────────────────────
|
|
|
|
|
|
def test_invalid_profile_name_returns_400(client):
|
|
resp = client.get("/api/v1/prompts/profiles/D")
|
|
assert resp.status_code == 400
|
|
|
|
resp = client.put("/api/v1/prompts/profiles/D/activate")
|
|
assert resp.status_code == 400
|
|
|
|
|
|
def test_invalid_step_returns_400(client):
|
|
resp = client.put(
|
|
"/api/v1/prompts/profiles/A/nonexistent",
|
|
json={"template": "test"},
|
|
)
|
|
assert resp.status_code == 400
|