e201aa07c9
CI / push-validation (pull_request) Successful in 25s
CI / helm (pull_request) Successful in 28s
CI / build (pull_request) Successful in 28s
CI / lint (pull_request) Successful in 53s
CI / quality (pull_request) Successful in 52s
CI / typecheck (pull_request) Successful in 54s
CI / security (pull_request) Successful in 1m27s
CI / unit_tests (pull_request) Successful in 8m0s
CI / docker (pull_request) Successful in 1m54s
CI / coverage (pull_request) Successful in 15m56s
CI / integration_tests (pull_request) Successful in 29m30s
CI / status-check (pull_request) Successful in 3s
CI / helm (push) Successful in 28s
CI / lint (push) Successful in 43s
CI / push-validation (push) Successful in 21s
CI / build (push) Successful in 50s
CI / quality (push) Successful in 1m6s
CI / e2e_tests (push) Successful in 55s
CI / typecheck (push) Successful in 1m27s
CI / security (push) Successful in 1m27s
CI / benchmark-regression (push) Failing after 39s
CI / unit_tests (push) Successful in 15m13s
CI / docker (push) Successful in 1m36s
CI / integration_tests (push) Successful in 25m29s
CI / coverage (push) Successful in 16m5s
CI / status-check (push) Successful in 2s
CI / benchmark-publish (push) Has been cancelled
Implemented test coverage and documentation updates to ensure overlay is properly recognized and communicated across the sandbox strategy validation surface.
- Verification: Confirmed that overlay already exists in the _VALID_STRATEGIES frozenset within src/cleveragents/resource/schema.py (introduced in a prior commit that closed issue #2827).
- Comprehensive audit: Audited strategy validation points to ensure consistency:
- _VALID_STRATEGIES in schema.py
- _IMPLEMENTED_STRATEGIES and _SUPPORTED_STRATEGIES in factory.py
- SandboxStrategy enum definitions in both resource.py and resource_type.py
All include overlay as an allowed strategy.
- Tests: Added a new BDD scenario titled: "Invalid sandbox_strategy error message lists overlay as allowed" to features/consolidated_resource.feature. This asserts that validation errors explicitly list overlay among the allowed values when an invalid strategy is used.
- Documentation: Updated docstring in src/cleveragents/infrastructure/sandbox/strategy_registry.py to reflect that the built-in SandboxFactory handles six strategies, including snapshot and overlay.
- Design considerations: The fix itself was already applied in a previous commit (dbf3b8d7) addressing #2827. Issue #3614, raised by the UAT bot, required additional test coverage and accurate documentation of the available strategies.
- Impact: No behavioral changes beyond ensuring test coverage and documentation are aligned with the existing implementation. The added test guarantees that error messages remain accurate and comprehensive as strategies evolve.
- Affected areas: resource/schema.py, factory.py, resource.py, resource_type.py, features/consolidated_resource.feature, strategy_registry.py.
ISSUES CLOSED: #3614
904 lines
38 KiB
Gherkin
904 lines
38 KiB
Gherkin
Feature: Consolidated Resource
|
|
Combined scenarios from: resource_handler_service_coverage, resource_handlers, resource_registry_model, resource_type_model
|
|
|
|
# ============================================================
|
|
# Originally from: resource_handler_service_coverage.feature
|
|
# Feature: ResourceHandlerService full coverage
|
|
# ============================================================
|
|
|
|
Scenario: Resolving a deferred binding raises ValueError
|
|
Given a mock sandbox manager for rhs
|
|
And a mock resource lookup for rhs
|
|
And a mock type lookup for rhs
|
|
And a ResourceHandlerService under test
|
|
Given an rhs binding that is deferred with slot name "repo"
|
|
When I resolve the rhs single binding
|
|
Then an rhs ValueError is raised with message containing "Cannot resolve deferred binding for slot 'repo'"
|
|
|
|
|
|
Scenario: Resolving a binding with no resource_id raises ValueError
|
|
Given a mock sandbox manager for rhs
|
|
And a mock resource lookup for rhs
|
|
And a mock type lookup for rhs
|
|
And a ResourceHandlerService under test
|
|
Given an rhs binding with no resource_id and slot name "data"
|
|
When I resolve the rhs single binding
|
|
Then an rhs ValueError is raised with message containing "Cannot resolve deferred binding for slot 'data'"
|
|
|
|
# --- resolve_binding: successful resolution ---
|
|
|
|
|
|
Scenario: Resolving a valid binding returns a BoundResource
|
|
Given a mock sandbox manager for rhs
|
|
And a mock resource lookup for rhs
|
|
And a mock type lookup for rhs
|
|
And a ResourceHandlerService under test
|
|
Given an rhs non-deferred binding with slot "repo" and resource_id "RES001"
|
|
And rhs resource "RES001" typed "git-checkout" located at "/tmp/repo" with strategy "none"
|
|
And an rhs type spec "git-checkout" with no handler and sandbox strategy "git_worktree"
|
|
And the rhs sandbox manager returns a sandbox with path "/sandbox/abc"
|
|
When I resolve the rhs single binding with plan_id "plan-1" and access "read_write"
|
|
Then the rhs result is a BoundResource with slot "repo" and sandbox_path "/sandbox/abc"
|
|
And the rhs BoundResource has resource_id "RES001" and access "read_write"
|
|
|
|
# --- _resolve_handler_for_type: type_spec has a handler ---
|
|
|
|
|
|
Scenario: Handler is resolved from type_spec handler reference
|
|
Given a mock sandbox manager for rhs
|
|
And a mock resource lookup for rhs
|
|
And a mock type lookup for rhs
|
|
And a ResourceHandlerService under test
|
|
Given an rhs non-deferred binding with slot "code" and resource_id "RES002"
|
|
And rhs resource "RES002" typed "custom/py" located at "/src" with strategy "none"
|
|
And an rhs type spec "custom/py" with handler "some.module:Handler" and sandbox strategy "copy_on_write"
|
|
And the rhs resolve_handler function returns a mock handler
|
|
And the rhs mock handler resolves to a BoundResource with sandbox_path "/sandbox/custom"
|
|
When I resolve the rhs single binding with plan_id "plan-2" and access "read_only"
|
|
Then the rhs result is a BoundResource with slot "code" and sandbox_path "/sandbox/custom"
|
|
|
|
# --- _resolve_handler_for_type: handler resolution fails, falls back ---
|
|
|
|
|
|
Scenario: Handler resolution failure falls back to DefaultHandler
|
|
Given a mock sandbox manager for rhs
|
|
And a mock resource lookup for rhs
|
|
And a mock type lookup for rhs
|
|
And a ResourceHandlerService under test
|
|
Given an rhs non-deferred binding with slot "files" and resource_id "RES003"
|
|
And rhs resource "RES003" typed "custom/broken" located at "/data" with strategy "copy_on_write"
|
|
And an rhs type spec "custom/broken" with handler "bad.module:Missing" and sandbox strategy "copy_on_write"
|
|
And the rhs resolve_handler function raises HandlerResolutionError
|
|
And the rhs sandbox manager returns a sandbox with path "/sandbox/fallback"
|
|
When I resolve the rhs single binding with plan_id "plan-3" and access "read_only"
|
|
Then the rhs result is a BoundResource with slot "files" and sandbox_path "/sandbox/fallback"
|
|
|
|
# --- _resolve_handler_for_type: no handler on type_spec ---
|
|
|
|
|
|
Scenario: No handler on type_spec uses DefaultHandler directly
|
|
Given a mock sandbox manager for rhs
|
|
And a mock resource lookup for rhs
|
|
And a mock type lookup for rhs
|
|
And a ResourceHandlerService under test
|
|
Given an rhs non-deferred binding with slot "dir" and resource_id "RES004"
|
|
And rhs resource "RES004" typed "fs-directory" located at "/home/user" with strategy "copy_on_write"
|
|
And an rhs type spec "fs-directory" with no handler and sandbox strategy "copy_on_write"
|
|
And the rhs sandbox manager returns a sandbox with path "/sandbox/dir"
|
|
When I resolve the rhs single binding with plan_id "plan-4" and access "read_only"
|
|
Then the rhs result is a BoundResource with slot "dir" and sandbox_path "/sandbox/dir"
|
|
|
|
# --- resolve_bindings: mixed deferred and non-deferred ---
|
|
|
|
|
|
Scenario: resolve_bindings skips deferred bindings and resolves the rest
|
|
Given a mock sandbox manager for rhs
|
|
And a mock resource lookup for rhs
|
|
And a mock type lookup for rhs
|
|
And a ResourceHandlerService under test
|
|
Given an rhs binding list with:
|
|
| slot_name | resource_id | deferred |
|
|
| repo | RES010 | false |
|
|
| param | | true |
|
|
| config | RES011 | false |
|
|
And rhs resource "RES010" typed "git-checkout" located at "/repo" with strategy "none"
|
|
And rhs resource "RES011" typed "fs-directory" located at "/config" with strategy "none"
|
|
And an rhs type spec "git-checkout" with no handler and sandbox strategy "git_worktree"
|
|
And an rhs type spec "fs-directory" with no handler and sandbox strategy "copy_on_write"
|
|
And the rhs sandbox manager returns a sandbox with path "/sandbox/multi"
|
|
When I resolve all rhs bindings with plan_id "plan-5" and access "read_only"
|
|
Then the rhs result dict has keys "repo" and "config"
|
|
And the rhs result dict does not contain key "param"
|
|
|
|
# --- resolve_bindings: failure propagation ---
|
|
|
|
|
|
Scenario: resolve_bindings raises when a non-deferred binding fails
|
|
Given a mock sandbox manager for rhs
|
|
And a mock resource lookup for rhs
|
|
And a mock type lookup for rhs
|
|
And a ResourceHandlerService under test
|
|
Given an rhs binding list with:
|
|
| slot_name | resource_id | deferred |
|
|
| broken | RES_MISSING | false |
|
|
And the rhs resource lookup raises NotFoundError for "RES_MISSING"
|
|
When I resolve all rhs bindings expecting an error
|
|
Then an rhs NotFoundError is raised
|
|
|
|
# --- resolve_bindings: all deferred returns empty dict ---
|
|
|
|
|
|
Scenario: resolve_bindings with all deferred bindings returns empty dict
|
|
Given a mock sandbox manager for rhs
|
|
And a mock resource lookup for rhs
|
|
And a mock type lookup for rhs
|
|
And a ResourceHandlerService under test
|
|
Given an rhs binding list with:
|
|
| slot_name | resource_id | deferred |
|
|
| a | | true |
|
|
| b | | true |
|
|
When I resolve all rhs bindings with plan_id "plan-6" and access "read_only"
|
|
Then the rhs result dict is empty
|
|
|
|
# --- resolve_resource: direct resolution ---
|
|
|
|
|
|
Scenario: resolve_resource resolves a resource directly without a binding
|
|
Given a mock sandbox manager for rhs
|
|
And a mock resource lookup for rhs
|
|
And a mock type lookup for rhs
|
|
And a ResourceHandlerService under test
|
|
Given rhs resource "RES020" typed "git-checkout" located at "/direct/repo" with strategy "none"
|
|
And an rhs type spec "git-checkout" with no handler and sandbox strategy "git_worktree"
|
|
And the rhs sandbox manager returns a sandbox with path "/sandbox/direct"
|
|
When I resolve the rhs resource directly with plan_id "plan-7" slot "src" and access "read_write"
|
|
Then the rhs result is a BoundResource with slot "src" and sandbox_path "/sandbox/direct"
|
|
And the rhs BoundResource has resource_id "RES020" and access "read_write"
|
|
|
|
# --- _DefaultHandler: resource sandbox_strategy overrides type ---
|
|
|
|
|
|
Scenario: DefaultHandler uses resource sandbox_strategy when present
|
|
Given a mock sandbox manager for rhs
|
|
And a mock resource lookup for rhs
|
|
And a mock type lookup for rhs
|
|
And a ResourceHandlerService under test
|
|
Given an rhs non-deferred binding with slot "db" and resource_id "RES030"
|
|
And rhs resource "RES030" typed "custom/db" located at "/data/db" with strategy "snapshot"
|
|
And an rhs type spec "custom/db" with no handler and sandbox strategy "copy_on_write"
|
|
And the rhs sandbox manager returns a sandbox with path "/sandbox/snap"
|
|
When I resolve the rhs single binding with plan_id "plan-8" and access "read_only"
|
|
Then the rhs sandbox manager was called with strategy "snapshot"
|
|
|
|
# --- _DefaultHandler: falls back to type_spec sandbox_strategy ---
|
|
|
|
|
|
Scenario: DefaultHandler falls back to type_spec sandbox_strategy when resource has none
|
|
Given a mock sandbox manager for rhs
|
|
And a mock resource lookup for rhs
|
|
And a mock type lookup for rhs
|
|
And a ResourceHandlerService under test
|
|
Given an rhs non-deferred binding with slot "fs" and resource_id "RES031"
|
|
And rhs resource "RES031" typed "fs-directory" located at "/mnt/share" with strategy "none"
|
|
And an rhs type spec "fs-directory" with no handler and sandbox strategy "copy_on_write"
|
|
And the rhs sandbox manager returns a sandbox with path "/sandbox/cow"
|
|
When I resolve the rhs single binding with plan_id "plan-9" and access "read_only"
|
|
Then the rhs sandbox manager was called with strategy "copy_on_write"
|
|
|
|
# --- _DefaultHandler: no location error ---
|
|
|
|
|
|
Scenario: DefaultHandler raises ValueError when resource has no location
|
|
Given a mock sandbox manager for rhs
|
|
And a mock resource lookup for rhs
|
|
And a mock type lookup for rhs
|
|
And a ResourceHandlerService under test
|
|
Given an rhs non-deferred binding with slot "empty" and resource_id "RES040"
|
|
And rhs resource "RES040" typed "fs-directory" with no location
|
|
And an rhs type spec "fs-directory" with no handler and sandbox strategy "copy_on_write"
|
|
When I resolve the rhs single binding
|
|
Then an rhs ValueError is raised with message containing "has no location"
|
|
|
|
# --- _DefaultHandler: sandbox context is None ---
|
|
|
|
|
|
Scenario: DefaultHandler raises RuntimeError when sandbox has no context
|
|
Given a mock sandbox manager for rhs
|
|
And a mock resource lookup for rhs
|
|
And a mock type lookup for rhs
|
|
And a ResourceHandlerService under test
|
|
Given an rhs non-deferred binding with slot "ctx" and resource_id "RES050"
|
|
And rhs resource "RES050" typed "fs-directory" located at "/tmp/ctx" with strategy "none"
|
|
And an rhs type spec "fs-directory" with no handler and sandbox strategy "copy_on_write"
|
|
And the rhs sandbox manager returns a sandbox with no context
|
|
When I resolve the rhs single binding
|
|
Then an rhs RuntimeError is raised with message containing "has no context"
|
|
|
|
|
|
# ============================================================
|
|
# Originally from: resource_handlers.feature
|
|
# Feature: Resource handler runtime
|
|
# ============================================================
|
|
|
|
Scenario: GitCheckoutHandler satisfies ResourceHandler protocol
|
|
Then GitCheckoutHandler should satisfy the ResourceHandler protocol
|
|
|
|
|
|
Scenario: FsDirectoryHandler satisfies ResourceHandler protocol
|
|
Then FsDirectoryHandler should satisfy the ResourceHandler protocol
|
|
|
|
# === GitCheckoutHandler ===
|
|
|
|
|
|
Scenario: GitCheckoutHandler resolves a git-checkout resource
|
|
Given a git-checkout resource with location "/tmp/test-repo"
|
|
And a sandbox manager with a mock factory
|
|
When I resolve the resource with GitCheckoutHandler for plan "PLAN001"
|
|
Then the bound resource should have slot name "repo"
|
|
And the bound resource should have resource type "git-checkout"
|
|
And the bound resource sandbox path should not be empty
|
|
|
|
|
|
Scenario: GitCheckoutHandler uses resource-level strategy override
|
|
Given a git-checkout resource at "/tmp/test-repo" using strategy "copy_on_write"
|
|
And a sandbox manager with a mock factory
|
|
When I resolve the resource with GitCheckoutHandler for plan "PLAN002"
|
|
Then the sandbox was created with strategy "copy_on_write"
|
|
|
|
|
|
Scenario: GitCheckoutHandler rejects resource without location
|
|
Given a git-checkout resource without location
|
|
And a sandbox manager with a mock factory
|
|
When I try to resolve the resource with GitCheckoutHandler
|
|
Then a handler ValueError should be raised
|
|
And the handler error should mention "no location"
|
|
|
|
# === FsDirectoryHandler ===
|
|
|
|
|
|
Scenario: FsDirectoryHandler resolves an fs-directory resource
|
|
Given an fs-directory resource with location "/tmp/test-dir"
|
|
And a sandbox manager with a mock factory
|
|
When I resolve the resource with FsDirectoryHandler for plan "PLAN003"
|
|
Then the bound resource should have slot name "workdir"
|
|
And the bound resource should have resource type "fs-directory"
|
|
And the bound resource sandbox path should not be empty
|
|
|
|
|
|
Scenario: FsDirectoryHandler uses copy_on_write by default
|
|
Given an fs-directory resource with location "/tmp/test-dir"
|
|
And a sandbox manager with a mock factory
|
|
When I resolve the resource with FsDirectoryHandler for plan "PLAN004"
|
|
Then the sandbox was created with strategy "copy_on_write"
|
|
|
|
|
|
Scenario: FsDirectoryHandler rejects resource without location
|
|
Given an fs-directory resource without location
|
|
And a sandbox manager with a mock factory
|
|
When I try to resolve the resource with FsDirectoryHandler
|
|
Then a handler ValueError should be raised
|
|
|
|
# === Handler Resolver ===
|
|
|
|
|
|
Scenario: Resolve handler from valid module:class string
|
|
When I resolve handler "cleveragents.resource.handlers.git_checkout:GitCheckoutHandler"
|
|
Then the resolved handler should be a GitCheckoutHandler instance
|
|
|
|
|
|
Scenario: Resolve handler from fs-directory reference
|
|
When I resolve handler "cleveragents.resource.handlers.fs_directory:FsDirectoryHandler"
|
|
Then the resolved handler should be a FsDirectoryHandler instance
|
|
|
|
|
|
Scenario: Resolve handler caches instances
|
|
When I resolve handler "cleveragents.resource.handlers.git_checkout:GitCheckoutHandler"
|
|
And I resolve the same handler reference again
|
|
Then both resolved handlers should be the same object
|
|
|
|
|
|
Scenario: Resolve handler rejects empty string
|
|
When I try to resolve an empty handler reference
|
|
Then a HandlerResolutionError should be raised
|
|
And the resolution error should mention "empty"
|
|
|
|
|
|
Scenario: Resolve handler rejects missing colon separator
|
|
When I try to resolve handler "cleveragents.resource.handlers.git_checkout"
|
|
Then a HandlerResolutionError should be raised
|
|
And the resolution error should mention "module.path:ClassName"
|
|
|
|
|
|
Scenario: Resolve handler rejects nonexistent module
|
|
When I try to resolve handler "nonexistent.module:SomeClass"
|
|
Then a HandlerResolutionError should be raised
|
|
And the resolution error should mention "Cannot import"
|
|
|
|
|
|
Scenario: Resolve handler rejects nonexistent class in valid module
|
|
When I try to resolve handler "cleveragents.resource.handlers.git_checkout:NonExistentClass"
|
|
Then a HandlerResolutionError should be raised
|
|
And the resolution error should mention "not found"
|
|
|
|
# === ResourceHandlerService ===
|
|
|
|
|
|
Scenario: ResourceHandlerService resolves a binding to BoundResource
|
|
Given a resource handler service with mock lookups
|
|
And a binding result for slot "repo" with resource id "RES001"
|
|
When I resolve the binding via resource handler service for plan "PLAN010"
|
|
Then the handler service should return a BoundResource
|
|
And the handler service BoundResource slot should be "repo"
|
|
And the handler service BoundResource sandbox path should not be empty
|
|
|
|
|
|
Scenario: ResourceHandlerService skips deferred bindings
|
|
Given a resource handler service with mock lookups
|
|
And a deferred binding result for slot "extra"
|
|
And a binding result for slot "repo" with resource id "RES001"
|
|
When I resolve all bindings via resource handler service for plan "PLAN011"
|
|
Then the handler service should return 1 bound resource
|
|
And the handler service should have resolved slot "repo"
|
|
|
|
|
|
Scenario: ResourceHandlerService rejects deferred single binding
|
|
Given a resource handler service with mock lookups
|
|
And a deferred binding result for slot "extra"
|
|
When I try to resolve the single deferred binding via resource handler service
|
|
Then a handler ValueError should be raised
|
|
And the handler error should mention "deferred"
|
|
|
|
|
|
Scenario: ResourceHandlerService uses fallback handler when type has no handler string
|
|
Given a resource handler service with mock lookups and no handler string
|
|
And a binding result for slot "data" with resource id "RES002"
|
|
When I resolve the binding via resource handler service for plan "PLAN012"
|
|
Then the handler service should return a BoundResource
|
|
And the handler service BoundResource sandbox path should not be empty
|
|
|
|
|
|
# ============================================================
|
|
# Originally from: resource_registry_model.feature
|
|
# Feature: Resource Registry Domain Model
|
|
# ============================================================
|
|
|
|
Scenario: PhysVirt has exactly two values
|
|
Then the PhysVirt enum should have values "physical, virtual"
|
|
|
|
|
|
Scenario: PhysVirt physical value
|
|
Given a PhysVirt value of "physical"
|
|
Then the PhysVirt string value should be "physical"
|
|
|
|
|
|
Scenario: PhysVirt virtual value
|
|
Given a PhysVirt value of "virtual"
|
|
Then the PhysVirt string value should be "virtual"
|
|
|
|
|
|
Scenario: PhysVirt rejects invalid value
|
|
When I try to create a PhysVirt with value "hybrid"
|
|
Then a resource validation error should be raised
|
|
|
|
# SandboxStrategy enum
|
|
|
|
|
|
Scenario: SandboxStrategy has exactly six values
|
|
Then the SandboxStrategy enum should have values "git_worktree, copy_on_write, transaction_rollback, snapshot, overlay, none"
|
|
|
|
|
|
Scenario Outline: SandboxStrategy accepts valid values
|
|
Given a SandboxStrategy value of "<strategy>"
|
|
Then the SandboxStrategy string value should be "<strategy>"
|
|
|
|
Examples:
|
|
| strategy |
|
|
| git_worktree |
|
|
| copy_on_write |
|
|
| transaction_rollback |
|
|
| snapshot |
|
|
| overlay |
|
|
| none |
|
|
|
|
|
|
Scenario: SandboxStrategy accepts overlay
|
|
Given a SandboxStrategy value of "overlay"
|
|
Then the SandboxStrategy string value should be "overlay"
|
|
|
|
|
|
Scenario: SandboxStrategy rejects versioning
|
|
When I try to create a SandboxStrategy with value "versioning"
|
|
Then a resource validation error should be raised
|
|
|
|
# ResourceCapabilities model
|
|
|
|
|
|
Scenario: ResourceCapabilities defaults
|
|
Given default ResourceCapabilities
|
|
Then the capability readable should be true
|
|
And the capability writable should be true
|
|
And the capability sandboxable should be true
|
|
And the capability checkpointable should be true
|
|
|
|
|
|
Scenario: ResourceCapabilities with custom values
|
|
Given ResourceCapabilities with readable false and checkpointable true
|
|
Then the capability readable should be false
|
|
And the capability writable should be true
|
|
And the capability sandboxable should be true
|
|
And the capability checkpointable should be true
|
|
|
|
|
|
Scenario: ResourceCapabilities is frozen
|
|
Given default ResourceCapabilities
|
|
When I try to mutate a ResourceCapabilities field
|
|
Then a resource validation error should be raised
|
|
|
|
# Resource model - creation
|
|
|
|
|
|
Scenario: Create a minimal physical resource
|
|
Given a Resource with resource_id "01HZQX7K8M3N4P5R6S7T8V9W0X" type "git-checkout" and classification "physical"
|
|
Then the resource resource_id should be "01HZQX7K8M3N4P5R6S7T8V9W0X"
|
|
And the resource type name should be "git-checkout"
|
|
And the resource classification should be "physical"
|
|
And the resource name should be empty
|
|
And the resource description should be empty
|
|
And the resource properties should be empty
|
|
And the resource parents should be empty
|
|
And the resource children should be empty
|
|
And the resource linked_projects should be empty
|
|
And the resource created_at should be set
|
|
And the resource updated_at should be set
|
|
|
|
|
|
Scenario: Create a full resource with all fields
|
|
Given a full Resource with id "01HZQX7K8M3N4P5R6S7T8V9W0X" name "local/my-repo" type "git-checkout" classification "physical" description "Main repo"
|
|
Then the resource name should be "local/my-repo"
|
|
And the resource description should be "Main repo"
|
|
|
|
|
|
Scenario: Resource with sandbox strategy override
|
|
Given a Resource with sandbox strategy "git_worktree"
|
|
Then the resource sandbox strategy should be "git_worktree"
|
|
|
|
|
|
Scenario: Resource with non-sandboxable rejects checkpointable
|
|
When I try to create a Resource with sandboxable false and checkpointable true
|
|
Then a resource validation error should be raised
|
|
|
|
# Resource model - validation
|
|
|
|
|
|
Scenario: Resource rejects invalid ULID
|
|
When I try to create a Resource with resource_id "invalid-ulid"
|
|
Then a resource validation error should be raised
|
|
|
|
|
|
Scenario: Resource rejects empty resource_type_name
|
|
When I try to create a Resource with empty resource_type_name
|
|
Then a resource validation error should be raised
|
|
|
|
|
|
Scenario: Resource rejects empty string name
|
|
When I try to create a Resource with empty string name
|
|
Then a resource validation error should be raised
|
|
|
|
|
|
Scenario: Resource accepts None name
|
|
Given a Resource with resource_id "01HZQX7K8M3N4P5R6S7T8V9W0X" type "git-checkout" and classification "physical"
|
|
Then the resource name should be empty
|
|
|
|
# Resource model - domain properties
|
|
|
|
|
|
Scenario: Physical resource is_physical
|
|
Given a Resource with resource_id "01HZQX7K8M3N4P5R6S7T8V9W0X" type "git-checkout" and classification "physical"
|
|
Then the resource is_physical should be true
|
|
And the resource is_virtual should be false
|
|
|
|
|
|
Scenario: Virtual resource is_virtual
|
|
Given a Resource with resource_id "01HZQX7K8M3N4P5R6S7T8V9W0X" type "git" and classification "virtual"
|
|
Then the resource is_physical should be false
|
|
And the resource is_virtual should be true
|
|
|
|
|
|
Scenario: Read-only resource
|
|
Given a Resource with writable false
|
|
Then the resource is_read_only should be true
|
|
|
|
|
|
Scenario: Writable resource
|
|
Given a Resource with resource_id "01HZQX7K8M3N4P5R6S7T8V9W0X" type "git-checkout" and classification "physical"
|
|
Then the resource is_read_only should be false
|
|
|
|
# Resource model - frozen
|
|
|
|
|
|
Scenario: Resource is frozen
|
|
Given a Resource with resource_id "01HZQX7K8M3N4P5R6S7T8V9W0X" type "git-checkout" and classification "physical"
|
|
When I try to mutate a Resource field
|
|
Then a resource validation error should be raised
|
|
|
|
# Resource model - with_updated_at
|
|
|
|
|
|
Scenario: with_updated_at returns new instance
|
|
Given a Resource with resource_id "01HZQX7K8M3N4P5R6S7T8V9W0X" type "git-checkout" and classification "physical"
|
|
When I call with_updated_at on the resource
|
|
Then the new resource should have a different updated_at
|
|
And the new resource should have the same resource_id
|
|
|
|
# Resource model - content_hash and location
|
|
|
|
|
|
Scenario: Resource with content_hash
|
|
Given a Resource with content_hash "sha256:abc123"
|
|
Then the resource content_hash should be "sha256:abc123"
|
|
|
|
|
|
Scenario: Resource with location
|
|
Given a Resource with location "/home/user/repos/main"
|
|
Then the resource location should be "/home/user/repos/main"
|
|
|
|
|
|
Scenario: Resource rejects empty location string
|
|
When I try to create a Resource with empty location
|
|
Then a resource validation error should be raised
|
|
|
|
# Resource model - parents and children
|
|
|
|
|
|
Scenario: Resource with parents and children
|
|
Given a Resource with parents and children
|
|
Then the resource should have 1 parent
|
|
And the resource should have 2 children
|
|
|
|
|
|
# ============================================================
|
|
# Originally from: resource_type_model.feature
|
|
# Feature: Resource Type Model and Schema Loader
|
|
# ============================================================
|
|
|
|
Scenario: Built-in resource type with unnamespaced name
|
|
Given a resource type config with name "git-checkout" and built_in true
|
|
When I create a ResourceTypeSpec from config
|
|
Then the resource type should be valid
|
|
And the rt_spec name should be "git-checkout"
|
|
|
|
|
|
Scenario: Custom resource type with namespaced name
|
|
Given a resource type config with name "myorg/custom-db" and built_in false
|
|
When I create a ResourceTypeSpec from config
|
|
Then the resource type should be valid
|
|
And the rt_spec name should be "myorg/custom-db"
|
|
|
|
|
|
Scenario: Custom type without namespace is rejected
|
|
Given a resource type config with name "no-namespace" and built_in false
|
|
When I try to create a ResourceTypeSpec from config
|
|
Then the resource type creation should fail with "namespace/name"
|
|
|
|
|
|
Scenario: Invalid name with special characters is rejected
|
|
Given a resource type config with name "bad!name" and built_in false
|
|
When I try to create a ResourceTypeSpec from config
|
|
Then the resource type creation should fail with "Invalid resource type name"
|
|
|
|
# ── ResourceKind ────────────────────────────────────────────
|
|
|
|
|
|
Scenario: Physical resource kind
|
|
Given a resource type config with resource_kind "physical"
|
|
When I create a ResourceTypeSpec from config
|
|
Then the resource type resource_kind should be "physical"
|
|
|
|
|
|
Scenario: Virtual resource kind
|
|
Given a resource type config with resource_kind "virtual" and equivalence set
|
|
When I create a ResourceTypeSpec from config
|
|
Then the resource type resource_kind should be "virtual"
|
|
|
|
|
|
Scenario: Virtual resource kind without equivalence is rejected
|
|
Given a resource type config with resource_kind "virtual" and no equivalence
|
|
When I try to create a ResourceTypeSpec from config
|
|
Then the resource type creation should fail with "equivalence"
|
|
|
|
# ── SandboxStrategy ─────────────────────────────────────────
|
|
|
|
|
|
Scenario: Git worktree sandbox strategy
|
|
Given a resource type config with sandbox_strategy "git_worktree"
|
|
When I create a ResourceTypeSpec from config
|
|
Then the resource type sandbox_strategy should be "git_worktree"
|
|
|
|
|
|
Scenario: Copy on write sandbox strategy
|
|
Given a resource type config with sandbox_strategy "copy_on_write"
|
|
When I create a ResourceTypeSpec from config
|
|
Then the resource type sandbox_strategy should be "copy_on_write"
|
|
|
|
|
|
Scenario: Transaction rollback sandbox strategy
|
|
Given a resource type config with sandbox_strategy "transaction_rollback"
|
|
When I create a ResourceTypeSpec from config
|
|
Then the resource type sandbox_strategy should be "transaction_rollback"
|
|
|
|
|
|
Scenario: Snapshot sandbox strategy
|
|
Given a resource type config with sandbox_strategy "snapshot"
|
|
When I create a ResourceTypeSpec from config
|
|
Then the resource type sandbox_strategy should be "snapshot"
|
|
|
|
|
|
Scenario: None sandbox strategy
|
|
Given a resource type config with sandbox_strategy "none"
|
|
When I create a ResourceTypeSpec from config
|
|
Then the resource type sandbox_strategy should be "none"
|
|
|
|
# ── CLI Arguments ───────────────────────────────────────────
|
|
|
|
|
|
Scenario: Valid CLI argument parsing
|
|
Given a resource type config with a cli_arg named "path" of type "path"
|
|
When I create a ResourceTypeSpec from config
|
|
Then the resource type should have 1 cli_arg
|
|
And the first cli_arg name should be "path"
|
|
|
|
|
|
Scenario: Multiple CLI arguments
|
|
Given a resource type config with cli_args "path" and "branch"
|
|
When I create a ResourceTypeSpec from config
|
|
Then the resource type should have 2 cli_args
|
|
|
|
|
|
Scenario: CLI argument with invalid name is rejected
|
|
Given a resource type config with a cli_arg named "BadName" of type "string"
|
|
When I try to create a ResourceTypeSpec from config
|
|
Then the resource type creation should fail with "not CLI-safe"
|
|
|
|
|
|
Scenario: CLI argument with invalid type is rejected
|
|
Given a resource type config with a cli_arg named "count" of type "bigint"
|
|
When I try to create a ResourceTypeSpec from config
|
|
Then the resource type creation should fail with "Invalid argument type"
|
|
|
|
# ── Parent/Child Types ──────────────────────────────────────
|
|
|
|
|
|
Scenario: Resource type with parent types
|
|
Given a resource type config with parent_types "git-checkout"
|
|
When I create a ResourceTypeSpec from config
|
|
Then the resource type should have parent_types containing "git-checkout"
|
|
|
|
|
|
Scenario: Resource type with child types
|
|
Given a resource type config with child_types "fs-directory"
|
|
When I create a ResourceTypeSpec from config
|
|
Then the resource type should have child_types containing "fs-directory"
|
|
|
|
|
|
Scenario: Resource type with both parent and child types
|
|
Given a resource type config with parent_types_set "git-checkout" and child_types_set "fs-directory"
|
|
When I create a ResourceTypeSpec from config
|
|
Then the resource type should have parent_types containing "git-checkout"
|
|
And the resource type should have child_types containing "fs-directory"
|
|
|
|
# ── from_config() ───────────────────────────────────────────
|
|
|
|
|
|
Scenario: from_config with missing name
|
|
Given a resource type config dict without name
|
|
When I try to call from_config
|
|
Then from_config should fail with "must include 'name'"
|
|
|
|
|
|
Scenario: from_config with missing resource_kind
|
|
Given a resource type config dict without resource_kind
|
|
When I try to call from_config
|
|
Then from_config should fail with "must include 'resource_kind'"
|
|
|
|
|
|
Scenario: from_config with missing sandbox_strategy
|
|
Given a resource type config dict without sandbox_strategy
|
|
When I try to call from_config
|
|
Then from_config should fail with "must include 'sandbox_strategy'"
|
|
|
|
|
|
Scenario: from_config with valid full config
|
|
Given a full resource type config dict
|
|
When I call from_config
|
|
Then the resource type spec should be valid
|
|
|
|
# ── as_cli_dict() ───────────────────────────────────────────
|
|
|
|
|
|
Scenario: as_cli_dict returns expected keys
|
|
Given a resource type config with name "myorg/test-type" and built_in false
|
|
When I create a ResourceTypeSpec and call as_cli_dict
|
|
Then the rt_cli dict should contain key "name" with value "myorg/test-type"
|
|
And the rt_cli dict should contain key "resource_kind"
|
|
And the rt_cli dict should contain key "sandbox_strategy"
|
|
And the rt_cli dict should contain key "capabilities"
|
|
|
|
# ── Schema loader ───────────────────────────────────────────
|
|
|
|
|
|
Scenario: Schema loader from YAML string
|
|
Given a valid resource type YAML string
|
|
When I load the resource type via from_yaml
|
|
Then the loaded schema should be valid
|
|
|
|
|
|
Scenario: Schema loader from YAML file
|
|
Given a valid resource type YAML file
|
|
When I load the resource type via from_yaml_file
|
|
Then the loaded schema should be valid
|
|
|
|
|
|
Scenario: Schema loader with env var interpolation
|
|
Given a resource type YAML with env var "${RT_TEST_HANDLER}"
|
|
And the env var "RT_TEST_HANDLER" is set to "test.handler:Handler"
|
|
When I load the resource type via from_yaml
|
|
Then the handler should be "test.handler:Handler"
|
|
|
|
|
|
Scenario: Schema loader with empty YAML
|
|
When I try to load an empty YAML string
|
|
Then the schema loader should fail with "empty"
|
|
|
|
|
|
Scenario: Schema loader with None YAML
|
|
When I try to load a None YAML string
|
|
Then the schema loader should fail with "None"
|
|
|
|
|
|
Scenario: Schema loader with non-mapping YAML
|
|
When I try to load a YAML list
|
|
Then the schema loader should fail with "mapping"
|
|
|
|
|
|
Scenario: Schema loader with unsupported version
|
|
Given a resource type YAML with schema_version "99"
|
|
When I try to load the resource type via from_yaml
|
|
Then the schema loader should fail with "Unsupported schema version"
|
|
|
|
|
|
Scenario: Schema loader from non-existent file
|
|
When I try to load from a non-existent file
|
|
Then the schema loader should raise FileNotFoundError
|
|
|
|
|
|
Scenario: Schema loader from directory instead of file
|
|
When I try to load from a directory path
|
|
Then the schema loader should fail with "not a file"
|
|
|
|
# ── Capabilities ────────────────────────────────────────────
|
|
|
|
|
|
Scenario: Default capabilities
|
|
Given a resource type config with default capabilities
|
|
When I create a ResourceTypeSpec from config
|
|
Then the capabilities should have read true
|
|
And the capabilities should have write true
|
|
And the capabilities should have sandbox true
|
|
And the capabilities should have checkpoint false
|
|
|
|
|
|
Scenario: Custom capabilities
|
|
Given a resource type config with custom capabilities
|
|
When I create a ResourceTypeSpec from config
|
|
Then the capabilities should have read true
|
|
And the capabilities should have write false
|
|
And the capabilities should have sandbox true
|
|
And the capabilities should have checkpoint true
|
|
|
|
|
|
Scenario: Unknown capability key is rejected
|
|
Given a resource type config with unknown capability "teleport"
|
|
When I try to create a ResourceTypeSpec from config
|
|
Then the resource type creation should fail with "Unknown capability"
|
|
|
|
# ── CLI argument edge cases ──────────────────────────────────
|
|
|
|
|
|
Scenario: cli_args entry that is not a mapping is rejected
|
|
Given a resource type config with a non-mapping cli_arg
|
|
When I try to create a ResourceTypeSpec from config
|
|
Then the resource type creation should fail with "must be a mapping"
|
|
|
|
# ── as_cli_dict coverage ──────────────────────────────────────
|
|
|
|
|
|
Scenario: as_cli_dict includes all optional fields when present
|
|
Given a full resource type config with handler and auto_discovery
|
|
When I create a ResourceTypeSpec and call as_cli_dict
|
|
Then the rt_cli dict should contain key "description"
|
|
And the rt_cli dict should contain key "cli_args"
|
|
And the rt_cli dict should contain key "child_types"
|
|
And the rt_cli dict should contain key "handler"
|
|
And the rt_cli dict should contain key "auto_discovery"
|
|
And the rt_cli dict should contain key "capabilities"
|
|
|
|
|
|
Scenario: as_cli_dict with equivalence field
|
|
Given a resource type config with resource_kind "virtual" and equivalence set
|
|
When I create a ResourceTypeSpec and call as_cli_dict
|
|
Then the rt_cli dict should contain key "equivalence"
|
|
|
|
|
|
Scenario: as_cli_dict with parent_types
|
|
Given a resource type config with parent_types "git-checkout"
|
|
When I create a ResourceTypeSpec and call as_cli_dict
|
|
Then the rt_cli dict should contain key "parent_types"
|
|
|
|
# ── Schema-level validation ───────────────────────────────────
|
|
|
|
|
|
Scenario: Schema rejects invalid cli_arg name
|
|
Given a resource type YAML with invalid cli_arg name
|
|
When I try to load the resource type via from_yaml
|
|
Then the schema loader should fail with "not CLI-safe"
|
|
|
|
|
|
Scenario: Schema rejects invalid cli_arg type
|
|
Given a resource type YAML with invalid cli_arg type
|
|
When I try to load the resource type via from_yaml
|
|
Then the schema loader should fail with "Invalid argument type"
|
|
|
|
|
|
Scenario: Schema rejects invalid resource type name
|
|
Given a resource type YAML with invalid name
|
|
When I try to load the resource type via from_yaml
|
|
Then the schema loader should fail with "Invalid resource type name"
|
|
|
|
|
|
Scenario: Schema rejects invalid resource_kind
|
|
Given a resource type YAML with invalid resource_kind
|
|
When I try to load the resource type via from_yaml
|
|
Then the schema loader should fail with "Invalid resource_kind"
|
|
|
|
|
|
Scenario: Schema rejects invalid sandbox_strategy
|
|
Given a resource type YAML with invalid sandbox_strategy
|
|
When I try to load the resource type via from_yaml
|
|
Then the schema loader should fail with "Invalid sandbox_strategy"
|
|
|
|
|
|
Scenario: Schema rejects custom type without namespace
|
|
Given a resource type YAML with custom type no namespace
|
|
When I try to load the resource type via from_yaml
|
|
Then the schema loader should fail with "namespace/name"
|
|
|
|
|
|
Scenario: Schema rejects virtual type without equivalence
|
|
Given a resource type YAML with virtual kind no equivalence
|
|
When I try to load the resource type via from_yaml
|
|
Then the schema loader should fail with "equivalence"
|
|
|
|
|
|
Scenario: Schema loader from_yaml_file with None path
|
|
When I try to load from a None file path
|
|
Then the schema loader should fail with "None"
|
|
|
|
|
|
Scenario: Schema env var interpolation in list items
|
|
Given a resource type YAML with env var in list
|
|
And the env var "RT_TEST_PARENT" is set to "custom-parent"
|
|
When I load the resource type via from_yaml
|
|
Then the loaded schema should have parent type "custom-parent"
|
|
|
|
|
|
Scenario: Schema accepts overlay sandbox strategy
|
|
Given a resource type YAML with sandbox_strategy "overlay"
|
|
When I load the resource type via from_yaml
|
|
Then the loaded schema sandbox_strategy should be "overlay"
|
|
|
|
|
|
Scenario: Invalid sandbox_strategy error message lists overlay as allowed
|
|
Given a resource type YAML with invalid sandbox_strategy
|
|
When I try to load the resource type via from_yaml
|
|
Then the schema loader should fail with "overlay"
|