from __future__ import annotations import unittest from dataclasses import replace from fastapi import FastAPI from fastapi.testclient import TestClient from govoplan_core.auth import get_api_principal from govoplan_core.core.modules import ( FrontendModule, FrontendRoute, ModuleManifest, ProductAvailabilityExplanation, ProductAreaContribution, ProductSurfaceContribution, QuickAccessTool, ) from govoplan_core.core.registry import PlatformRegistry, RegistryError from govoplan_core.core.views import ViewSurface from govoplan_core.server.platform import create_platform_router def presentation_manifest() -> ModuleManifest: return ModuleManifest( id="example", name="Example", version="test", frontend=FrontendModule( module_id="example", routes=( FrontendRoute( path="/example", component="ExamplePage", surface_id="example.route.main", ), ), view_surfaces=( ViewSurface( id="example.quick.summary", module_id="example", kind="quick_access", label="Example summary", ), ), product_areas=( ProductAreaContribution( id="work", module_id="example", label="Work", icon="list-checks", surface_ids=("example.route.main",), ), ), product_surfaces=( ProductSurfaceContribution( id="work.examples", module_id="example", label="Examples", description="Review and update governed examples.", icon="list-checks", entry_path="/work/examples", route_path="/example", surface_ids=("example.route.main",), presentations=("task", "reader"), unavailable=ProductAvailabilityExplanation( reason="authorization", title="Examples are unavailable", description="Your current responsibility does not include examples.", resolution="Ask the responsible administrator to review your assignment.", responsible_role="Access administrator", ), ), ), quick_access_tools=( QuickAccessTool( id="example.summary", module_id="example", category_id="work", label="Example summary", surface_id="example.quick.summary", icon="list-checks", full_page_path="/example", required_any=("example:item:read",), returned_reference_kinds=("example.item",), help_context_id="example.quick.summary", ), ), ), ) class PresentationContractTests(unittest.TestCase): def test_registry_accepts_owned_presentation_contributions(self) -> None: registry = PlatformRegistry() registry.register(presentation_manifest()) snapshot = registry.validate() self.assertEqual("example", snapshot.manifests[0].id) def test_registry_rejects_unknown_tool_surface(self) -> None: manifest = presentation_manifest() frontend = manifest.frontend assert frontend is not None invalid = ModuleManifest( id=manifest.id, name=manifest.name, version=manifest.version, frontend=FrontendModule( module_id="example", routes=frontend.routes, quick_access_tools=( QuickAccessTool( id="example.summary", module_id="example", category_id="work", label="Example summary", surface_id="example.missing", icon="list-checks", ), ), ), ) registry = PlatformRegistry() registry.register(invalid) with self.assertRaisesRegex(RegistryError, "unknown surface"): registry.validate() def test_platform_payload_exposes_presentation_catalogue(self) -> None: registry = PlatformRegistry() registry.register(presentation_manifest()) registry.validate() app = FastAPI() app.state.govoplan_registry = registry app.include_router(create_platform_router(), prefix="/api/v1") app.dependency_overrides[get_api_principal] = lambda: object() with TestClient(app) as client: response = client.get("/api/v1/platform/modules") self.assertEqual(200, response.status_code) frontend = response.json()["modules"][0]["frontend"] self.assertEqual("1", frontend["presentation_contract_version"]) self.assertEqual("work", frontend["product_areas"][0]["id"]) product_surface = frontend["product_surfaces"][0] self.assertEqual("1", product_surface["contract_version"]) self.assertEqual("work.examples", product_surface["id"]) self.assertEqual("/work/examples", product_surface["entry_path"]) self.assertEqual("/example", product_surface["route_path"]) self.assertEqual(["task", "reader"], product_surface["presentations"]) self.assertEqual( "authorization", product_surface["unavailable"]["reason"], ) self.assertEqual("example.summary", frontend["quick_access_tools"][0]["id"]) self.assertEqual("1", frontend["quick_access_tools"][0]["contract_version"]) self.assertEqual( ["example.item"], frontend["quick_access_tools"][0]["returned_reference_kinds"], ) self.assertEqual( "example.quick.summary", frontend["quick_access_tools"][0]["help_context_id"], ) def test_registry_rejects_product_surface_without_owner_route(self) -> None: manifest = presentation_manifest() frontend = manifest.frontend assert frontend is not None surface = frontend.product_surfaces[0] invalid = ModuleManifest( id=manifest.id, name=manifest.name, version=manifest.version, frontend=FrontendModule( module_id=manifest.id, routes=frontend.routes, product_surfaces=( ProductSurfaceContribution( id=surface.id, module_id=surface.module_id, label=surface.label, description=surface.description, icon=surface.icon, entry_path=surface.entry_path, route_path="/missing", surface_ids=surface.surface_ids, unavailable=surface.unavailable, ), ), ), ) registry = PlatformRegistry() registry.register(invalid) with self.assertRaisesRegex(RegistryError, "unknown owner route"): registry.validate() def test_registry_rejects_product_alias_that_shadows_a_route(self) -> None: manifest = presentation_manifest() frontend = manifest.frontend assert frontend is not None surface = frontend.product_surfaces[0] invalid = replace( manifest, frontend=replace( frontend, routes=( *frontend.routes, FrontendRoute(path="/shortcut", component="ShortcutPage"), ), product_surfaces=(replace(surface, aliases=("/shortcut",)),), ), ) registry = PlatformRegistry() registry.register(invalid) with self.assertRaisesRegex(RegistryError, "collides with a concrete route"): registry.validate() if __name__ == "__main__": unittest.main()