CI & Build / Python lint (push) Successful in 5s
CI & Build / Plugin hooks (push) Successful in 17s
CI & Build / TypeScript typecheck (push) Successful in 57s
CI & Build / integration (push) Successful in 1m19s
CI & Build / Python tests (push) Successful in 2m2s
CI & Build / Build & push image (push) Successful in 15s
Docstrings, section headers and assertion messages drop the record number, milestone or step that introduced the behaviour, and incident accounts are restated as the present-tense failure shape. A dead pointer to a design doc that is not in the repo is removed. Renamed to the property they check: test_the_old_shape_would_be_caught -> test_a_rank_then_scope_query_is_caught test_a_ledger_repeat_no_longer_produces_that_case_at_all -> test_a_ledger_repeat_is_counted_not_withheld test_the_header_no_longer_promises_once_per_session -> test_the_header_explains_the_seen_marker test_the_portal_case_no_longer_stamps_one_canon_onto_a_whole_file -> test_one_canon_is_not_stamped_onto_a_whole_file test_the_repos_own_prose_no_longer_defines_with -> test_the_repos_own_prose_does_not_define_with test_the_original_regression_create_note_with_content -> test_the_real_server_refuses_content_on_create_note Fixture and example ids stay: "note 5" in a supersession chain, "rule 156" in an illustrative question, `#2275` as the hex-versus-citation case, `#3244` as the example of a bare number, fixed dates in date-filter tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
120 lines
4.9 KiB
Python
120 lines
4.9 KiB
Python
"""Structural tests for the design-systems blueprint + the parity guard.
|
|
|
|
Modelled on tests/test_routes_snippets.py. The enumerations below are
|
|
deliberate: relaxing one to a pattern would stop it catching the thing it was
|
|
written for, which is a capability landing on one surface and not the other.
|
|
"""
|
|
import inspect
|
|
from tests.helpers import FakeMCP
|
|
|
|
|
|
def test_design_systems_blueprint_registered():
|
|
from scribe.routes.design_systems import design_systems_bp
|
|
assert design_systems_bp.name == "design_systems"
|
|
assert design_systems_bp.url_prefix == "/api"
|
|
|
|
|
|
def test_design_systems_blueprint_registered_in_app():
|
|
from scribe.app import create_app
|
|
app = create_app()
|
|
assert "design_systems" in app.blueprints
|
|
|
|
|
|
def test_route_handlers_callable():
|
|
from scribe.routes import design_systems as routes
|
|
for name in (
|
|
"list_design_systems", "create_design_system", "get_design_system",
|
|
"update_design_system", "delete_design_system", "resolve_design_system",
|
|
"get_design_system_stylesheet",
|
|
"check_snippets_against_system",
|
|
"list_design_tokens", "create_design_token",
|
|
"update_design_token", "delete_design_token", "set_project_design_system",
|
|
):
|
|
assert callable(getattr(routes, name))
|
|
|
|
|
|
def test_every_endpoint_is_reachable_on_the_app():
|
|
"""The handlers existing is not the same as them being routed. This catches
|
|
a decorator that was copied without its path changing — two handlers on one
|
|
rule, where the second silently never runs."""
|
|
from scribe.app import create_app
|
|
app = create_app()
|
|
rules = {
|
|
str(r.rule) for r in app.url_map.iter_rules()
|
|
if r.endpoint.startswith("design_systems.")
|
|
}
|
|
assert rules == {
|
|
"/api/design-systems",
|
|
# Static segment, declared before the <int:...> rule reads it — Quart's
|
|
# int converter will not match "starter-roles", so the two cannot
|
|
# collide. Worth stating: a static-vs-dynamic sibling on the same prefix
|
|
# is exactly where a silently-shadowed route hides.
|
|
"/api/design-systems/starter-roles",
|
|
"/api/design-systems/<int:design_system_id>",
|
|
"/api/design-systems/<int:design_system_id>/resolved",
|
|
"/api/design-systems/<int:design_system_id>/stylesheet",
|
|
"/api/design-systems/<int:design_system_id>/snippet-check",
|
|
"/api/design-systems/<int:design_system_id>/tokens",
|
|
"/api/design-tokens/<int:token_id>",
|
|
"/api/projects/<int:project_id>/design-system",
|
|
}
|
|
|
|
|
|
def test_service_functions_take_user_id():
|
|
"""Routes must call the services with user_id — verify the contract (#33)."""
|
|
from scribe.services import design_systems as svc
|
|
for fn_name in (
|
|
"create_design_system", "list_design_systems", "get_design_system",
|
|
"update_design_system", "delete_design_system", "resolve_design_system",
|
|
"create_token", "list_tokens", "update_token", "delete_token",
|
|
"set_project_design_system",
|
|
"stylesheet_for_system", "check_snippets_against_system",
|
|
):
|
|
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.
|
|
|
|
Two surfaces drift apart without failing anything — one door with no
|
|
delete, the other with no near-duplicate gate — until someone goes
|
|
looking.
|
|
"""
|
|
from scribe.mcp.tools import design_systems as tools
|
|
from scribe.routes import design_systems as routes
|
|
|
|
for name in (
|
|
"create_design_system", "list_design_systems", "get_design_system",
|
|
"resolve_design_system", "update_design_system", "delete_design_system",
|
|
"create_design_token", "list_design_tokens", "update_design_token",
|
|
"delete_design_token", "set_project_design_system",
|
|
"get_design_system_stylesheet", "list_starter_role_groups",
|
|
):
|
|
assert callable(getattr(tools, name)), f"MCP tool missing: {name}"
|
|
assert callable(getattr(routes, name)), f"REST route missing: {name}"
|
|
|
|
# The snippet check is the one verb whose handler names differ between the
|
|
# surfaces (the tool says what it checks AGAINST; the route is already under
|
|
# the system), so the loop above can't pair it. It still has to exist on both.
|
|
assert callable(tools.check_snippets_against_design_system)
|
|
assert callable(routes.check_snippets_against_system)
|
|
|
|
|
|
def test_every_mcp_tool_in_the_module_is_registered():
|
|
"""A tool written but never registered is invisible to an agent, and nothing
|
|
else in the codebase would notice."""
|
|
from scribe.mcp.tools import design_systems as tools
|
|
|
|
mcp = FakeMCP()
|
|
|
|
tools.register(mcp)
|
|
|
|
public = {
|
|
name for name, obj in vars(tools).items()
|
|
if inspect.iscoroutinefunction(obj) and not name.startswith("_")
|
|
}
|
|
assert set(mcp.names) == public
|