"""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 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", "import_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", "/api/design-systems/", "/api/design-systems//resolved", "/api/design-systems//import", "/api/design-systems//stylesheet", "/api/design-systems//snippet-check", "/api/design-systems//tokens", "/api/design-tokens/", "/api/projects//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", "import_from_rulebook", "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 (rule #33). This guard exists because the snippet surfaces drifted apart once — MCP had no delete, the web side had no near-duplicate gate — and neither failed anything until someone went 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", ): assert callable(getattr(tools, name)), f"MCP tool missing: {name}" assert callable(getattr(routes, name)), f"REST route missing: {name}" # Import is the one verb whose handler names differ between the surfaces # (the tool says what it reads FROM; 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.import_design_system_from_rulebook) assert callable(routes.import_design_system) 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 registered = [] class _Recorder: def tool(self, name): registered.append(name) return lambda fn: fn tools.register(_Recorder()) public = { name for name, obj in vars(tools).items() if inspect.iscoroutinefunction(obj) and not name.startswith("_") } assert set(registered) == public