"""Helper script for component_resolver.robot end-to-end tests. Each subcommand is a self-contained check that prints a sentinel on success. """ from __future__ import annotations import sys from pathlib import Path from typing import Any, Protocol, runtime_checkable # Ensure local source tree is importable _SRC = str(Path(__file__).resolve().parents[1] / "src") if _SRC not in sys.path: sys.path.insert(0, _SRC) from cleveragents.application.services.component_resolver import ( # noqa: E402 ComponentNotFoundError, ComponentRegistrationError, ComponentResolver, ScopeLevel, ) # --------------------------------------------------------------------------- # Test protocol and implementations # --------------------------------------------------------------------------- @runtime_checkable class _Greeter(Protocol): def greet(self) -> str: ... class _DefaultGreeter: def greet(self) -> str: return "hello-global" class _ProjectGreeter: def greet(self) -> str: return "hello-project" class _PlanGreeter: def greet(self) -> str: return "hello-plan" # --------------------------------------------------------------------------- # Subcommands # --------------------------------------------------------------------------- def global_default_resolution() -> None: """Verify global default resolution.""" resolver = ComponentResolver() resolver.register_global(_Greeter, _DefaultGreeter()) result = resolver.resolve(_Greeter) if result.scope == ScopeLevel.GLOBAL and result.component.greet() == "hello-global": print("component-resolver-global-ok") else: print(f"FAIL: {result.scope} / {result.component.greet()}", file=sys.stderr) sys.exit(1) def project_override() -> None: """Verify project override takes precedence over global.""" resolver = ComponentResolver() resolver.register_global(_Greeter, _DefaultGreeter()) resolver.register_project("proj-1", _Greeter, _ProjectGreeter()) result = resolver.resolve(_Greeter, project_id="proj-1") if ( result.scope == ScopeLevel.PROJECT and result.component.greet() == "hello-project" ): print("component-resolver-project-override-ok") else: print(f"FAIL: {result.scope} / {result.component.greet()}", file=sys.stderr) sys.exit(1) def plan_override() -> None: """Verify plan override takes highest precedence.""" resolver = ComponentResolver() resolver.register_global(_Greeter, _DefaultGreeter()) resolver.register_project("proj-1", _Greeter, _ProjectGreeter()) resolver.register_plan("plan-1", _Greeter, _PlanGreeter()) result = resolver.resolve(_Greeter, plan_id="plan-1", project_id="proj-1") if result.scope == ScopeLevel.PLAN and result.component.greet() == "hello-plan": print("component-resolver-plan-override-ok") else: print(f"FAIL: {result.scope} / {result.component.greet()}", file=sys.stderr) sys.exit(1) def fallthrough_to_global() -> None: """Verify fallthrough when no plan or project override exists.""" resolver = ComponentResolver() resolver.register_global(_Greeter, _DefaultGreeter()) result = resolver.resolve(_Greeter, plan_id="no-plan", project_id="no-project") if result.scope == ScopeLevel.GLOBAL and result.component.greet() == "hello-global": print("component-resolver-fallthrough-ok") else: print(f"FAIL: {result.scope} / {result.component.greet()}", file=sys.stderr) sys.exit(1) def missing_component_error() -> None: """Verify ComponentNotFoundError for unregistered components.""" resolver = ComponentResolver() try: resolver.resolve(_Greeter) print("FAIL: expected ComponentNotFoundError", file=sys.stderr) sys.exit(1) except ComponentNotFoundError: print("component-resolver-not-found-ok") def caching_behavior() -> None: """Verify resolution caching.""" resolver = ComponentResolver() resolver.register_global(_Greeter, _DefaultGreeter()) r1 = resolver.resolve(_Greeter) r2 = resolver.resolve(_Greeter) if r1 is r2 and resolver.cache_size() == 1: print("component-resolver-caching-ok") else: print(f"FAIL: same={r1 is r2}, cache={resolver.cache_size()}", file=sys.stderr) sys.exit(1) def security_module_prefix() -> None: """Verify module prefix restriction.""" resolver = ComponentResolver() try: resolver._import_component("evil_module:Hacker") print("FAIL: expected error", file=sys.stderr) sys.exit(1) except ComponentRegistrationError: print("component-resolver-security-ok") def introspection_apis() -> None: """Verify introspection methods.""" resolver = ComponentResolver() resolver.register_global(_Greeter, _DefaultGreeter()) resolver.register_project("p1", _Greeter, _ProjectGreeter()) resolver.register_plan("pl1", _Greeter, _PlanGreeter()) g = resolver.list_global_types() p = resolver.list_project_types("p1") pl = resolver.list_plan_types("pl1") if "_Greeter" in g and "_Greeter" in p and "_Greeter" in pl: print("component-resolver-introspection-ok") else: print(f"FAIL: global={g}, project={p}, plan={pl}", file=sys.stderr) sys.exit(1) def clear_all() -> None: """Verify clear removes all registrations.""" resolver = ComponentResolver() resolver.register_global(_Greeter, _DefaultGreeter()) resolver.register_project("p1", _Greeter, _ProjectGreeter()) resolver.register_plan("pl1", _Greeter, _PlanGreeter()) resolver.clear() ok = ( not resolver.has_global(_Greeter) and not resolver.has_project("p1", _Greeter) and not resolver.has_plan("pl1", _Greeter) ) if ok: print("component-resolver-clear-ok") else: print("FAIL: clear did not remove all", file=sys.stderr) sys.exit(1) # --------------------------------------------------------------------------- # Main dispatcher # --------------------------------------------------------------------------- _COMMANDS: dict[str, Any] = { "global-default": global_default_resolution, "project-override": project_override, "plan-override": plan_override, "fallthrough": fallthrough_to_global, "not-found": missing_component_error, "caching": caching_behavior, "security": security_module_prefix, "introspection": introspection_apis, "clear": clear_all, } if __name__ == "__main__": if len(sys.argv) < 2: print(f"Usage: {sys.argv[0]} ", file=sys.stderr) print(f"Commands: {', '.join(sorted(_COMMANDS))}", file=sys.stderr) sys.exit(1) cmd = sys.argv[1] fn = _COMMANDS.get(cmd) if fn is None: print(f"Unknown command: {cmd}", file=sys.stderr) sys.exit(1) fn()