"""Step definitions for sandbox factory strategy routing coverage. Covers lines in ``factory.py``: - Empty resource_id / original_path validation - none strategy -> NoSandbox - git_worktree strategy -> GitWorktreeSandbox - copy_on_write strategy -> CopyOnWriteSandbox - transaction_rollback -> TransactionSandbox - snapshot -> NotImplementedError - overlay / versioning (removed) -> ValueError (unknown) - Unknown strategy -> ValueError - is_supported() for all strategies - get_supported_strategies() for spec-aligned resource types """ from __future__ import annotations from behave import given, then, when from cleveragents.infrastructure.sandbox.copy_on_write import CopyOnWriteSandbox from cleveragents.infrastructure.sandbox.factory import SandboxFactory from cleveragents.infrastructure.sandbox.git_worktree import GitWorktreeSandbox from cleveragents.infrastructure.sandbox.no_sandbox import NoSandbox from cleveragents.infrastructure.sandbox.protocol import SandboxStatus from cleveragents.infrastructure.sandbox.transaction_sandbox import TransactionSandbox # --------------------------------------------------------------------------- # Givens # --------------------------------------------------------------------------- @given("the sandbox factory is available") def given_sandbox_factory_available(context): """Instantiate a SandboxFactory for the scenario.""" context.sandbox_factory = SandboxFactory() # --------------------------------------------------------------------------- # Whens - create_sandbox # --------------------------------------------------------------------------- @when("a sandbox is requested with an empty resource identifier") def when_factory_empty_resource_id(context): """Attempt to create a sandbox with an empty resource_id.""" context.factory_error = None try: context.sandbox_factory.create_sandbox( resource_id="", original_path="/some/path", sandbox_strategy="none", ) except ValueError as exc: context.factory_error = exc @when("a sandbox is requested with an empty resource path") def when_factory_empty_path(context): """Attempt to create a sandbox with an empty original_path.""" context.factory_error = None try: context.sandbox_factory.create_sandbox( resource_id="res-1", original_path="", sandbox_strategy="none", ) except ValueError as exc: context.factory_error = exc @when( 'a sandbox is requested for resource "{res_id}" at "{path}" using the none strategy' ) def when_factory_none_strategy(context, res_id: str, path: str): """Request a sandbox with the 'none' (passthrough) strategy.""" context.factory_error = None context.factory_sandbox = context.sandbox_factory.create_sandbox( resource_id=res_id, original_path=path, sandbox_strategy="none", ) @when( 'a sandbox is requested for resource "{res_id}" at "{path}" ' "using the git worktree strategy" ) def when_factory_git_worktree(context, res_id: str, path: str): """Request a sandbox with the 'git_worktree' strategy.""" context.factory_error = None context.factory_sandbox = context.sandbox_factory.create_sandbox( resource_id=res_id, original_path=path, sandbox_strategy="git_worktree", ) @when( 'a sandbox is requested for resource "{res_id}" at "{path}" ' "using the copy-on-write strategy" ) def when_factory_copy_on_write(context, res_id: str, path: str): """Request a sandbox with the 'copy_on_write' strategy.""" context.factory_error = None context.factory_sandbox = context.sandbox_factory.create_sandbox( resource_id=res_id, original_path=path, sandbox_strategy="copy_on_write", ) @when( 'a sandbox is requested for resource "{res_id}" at "{path}" ' "using the overlay strategy" ) def when_factory_overlay(context, res_id: str, path: str): """Request a sandbox with the removed 'overlay' strategy (now unknown).""" context.factory_error = None try: context.sandbox_factory.create_sandbox( resource_id=res_id, original_path=path, sandbox_strategy="overlay", # type: ignore[arg-type] ) except ValueError as exc: context.factory_error = exc @when( 'a sandbox is requested for resource "{res_id}" at "{path}" ' "using the transaction rollback strategy" ) def when_factory_transaction_rollback(context, res_id: str, path: str): """Request a sandbox with the 'transaction_rollback' strategy.""" context.factory_error = None context.factory_sandbox = context.sandbox_factory.create_sandbox( resource_id=res_id, original_path=path, sandbox_strategy="transaction_rollback", ) @when( 'a sandbox is requested for resource "{res_id}" at "{path}" ' "using the versioning strategy" ) def when_factory_versioning(context, res_id: str, path: str): """Request a sandbox with the removed 'versioning' strategy (now unknown).""" context.factory_error = None try: context.sandbox_factory.create_sandbox( resource_id=res_id, original_path=path, sandbox_strategy="versioning", # type: ignore[arg-type] ) except ValueError as exc: context.factory_error = exc @when( 'a sandbox is requested for resource "{res_id}" at "{path}" ' "using the snapshot strategy" ) def when_factory_snapshot(context, res_id: str, path: str): """Request a sandbox with the 'snapshot' strategy (not yet implemented).""" context.factory_error = None try: context.sandbox_factory.create_sandbox( resource_id=res_id, original_path=path, sandbox_strategy="snapshot", ) except NotImplementedError as exc: context.factory_error = exc @when( 'a sandbox is requested for resource "{res_id}" at "{path}" ' "using an unrecognised strategy" ) def when_factory_unknown_strategy(context, res_id: str, path: str): """Request a sandbox with a completely bogus strategy name.""" context.factory_error = None try: context.sandbox_factory.create_sandbox( resource_id=res_id, original_path=path, sandbox_strategy="quantum_entanglement", # type: ignore[arg-type] ) except ValueError as exc: context.factory_error = exc # --------------------------------------------------------------------------- # Whens - is_supported # --------------------------------------------------------------------------- @when("the factory is asked whether the none strategy is supported") def when_factory_is_supported_none(context): """Check support for the 'none' strategy.""" context.factory_supported = SandboxFactory.is_supported("none") @when("the factory is asked whether the git worktree strategy is supported") def when_factory_is_supported_git(context): """Check support for the 'git_worktree' strategy.""" context.factory_supported = SandboxFactory.is_supported("git_worktree") @when("the factory is asked whether the copy-on-write strategy is supported") def when_factory_is_supported_cow(context): """Check support for the 'copy_on_write' strategy.""" context.factory_supported = SandboxFactory.is_supported("copy_on_write") @when("the factory is asked whether the overlay strategy is supported") def when_factory_is_supported_overlay(context): """Check support for the 'overlay' strategy.""" context.factory_supported = SandboxFactory.is_supported("overlay") @when("the factory is asked whether an unrecognised strategy is supported") def when_factory_is_supported_unknown(context): """Check support for a made-up strategy.""" context.factory_supported = SandboxFactory.is_supported("quantum_entanglement") # --------------------------------------------------------------------------- # Whens - get_supported_strategies # --------------------------------------------------------------------------- @when('the compatible strategies for a "{resource_type}" resource are queried') def when_factory_get_strategies(context, resource_type: str): """Query the compatible strategies for a known resource type.""" context.factory_strategies = SandboxFactory.get_supported_strategies(resource_type) @when('the compatible strategies for an "{resource_type}" resource are queried') def when_factory_get_strategies_an(context, resource_type: str): """Query the compatible strategies for a known resource type (article 'an').""" context.factory_strategies = SandboxFactory.get_supported_strategies(resource_type) @when("the compatible strategies for an unknown resource type are queried") def when_factory_get_strategies_unknown(context): """Query the compatible strategies for a resource type not in the map.""" context.factory_strategies = SandboxFactory.get_supported_strategies( "quantum_computer" ) # --------------------------------------------------------------------------- # Thens - validation errors # --------------------------------------------------------------------------- @then("the factory should reject the request due to a missing resource identifier") def then_factory_missing_resource_id(context): """Assert a ValueError about resource_id was raised.""" assert context.factory_error is not None, "Expected a ValueError" assert isinstance(context.factory_error, ValueError) assert "resource_id" in str(context.factory_error) @then("the factory should reject the request due to a missing resource path") def then_factory_missing_path(context): """Assert a ValueError about original_path was raised.""" assert context.factory_error is not None, "Expected a ValueError" assert isinstance(context.factory_error, ValueError) assert "original_path" in str(context.factory_error) @then("the factory should reject the request due to an unknown strategy") def then_factory_unknown_strategy(context): """Assert a ValueError about an unknown strategy was raised.""" assert context.factory_error is not None, "Expected a ValueError" assert isinstance(context.factory_error, ValueError) assert "unknown" in str(context.factory_error).lower() # --------------------------------------------------------------------------- # Thens - none strategy produces passthrough sandbox # --------------------------------------------------------------------------- @then("the factory should produce a passthrough sandbox") def then_factory_produces_nosandbox(context): """Assert the returned sandbox is a NoSandbox instance.""" assert isinstance(context.factory_sandbox, NoSandbox) @then("the produced sandbox should be in the pending state") def then_factory_sandbox_pending(context): """Assert the sandbox is in PENDING status.""" assert context.factory_sandbox.status == SandboxStatus.PENDING # --------------------------------------------------------------------------- # Thens - git worktree strategy produces sandbox # --------------------------------------------------------------------------- @then("the factory should produce a git worktree sandbox") def then_factory_produces_git_worktree(context): """Assert the returned sandbox is a GitWorktreeSandbox instance.""" assert isinstance(context.factory_sandbox, GitWorktreeSandbox) @then("the produced git worktree sandbox should be in the pending state") def then_factory_git_worktree_pending(context): """Assert the git worktree sandbox is in PENDING status.""" assert context.factory_sandbox.status == SandboxStatus.PENDING # --------------------------------------------------------------------------- # Thens - copy-on-write strategy produces sandbox # --------------------------------------------------------------------------- @then("the factory should produce a copy-on-write sandbox") def then_factory_produces_cow(context): """Assert the returned sandbox is a CopyOnWriteSandbox instance.""" assert isinstance(context.factory_sandbox, CopyOnWriteSandbox) @then("the produced copy-on-write sandbox should be in the pending state") def then_factory_cow_pending(context): """Assert the copy-on-write sandbox is in PENDING status.""" assert context.factory_sandbox.status == SandboxStatus.PENDING # --------------------------------------------------------------------------- # Thens - not yet implemented # --------------------------------------------------------------------------- @then("the factory should indicate the strategy is not yet implemented") def then_factory_not_implemented(context): """Assert a NotImplementedError was raised.""" assert context.factory_error is not None, "Expected a NotImplementedError" assert isinstance(context.factory_error, NotImplementedError) @then("the factory should produce a transaction sandbox in the pending state") def then_factory_produces_transaction_sandbox(context): """Assert the returned sandbox is a TransactionSandbox in PENDING.""" assert isinstance(context.factory_sandbox, TransactionSandbox) assert context.factory_sandbox.status == SandboxStatus.PENDING # --------------------------------------------------------------------------- # Thens - is_supported # --------------------------------------------------------------------------- @then("the factory should confirm the strategy is supported") def then_factory_supported_true(context): """Assert the strategy is reported as supported.""" assert context.factory_supported is True @then("the factory should indicate the strategy is not supported") def then_factory_supported_false(context): """Assert the strategy is reported as not supported.""" assert context.factory_supported is False # --------------------------------------------------------------------------- # Thens - get_supported_strategies # --------------------------------------------------------------------------- @then("the factory should return git worktree, copy-on-write, and none as compatible") def then_factory_git_checkout_strategies(context): """Assert the git-checkout strategies are correct.""" assert context.factory_strategies == ["git_worktree", "copy_on_write", "none"] @then("the factory should return copy-on-write and none as compatible") def then_factory_cow_none_strategies(context): """Assert the resource type supports copy_on_write and none.""" assert context.factory_strategies == ["copy_on_write", "none"] @then("the factory should return copy-on-write, overlay, and none as compatible") def then_factory_cow_overlay_none_strategies(context): """Assert the resource type supports copy_on_write, overlay, and none.""" assert context.factory_strategies == ["copy_on_write", "overlay", "none"] @then("the factory should return only the none strategy as compatible") def then_factory_none_only_strategies(context): """Assert only the 'none' strategy is returned.""" assert context.factory_strategies == ["none"]