254 lines
8.9 KiB
Python
254 lines
8.9 KiB
Python
"""Tests for Package 3.2 PromptService — CRUD for prompt profiles and templates.
|
|
|
|
Uses real sqlite3 with tmp_path for full isolation. No mocks.
|
|
Each test gets its own fresh database seeded with A/B/C profiles.
|
|
"""
|
|
|
|
import sqlite3
|
|
|
|
import pytest
|
|
|
|
from app.core.sqlite_db import _SEED_TEMPLATES, init_prompts_db, seed_default_profiles
|
|
from app.services.prompt_service import PromptService
|
|
|
|
|
|
# ── Helper ─────────────────────────────────────────────────────────────────
|
|
|
|
|
|
def _create_service(tmp_path) -> PromptService:
|
|
db_path = str(tmp_path / "test.db")
|
|
conn = sqlite3.connect(db_path)
|
|
conn.row_factory = sqlite3.Row
|
|
conn.execute("PRAGMA foreign_keys=ON")
|
|
init_prompts_db(conn)
|
|
seed_default_profiles(conn)
|
|
conn.close()
|
|
return PromptService(db_path=db_path)
|
|
|
|
|
|
# ── list_profiles ──────────────────────────────────────────────────────────
|
|
|
|
|
|
def test_list_profiles_returns_abc_with_a_active(tmp_path):
|
|
svc = _create_service(tmp_path)
|
|
profiles = svc.list_profiles()
|
|
|
|
assert len(profiles) == 3
|
|
names = [p["name"] for p in profiles]
|
|
assert names == ["A", "B", "C"]
|
|
|
|
active_map = {p["name"]: p["is_active"] for p in profiles}
|
|
assert active_map["A"] is True
|
|
assert active_map["B"] is False
|
|
assert active_map["C"] is False
|
|
|
|
|
|
# ── activate_profile ───────────────────────────────────────────────────────
|
|
|
|
|
|
def test_activate_profile_b(tmp_path):
|
|
svc = _create_service(tmp_path)
|
|
svc.activate_profile("B")
|
|
|
|
profiles = svc.list_profiles()
|
|
active_map = {p["name"]: p["is_active"] for p in profiles}
|
|
assert active_map["A"] is False
|
|
assert active_map["B"] is True
|
|
assert active_map["C"] is False
|
|
|
|
|
|
def test_activate_profile_c(tmp_path):
|
|
svc = _create_service(tmp_path)
|
|
svc.activate_profile("C")
|
|
|
|
profiles = svc.list_profiles()
|
|
active_map = {p["name"]: p["is_active"] for p in profiles}
|
|
assert active_map["A"] is False
|
|
assert active_map["B"] is False
|
|
assert active_map["C"] is True
|
|
|
|
|
|
def test_activate_profile_invalid_name_raises(tmp_path):
|
|
svc = _create_service(tmp_path)
|
|
with pytest.raises(ValueError, match="Invalid profile name"):
|
|
svc.activate_profile("D")
|
|
|
|
|
|
# ── get_active_profile_name ────────────────────────────────────────────────
|
|
|
|
|
|
def test_get_active_profile_name_returns_a_after_seed(tmp_path):
|
|
svc = _create_service(tmp_path)
|
|
assert svc.get_active_profile_name() == "A"
|
|
|
|
|
|
def test_get_active_profile_name_after_switch(tmp_path):
|
|
svc = _create_service(tmp_path)
|
|
svc.activate_profile("B")
|
|
assert svc.get_active_profile_name() == "B"
|
|
|
|
|
|
# ── get_profile_prompts ───────────────────────────────────────────────────
|
|
|
|
|
|
def test_get_profile_prompts_returns_all_three_steps(tmp_path):
|
|
svc = _create_service(tmp_path)
|
|
prompts = svc.get_profile_prompts("A")
|
|
|
|
assert set(prompts.keys()) == {
|
|
"decompose", "filter", "generate",
|
|
"generate_per_subq", "filter_intro", "filter_section", "filter_outro",
|
|
}
|
|
assert "{question}" in prompts["decompose"]
|
|
assert "{question}" in prompts["filter"]
|
|
assert "{question}" in prompts["generate"]
|
|
assert "{context}" in prompts["generate"]
|
|
assert "{context_sections}" in prompts["generate_per_subq"]
|
|
|
|
|
|
def test_get_profile_prompts_invalid_name_raises(tmp_path):
|
|
svc = _create_service(tmp_path)
|
|
with pytest.raises(ValueError, match="Invalid profile name"):
|
|
svc.get_profile_prompts("D")
|
|
|
|
|
|
# ── get_prompt_template ────────────────────────────────────────────────────
|
|
|
|
|
|
def test_get_prompt_template_for_active_profile(tmp_path):
|
|
svc = _create_service(tmp_path)
|
|
template = svc.get_prompt_template("decompose")
|
|
assert template == _SEED_TEMPLATES["decompose"]
|
|
|
|
|
|
def test_get_prompt_template_after_activate(tmp_path):
|
|
svc = _create_service(tmp_path)
|
|
svc.update_prompt("B", "decompose", "B custom template")
|
|
svc.activate_profile("B")
|
|
assert svc.get_prompt_template("decompose") == "B custom template"
|
|
|
|
|
|
def test_get_prompt_template_invalid_step_raises(tmp_path):
|
|
svc = _create_service(tmp_path)
|
|
with pytest.raises(ValueError, match="Invalid step"):
|
|
svc.get_prompt_template("nonexistent")
|
|
|
|
|
|
# ── update_prompt ──────────────────────────────────────────────────────────
|
|
|
|
|
|
def test_update_prompt_persists_change(tmp_path):
|
|
svc = _create_service(tmp_path)
|
|
new_template = "Custom decompose prompt for {question}"
|
|
|
|
svc.update_prompt("A", "decompose", new_template)
|
|
prompts = svc.get_profile_prompts("A")
|
|
|
|
assert prompts["decompose"] == new_template
|
|
assert prompts["filter"] == _SEED_TEMPLATES["filter"]
|
|
assert prompts["generate"] == _SEED_TEMPLATES["generate"]
|
|
|
|
|
|
def test_update_prompt_invalid_name_raises(tmp_path):
|
|
svc = _create_service(tmp_path)
|
|
with pytest.raises(ValueError, match="Invalid profile name"):
|
|
svc.update_prompt("D", "decompose", "template")
|
|
|
|
|
|
def test_update_prompt_invalid_step_raises(tmp_path):
|
|
svc = _create_service(tmp_path)
|
|
with pytest.raises(ValueError, match="Invalid step"):
|
|
svc.update_prompt("A", "nonexistent", "template")
|
|
|
|
|
|
# ── update_all_prompts ─────────────────────────────────────────────────────
|
|
|
|
|
|
def test_update_all_prompts_batch(tmp_path):
|
|
svc = _create_service(tmp_path)
|
|
new_prompts = {
|
|
"decompose": "New decompose",
|
|
"filter": "New filter",
|
|
"generate": "New generate",
|
|
}
|
|
|
|
svc.update_all_prompts("A", new_prompts)
|
|
prompts = svc.get_profile_prompts("A")
|
|
|
|
assert prompts["decompose"] == "New decompose"
|
|
assert prompts["filter"] == "New filter"
|
|
assert prompts["generate"] == "New generate"
|
|
|
|
|
|
def test_update_all_prompts_invalid_name_raises(tmp_path):
|
|
svc = _create_service(tmp_path)
|
|
with pytest.raises(ValueError, match="Invalid profile name"):
|
|
svc.update_all_prompts("D", {"decompose": "x"})
|
|
|
|
|
|
def test_update_all_prompts_invalid_step_raises(tmp_path):
|
|
svc = _create_service(tmp_path)
|
|
with pytest.raises(ValueError, match="Invalid step"):
|
|
svc.update_all_prompts("A", {"nonexistent": "x"})
|
|
|
|
|
|
# ── reset_to_defaults ─────────────────────────────────────────────────────
|
|
|
|
|
|
def test_reset_to_defaults_all_steps(tmp_path):
|
|
svc = _create_service(tmp_path)
|
|
svc.update_all_prompts("A", {
|
|
"decompose": "MODIFIED decompose",
|
|
"filter": "MODIFIED filter",
|
|
"generate": "MODIFIED generate",
|
|
})
|
|
|
|
svc.reset_to_defaults("A", step=None)
|
|
|
|
prompts = svc.get_profile_prompts("A")
|
|
assert prompts["decompose"] == _SEED_TEMPLATES["decompose"]
|
|
assert prompts["filter"] == _SEED_TEMPLATES["filter"]
|
|
assert prompts["generate"] == _SEED_TEMPLATES["generate"]
|
|
|
|
|
|
def test_reset_to_defaults_single_step(tmp_path):
|
|
svc = _create_service(tmp_path)
|
|
svc.update_all_prompts("A", {
|
|
"decompose": "MODIFIED decompose",
|
|
"filter": "MODIFIED filter",
|
|
"generate": "MODIFIED generate",
|
|
})
|
|
|
|
svc.reset_to_defaults("A", step="filter")
|
|
|
|
prompts = svc.get_profile_prompts("A")
|
|
assert prompts["decompose"] == "MODIFIED decompose"
|
|
assert prompts["filter"] == _SEED_TEMPLATES["filter"]
|
|
assert prompts["generate"] == "MODIFIED generate"
|
|
|
|
|
|
def test_reset_to_defaults_invalid_name_raises(tmp_path):
|
|
svc = _create_service(tmp_path)
|
|
with pytest.raises(ValueError, match="Invalid profile name"):
|
|
svc.reset_to_defaults("D")
|
|
|
|
|
|
# ── Edge cases ─────────────────────────────────────────────────────────────
|
|
|
|
|
|
def test_empty_string_template_allowed(tmp_path):
|
|
svc = _create_service(tmp_path)
|
|
svc.update_prompt("A", "decompose", "")
|
|
prompts = svc.get_profile_prompts("A")
|
|
assert prompts["decompose"] == ""
|
|
|
|
|
|
def test_very_long_template_allowed(tmp_path):
|
|
svc = _create_service(tmp_path)
|
|
long_template = "x" * 50_000
|
|
|
|
svc.update_prompt("A", "decompose", long_template)
|
|
prompts = svc.get_profile_prompts("A")
|
|
assert prompts["decompose"] == long_template
|
|
assert len(prompts["decompose"]) == 50_000
|