Files
FabledScribe/tests/test_record_kind_surface.py
T
bvandeusenandClaude Opus 5.5 ba0b56018e docs(6209): calibration stamp, after-write hook, usage seam, work log, duplicates, skill structure, tuning and rulebook routes, kind surface, prompt filter, moments, strict args, auth, lesson write path
Fourteen more test files: docstrings state the failure each guard defends
as a present-tense shape instead of the incident that found it, and issue,
milestone and rule numbers leave comments and assertion messages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 21:35:44 -04:00

115 lines
4.3 KiB
Python

"""Every typed record kind gets the same doors.
WHY THIS IS ONE GUARD AND NOT FIFTEEN
A kind can ship across several steps and still arrive with three of five
tools, no way to list it, and no duplicate report — with nothing red, because
every test asks "does what I built work" and none asks "is the kind
finished". A per-tool test cannot catch a MISSING tool.
So this asserts the property directly — a kind with its own `note_type` has a
full create/read/update/delete plus a listing, each one registered and each one
classified for auth — and derives the expectation from the kinds themselves. A
fourth kind added later inherits the bar without anyone remembering to.
A kind deletable only through `delete_note` is the same failure: "a kind whose
own tools offer create/read/update reads as one you cannot retire".
"""
from __future__ import annotations
import importlib
import pytest
from scribe.mcp.server import (
_DELIBERATELY_WRITE_SCOPED,
_READ_ONLY_TOOLS,
_WRITE_TOOLS,
)
from scribe.services.dedup import _REPORT_KINDS
# singular -> the tools module, which is also the plural used by the listing.
KINDS = {"snippet": "snippets", "process": "processes", "lesson": "lessons"}
def _module(plural: str):
return importlib.import_module(f"scribe.mcp.tools.{plural}")
def _expected_tools(singular: str, plural: str) -> list[str]:
return [
f"list_{plural}",
f"create_{singular}",
f"get_{singular}",
f"update_{singular}",
f"delete_{singular}",
]
@pytest.mark.parametrize("singular,plural", sorted(KINDS.items()))
def test_a_kind_has_all_five_doors(singular, plural):
"""THE guard. A kind that can be created and not listed, or updated and not
retired, is one whose gaps are invisible until someone needs the missing
half."""
module = _module(plural)
missing = [
name for name in _expected_tools(singular, plural)
if not callable(getattr(module, name, None))
]
assert not missing, f"{plural}.py is missing {missing}"
@pytest.mark.parametrize("singular,plural", sorted(KINDS.items()))
def test_every_door_is_actually_registered(singular, plural):
"""Defining the function is not offering it. `register(mcp)` is what puts a
tool on the surface, and a handler left out of that list is dead code that
every other test still exercises directly."""
module = _module(plural)
registered: list[str] = []
class _Recorder:
def tool(self, name):
registered.append(name)
return lambda fn: fn
module.register(_Recorder())
missing = [n for n in _expected_tools(singular, plural) if n not in registered]
assert not missing, f"{plural}.register() does not offer {missing}"
@pytest.mark.parametrize("singular,plural", sorted(KINDS.items()))
def test_every_door_is_classified_for_auth(singular, plural):
"""`test_mcp_auth` requires every registered tool to sit in exactly one of
the three sets. Asserted per KIND as well, because that test enumerates
what is registered — so a whole module wired up with none of its tools
classified is caught here by the kind that owns them."""
classified = _READ_ONLY_TOOLS | _WRITE_TOOLS | _DELIBERATELY_WRITE_SCOPED
missing = [n for n in _expected_tools(singular, plural) if n not in classified]
assert not missing, f"unclassified in server.py: {missing}"
@pytest.mark.parametrize("singular", sorted(KINDS))
def test_every_kind_can_be_asked_whether_it_holds_duplicates(singular):
"""A kind with no report is one whose duplicates are found by accident.
It matters most where the write-path gate is deliberately permissive: a
lesson's bar sits above the band where two genuinely different lessons
would block each other, and the report is where that tolerance is meant to
be reviewed.
"""
assert singular in _REPORT_KINDS
def test_the_surface_guards_can_fail():
"""Falsify the shape these assert against, so a guard that has
quietly stopped describing anything cannot pass by describing nothing."""
assert _expected_tools("lesson", "lessons") == [
"list_lessons", "create_lesson", "get_lesson",
"update_lesson", "delete_lesson",
]
# A kind that does not exist has no module — the lookup is real, not a
# getattr that returns None for everything.
with pytest.raises(ModuleNotFoundError):
_module("widgets")