Files
LLeMbas/tests/test_admin_prompts.py
T
Homer e970f10cca A time in no particular zone, and a preview missing what it previews
The first audit pass: everything from 0.8.1 to 0.9.8 read as a whole rather
than one feature at a time, starting with what a model is actually told.

Four of these had shipped as correct. The date line carried a timezone
variable that resolves to nothing until somebody chooses one -- so every
default account was told times were "in  unless they say otherwise", while
two comments asserted the line disappeared instead. The prompt preview
built its variables without a chat, which is what eleven fragments are
gated on, so the whole agent surface was absent from it whatever was
ticked. Plan mode was instructed to keep its plan current with a tool that
mode withdraws. And knowledge_get returned a document whole where every
sibling reader caps and says so, its description promising exactly that.

The subagent guidance was wrong in both directions at once: it denied a
documented parameter and named seven of twenty-three allowed commands.
Both halves are pinned by tests against the real list and the real schema
now, because prose and a constant drift the moment one is edited alone.

docs/notes/audit-0.9.md carries the findings that are not fixed here, with
why -- the ones whose fix would change what a feature does are the user's
call, not this pass's.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 09:13:54 +02:00

208 lines
8.6 KiB
Python

"""The prompt editor: what it saves, what it refuses to save, and the preview."""
from __future__ import annotations
import pytest
from fastapi.testclient import TestClient
from sqlalchemy import select
from lembas.db.models import User
from lembas.services import harness, prompts, settings_store
from lembas.services.library import memories as memories_service
@pytest.fixture
def plain_user(client: TestClient, db, registered) -> User:
"""A second, non-admin account. Leaves the client signed in as them."""
client.post("/auth/logout", follow_redirects=False)
client.post(
"/auth/register",
data={"name": "Sam", "email": "sam@shire.test", "password": "potatoes-po-ta-toes"},
follow_redirects=False,
)
return db.scalar(select(User).where(User.email == "sam@shire.test"))
def _owner(db) -> User:
return db.scalar(select(User).where(User.email == "frodo@shire.test"))
# --- Access ------------------------------------------------------------------
def test_the_page_lists_every_fragment(client: TestClient, registered):
page = client.get("/admin/prompts").text
for fragment in prompts.BUILTIN:
assert f'name="prompt.{fragment.key}"' in page, fragment.key
def test_the_preview_controls_are_inside_what_the_preview_includes(
client: TestClient, registered
):
"""`hx-include="#prompt-form, #preview-controls"` is the whole wiring, so a
control placed outside that container is submitted by nothing and changes
nothing -- with no error, which is this codebase's recurring failure. Assert
the containment rather than the markup of any one field.
"""
page = client.get("/admin/prompts").text
controls = page.split('id="preview-controls"', 1)[1].split("</div>\n </div>", 1)[0]
for name in ("preview_model", "preview_bases", "preview_documents",
"preview_situation", "preview_mode", "preview_family"):
assert f'name="{name}"' in controls, name
def test_the_page_is_refused_to_a_plain_user(client: TestClient, plain_user):
assert client.get("/admin/prompts").status_code == 403
assert client.post("/admin/prompts", data={}).status_code == 403
# --- Saving ------------------------------------------------------------------
def test_saving_changes_what_a_model_is_told(client: TestClient, db, registered):
client.post(
"/admin/prompts",
data={"prompt.tool.web_search": "- Always search twice.", "max_harness_chars": "0"},
follow_redirects=False,
)
text = harness.compose(db, _owner(db), _tools("web_search"))
assert "- Always search twice." in text
assert "Look things up" not in text
def test_a_cleared_box_turns_the_fragment_off(client: TestClient, db, registered):
"""The trap this page was written around: FastAPI cannot tell an empty form
field from an absent one, so the handler reads the raw form."""
client.post(
"/admin/prompts",
data={"prompt.core.style": "", "max_harness_chars": "0"},
follow_redirects=False,
)
assert prompts.stored(db) == {"core.style": ""}
assert "Answer in the language" not in harness.compose(db, _owner(db), [])
def test_a_fragment_not_submitted_at_all_is_left_alone(client: TestClient, db, registered):
prompts.save(db, {"core.heading": "## Rules"})
client.post("/admin/prompts", data={"max_harness_chars": "0"}, follow_redirects=False)
assert prompts.resolve(db, "core.heading") == "## Rules"
def test_saving_the_built_in_wording_stores_nothing(client: TestClient, db, registered):
"""Opening the page and pressing Save must not freeze today's defaults, or a
later release could never improve them."""
page_fields = {f"prompt.{f.key}": f.default for f in prompts.BUILTIN}
client.post(
"/admin/prompts", data={**page_fields, "max_harness_chars": "0"}, follow_redirects=False
)
assert prompts.stored(db) == {}
def test_the_character_cap_is_clamped_and_kept(client: TestClient, db, registered):
client.post("/admin/prompts", data={"max_harness_chars": "-5"}, follow_redirects=False)
assert settings_store.get(db, "max_harness_chars", key=settings_store.PROMPTS) == 0
client.post("/admin/prompts", data={"max_harness_chars": "600"}, follow_redirects=False)
assert harness.limit_for(db) == 600
# --- Resetting ---------------------------------------------------------------
def test_use_default_fills_the_box_without_saving(client: TestClient, db, registered):
prompts.save(db, {"core.heading": "## Rules"})
response = client.post("/admin/prompts/default", data={"key": "core.heading"})
assert "## How to work" in response.text
assert "edited" not in response.text
# Nothing was written: it takes a Save to make it stick.
assert prompts.resolve(db, "core.heading") == "## Rules"
def test_use_default_on_an_unknown_key_is_a_404(client: TestClient, registered):
assert client.post("/admin/prompts/default", data={"key": "nope.nope"}).status_code == 404
def test_restore_all_defaults_empties_the_overrides(client: TestClient, db, registered):
prompts.save(db, {"core.heading": "## Rules", "core.style": ""})
client.post("/admin/prompts/reset", follow_redirects=False)
assert prompts.stored(db) == {}
# --- Preview -----------------------------------------------------------------
def test_the_preview_shows_text_that_has_not_been_saved(client: TestClient, db, registered):
body = client.post(
"/admin/prompts/preview",
data={"prompt.core.heading": "## Draft heading", "preview_family": ["web_search"]},
).text
assert "## Draft heading" in body
assert prompts.stored(db) == {}
def test_the_preview_only_shows_guidance_for_the_families_ticked(client: TestClient, registered):
body = client.post("/admin/prompts/preview", data={"preview_family": ["notes"]}).text
assert "You keep notes" in body
assert "Look things up" not in body
def test_the_preview_escapes_what_a_model_wrote(client: TestClient, db, registered):
"""A memory is model-written text on an admin page. Hard rule 6 applies to
the preview exactly as it does to a chat bubble."""
memories_service.add(db, owner=_owner(db), content="<img src=x onerror=alert(1)>")
body = client.post("/admin/prompts/preview", data={"preview_family": ["memory"]}).text
assert "<img src=x" not in body
assert "&lt;img src=x" in body
def test_the_preview_can_reach_the_fragments_that_need_a_chat(client: TestClient, registered):
"""Eleven fragments are gated on variables `context_variables` fills only
when it is handed a real `Chat`, and the preview hands it `None`. So the
entire agent surface, both scheduling fragments and the helper warning were
absent from every preview whatever was ticked -- an administrator editing
`tool.agent` previewed a system message with `tool.agent` missing from it,
and nothing said so. The samples are what close that.
"""
agent = client.post(
"/admin/prompts/preview",
data={"preview_family": ["agent"], "preview_mode": "plan"},
).text
# tool.agent (agent_target), and the mode picked rather than a fixed one.
assert "buildbox" in agent
assert "Plan** mode" in agent
# tool.project_files, context.agent_instructions, context.plan, tool.background
assert "pyproject.toml" in agent
assert "AGENTS.md" in agent
assert "Fix the parser" in agent
task = client.post(
"/admin/prompts/preview", data={"preview_situation": "task", "preview_family": []}
).text
assert "every weekday at 08:00" in task
helper = client.post(
"/admin/prompts/preview", data={"preview_situation": "helper", "preview_family": []}
).text
assert "buildbox" not in helper
assert helper != task
def test_the_preview_gates_its_samples_exactly_as_a_real_request_would(
client: TestClient, registered
):
"""A preview that admitted a fragment the real request would not is worse
than one that omitted it, so the samples follow the same gates: the agent
block on the family, the other two on the situation and on no family at all.
"""
plain = client.post("/admin/prompts/preview", data={"preview_family": ["notes"]}).text
assert "buildbox" not in plain
assert "AGENTS.md" not in plain
assert "every weekday at 08:00" not in plain
def test_the_preview_warns_when_the_cap_would_cut_it_off(client: TestClient, db, registered):
settings_store.update(db, {"max_harness_chars": 60}, key=settings_store.PROMPTS)
body = client.post("/admin/prompts/preview", data={"preview_family": ["web_search"]}).text
assert "past the cap is cut off" in body
def _tools(*names):
from lembas.services import tools as tools_service
return [tools_service.REGISTRY[name].schema for name in names]