Files
FabledScribe/tests/test_routes_snippets.py
T
bvandeusen 84c5c0dc81
CI & Build / Python lint (push) Successful in 3s
CI & Build / integration (push) Successful in 16s
CI & Build / TypeScript typecheck (push) Successful in 34s
CI & Build / Python tests (push) Successful in 52s
CI & Build / Build & push image (push) Successful in 45s
fix(snippets): satisfy the parity guards the drift check tripped
Two CI failures, both the guards working as intended rather than defects
in them:

- The MCP tool-name manifest and the REST route/service parity lists are
  explicit, so a new capability has to be added to both surfaces or the
  test fails. verify_snippet / verify_snippet_route / record_verification
  added, plus an assertion that the `verification` filter reaches both
  callers — a verdict an agent records must be visible to the human
  looking at the same corpus, or the two surfaces disagree about what's
  rotten (rule #33).

- vue-tsc rejected two object-literal lookups keyed by the status union:
  `status` includes "ok" and "unverified", and an object literal has to
  enumerate every member even just to say "nothing to show for these".
  Typed as Record<string, string>, which is what the ?? "" fallback
  already assumed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UaYUaouG9jjhATyuxCKrQs
2026-07-28 18:24:48 -04:00

105 lines
4.8 KiB
Python

"""Structural tests for the snippets blueprint — registration + handler/service
contracts. Full HTTP integration needs a live DB + auth the unit env lacks."""
import inspect
def test_snippets_blueprint_registered():
from scribe.routes.snippets import snippets_bp
assert snippets_bp.name == "snippets"
assert snippets_bp.url_prefix == "/api/snippets"
def test_snippets_blueprint_registered_in_app():
from scribe.app import create_app
app = create_app()
assert "snippets" in app.blueprints
def test_snippet_handlers_callable():
from scribe.routes import snippets as routes
for name in (
"list_snippets_route", "create_snippet_route", "get_snippet_route",
"update_snippet_route", "delete_snippet_route", "merge_snippet_route",
"verify_snippet_route",
):
assert callable(getattr(routes, name))
def test_service_functions_take_user_id():
"""Routes must call snippet services with user_id — verify the contract."""
from scribe.services import snippets as svc
for fn_name in (
"create_snippet", "list_snippets", "get_snippet", "update_snippet",
"delete_snippet", "merge_snippets", "record_verification",
):
fn = getattr(svc, fn_name)
assert callable(fn)
assert "user_id" in inspect.signature(fn).parameters
def test_agent_and_web_surfaces_stay_at_parity():
"""The MCP tools and the REST routes are two callers of one service; a
capability on one has to exist on the other (rule #33). This guard exists
because they drifted apart once: MCP had no delete or `locations`, and the
web side had no `system_ids` and no near-duplicate gate."""
from scribe.mcp.tools import snippets as tools
from scribe.routes import snippets as routes
# Every write verb the web surface offers, the agent surface offers too.
for verb in ("create", "get", "list", "update", "delete", "merge", "verify"):
assert callable(getattr(tools, f"{verb}_snippets", None) or
getattr(tools, f"{verb}_snippet", None)), f"MCP lacks {verb}"
# Multi-location records are reachable from both.
assert "locations" in inspect.signature(tools.create_snippet).parameters
assert "locations" in inspect.signature(tools.update_snippet).parameters
assert "locations" in inspect.getsource(routes.create_snippet_route)
assert "locations" in inspect.getsource(routes.update_snippet_route)
# System association and the duplicate gate reach both.
assert "system_ids" in inspect.signature(tools.create_snippet).parameters
for route in (routes.create_snippet_route, routes.update_snippet_route):
assert "system_ids" in inspect.getsource(route)
assert "find_duplicate_note" in inspect.getsource(routes.create_snippet_route)
# The drift check (#2086) reaches both: recording a verdict, and filtering
# the list by one. A verdict an agent records has to be visible to the human
# looking at the same corpus, or the two surfaces disagree about what's rotten.
assert "verification" in inspect.signature(tools.list_snippets).parameters
assert "verification" in inspect.getsource(routes.list_snippets_route)
def test_project_scoping_reaches_every_caller():
"""A snippet search has to be narrowable to one project from both surfaces."""
from scribe.mcp.tools import snippets as tools
from scribe.routes import snippets as routes
from scribe.services import snippets as svc
assert "project_id" in inspect.signature(svc.list_snippets).parameters
assert "project_id" in inspect.signature(tools.list_snippets).parameters
assert "project_id" in inspect.getsource(routes.list_snippets_route)
def test_location_lookup_reaches_every_caller():
"""The reverse lookup — "what already lives here?" — has to be askable from
both surfaces, or an agent and a human get different answers about the same
file (rule #33)."""
from scribe.mcp.tools import snippets as tools
from scribe.routes import snippets as routes
from scribe.services import snippets as svc
for key in ("repo", "path", "symbol"):
assert key in inspect.signature(svc.list_snippets).parameters
assert key in inspect.signature(tools.list_snippets).parameters
assert f'request.args.get("{key}"' in inspect.getsource(routes)
def test_update_field_map_matches_service_kwargs():
"""Every field the PATCH route forwards must be a real update_snippet kwarg
(rule #33 interface-contract parity)."""
from scribe.routes import snippets as routes
from scribe.services import snippets as svc
params = inspect.signature(svc.update_snippet).parameters
for field in routes._STR_FIELDS:
assert field in params, f"update_snippet has no '{field}' kwarg"
assert "tags" in params
assert "project_id" in params