Files
cleveragents-core/features/steps/resource_model_steps.py
2026-02-13 21:12:54 +00:00

559 lines
19 KiB
Python

"""Step definitions for Resource domain model tests (B1.2, B1.3, B1.4)."""
from __future__ import annotations
from pathlib import Path
from behave import given, then, when
from behave.runner import Context
from pydantic import ValidationError
from cleveragents.domain.models.core.resource import (
Resource,
ResourceType,
SandboxStrategy,
)
# ResourceType Steps
@then('the resource types should be "{expected_order}"')
def step_check_resource_type_values(context: Context, expected_order: str) -> None:
"""Verify that ResourceType has all expected values in order."""
expected = [v.strip() for v in expected_order.split(",")]
actual = [rt.value for rt in ResourceType]
assert actual == expected, f"Expected resource types {expected}, got {actual}"
@when("I access ResourceType.GIT_REPOSITORY")
def step_access_git_repository(context: Context) -> None:
"""Access the GIT_REPOSITORY enum member."""
context.resource_type = ResourceType.GIT_REPOSITORY
@when("I access ResourceType.FILESYSTEM")
def step_access_filesystem(context: Context) -> None:
"""Access the FILESYSTEM enum member."""
context.resource_type = ResourceType.FILESYSTEM
@when("I access ResourceType.DATABASE")
def step_access_database(context: Context) -> None:
"""Access the DATABASE enum member."""
context.resource_type = ResourceType.DATABASE
@when("I access ResourceType.API_ENDPOINT")
def step_access_api_endpoint(context: Context) -> None:
"""Access the API_ENDPOINT enum member."""
context.resource_type = ResourceType.API_ENDPOINT
@when("I access ResourceType.DOCUMENT_CORPUS")
def step_access_document_corpus(context: Context) -> None:
"""Access the DOCUMENT_CORPUS enum member."""
context.resource_type = ResourceType.DOCUMENT_CORPUS
@when("I access ResourceType.CLOUD_INFRASTRUCTURE")
def step_access_cloud_infrastructure(context: Context) -> None:
"""Access the CLOUD_INFRASTRUCTURE enum member."""
context.resource_type = ResourceType.CLOUD_INFRASTRUCTURE
@then('the resource type value should be "{expected}"')
def step_check_resource_type_value(context: Context, expected: str) -> None:
"""Check the resource type value matches expected."""
actual = context.resource_type.value
assert actual == expected, f"Expected value '{expected}', got '{actual}'"
@then("the resource type should be a string")
def step_check_resource_type_is_string(context: Context) -> None:
"""Verify ResourceType inherits from str."""
assert isinstance(context.resource_type, str), (
f"Expected ResourceType to be a string, got {type(context.resource_type)}"
)
@when('I create a ResourceType from string "{value}"')
def step_create_resource_type_from_string(context: Context, value: str) -> None:
"""Create a ResourceType from a string value."""
context.error = None
try:
context.resource_type = ResourceType(value)
except ValueError as e:
context.error = e
@when('I try to create a ResourceType from string "{value}"')
def step_try_create_resource_type_from_string(context: Context, value: str) -> None:
"""Attempt to create a ResourceType from an invalid string."""
context.error = None
try:
context.resource_type = ResourceType(value)
except ValueError as e:
context.error = e
@then("a ValueError should be raised")
def step_check_value_error_raised(context: Context) -> None:
"""Verify a ValueError was captured."""
assert context.error is not None, "Expected a ValueError but none was raised"
assert isinstance(context.error, ValueError), (
f"Expected ValueError, got {type(context.error).__name__}"
)
# SandboxStrategy Steps
@then('the sandbox strategies should be "{expected_order}"')
def step_check_sandbox_strategy_values(context: Context, expected_order: str) -> None:
"""Verify that SandboxStrategy has all expected values in order."""
expected = [v.strip() for v in expected_order.split(",")]
actual = [ss.value for ss in SandboxStrategy]
assert actual == expected, f"Expected sandbox strategies {expected}, got {actual}"
@when("I access SandboxStrategy.GIT_WORKTREE")
def step_access_git_worktree(context: Context) -> None:
"""Access the GIT_WORKTREE enum member."""
context.sandbox_strategy = SandboxStrategy.GIT_WORKTREE
@when("I access SandboxStrategy.COPY_ON_WRITE")
def step_access_copy_on_write(context: Context) -> None:
"""Access the COPY_ON_WRITE enum member."""
context.sandbox_strategy = SandboxStrategy.COPY_ON_WRITE
@when("I access SandboxStrategy.OVERLAY")
def step_access_overlay(context: Context) -> None:
"""Access the OVERLAY enum member."""
context.sandbox_strategy = SandboxStrategy.OVERLAY
@when("I access SandboxStrategy.TRANSACTION_ROLLBACK")
def step_access_transaction_rollback(context: Context) -> None:
"""Access the TRANSACTION_ROLLBACK enum member."""
context.sandbox_strategy = SandboxStrategy.TRANSACTION_ROLLBACK
@when("I access SandboxStrategy.VERSIONING")
def step_access_versioning(context: Context) -> None:
"""Access the VERSIONING enum member."""
context.sandbox_strategy = SandboxStrategy.VERSIONING
@when("I access SandboxStrategy.NONE")
def step_access_none_strategy(context: Context) -> None:
"""Access the NONE enum member."""
context.sandbox_strategy = SandboxStrategy.NONE
@then('the sandbox strategy value should be "{expected}"')
def step_check_sandbox_strategy_value(context: Context, expected: str) -> None:
"""Check the sandbox strategy value matches expected."""
actual = context.sandbox_strategy.value
assert actual == expected, f"Expected value '{expected}', got '{actual}'"
@then("the sandbox strategy should be a string")
def step_check_sandbox_strategy_is_string(context: Context) -> None:
"""Verify SandboxStrategy inherits from str."""
assert isinstance(context.sandbox_strategy, str), (
f"Expected SandboxStrategy to be a string, got {type(context.sandbox_strategy)}"
)
# SandboxStrategy.supports_rollback Tests
@then("SandboxStrategy.GIT_WORKTREE should support rollback")
def step_git_worktree_supports_rollback(context: Context) -> None:
"""Verify GIT_WORKTREE supports rollback."""
assert SandboxStrategy.GIT_WORKTREE.supports_rollback is True
@then("SandboxStrategy.COPY_ON_WRITE should support rollback")
def step_copy_on_write_supports_rollback(context: Context) -> None:
"""Verify COPY_ON_WRITE supports rollback."""
assert SandboxStrategy.COPY_ON_WRITE.supports_rollback is True
@then("SandboxStrategy.OVERLAY should support rollback")
def step_overlay_supports_rollback(context: Context) -> None:
"""Verify OVERLAY supports rollback."""
assert SandboxStrategy.OVERLAY.supports_rollback is True
@then("SandboxStrategy.TRANSACTION_ROLLBACK should support rollback")
def step_transaction_rollback_supports_rollback(context: Context) -> None:
"""Verify TRANSACTION_ROLLBACK supports rollback."""
assert SandboxStrategy.TRANSACTION_ROLLBACK.supports_rollback is True
@then("SandboxStrategy.VERSIONING should support rollback")
def step_versioning_supports_rollback(context: Context) -> None:
"""Verify VERSIONING supports rollback."""
assert SandboxStrategy.VERSIONING.supports_rollback is True
@then("SandboxStrategy.NONE should not support rollback")
def step_none_does_not_support_rollback(context: Context) -> None:
"""Verify NONE does not support rollback."""
assert SandboxStrategy.NONE.supports_rollback is False
# SandboxStrategy.is_copy_based Tests
@then("SandboxStrategy.COPY_ON_WRITE should be copy based")
def step_copy_on_write_is_copy_based(context: Context) -> None:
"""Verify COPY_ON_WRITE is copy based."""
assert SandboxStrategy.COPY_ON_WRITE.is_copy_based is True
@then("SandboxStrategy.OVERLAY should be copy based")
def step_overlay_is_copy_based(context: Context) -> None:
"""Verify OVERLAY is copy based."""
assert SandboxStrategy.OVERLAY.is_copy_based is True
@then("SandboxStrategy.GIT_WORKTREE should not be copy based")
def step_git_worktree_not_copy_based(context: Context) -> None:
"""Verify GIT_WORKTREE is not copy based."""
assert SandboxStrategy.GIT_WORKTREE.is_copy_based is False
@then("SandboxStrategy.TRANSACTION_ROLLBACK should not be copy based")
def step_transaction_rollback_not_copy_based(context: Context) -> None:
"""Verify TRANSACTION_ROLLBACK is not copy based."""
assert SandboxStrategy.TRANSACTION_ROLLBACK.is_copy_based is False
@then("SandboxStrategy.VERSIONING should not be copy based")
def step_versioning_not_copy_based(context: Context) -> None:
"""Verify VERSIONING is not copy based."""
assert SandboxStrategy.VERSIONING.is_copy_based is False
@then("SandboxStrategy.NONE should not be copy based")
def step_none_not_copy_based(context: Context) -> None:
"""Verify NONE is not copy based."""
assert SandboxStrategy.NONE.is_copy_based is False
# Resource Pydantic Model Steps (B1.2)
VALID_ULID = "01ARZ3NDEKTSV4RRFFQ69G5FAV"
@given(
'a Resource with required fields id "{resource_id}" name "{name}" type "{rtype}" location "{location}"'
)
def step_create_resource(
context: Context, resource_id: str, name: str, rtype: str, location: str
) -> None:
"""Create a Resource with required fields only."""
context.error = None
try:
context.resource = Resource(
resource_id=resource_id,
name=name,
type=ResourceType(rtype),
location=location,
)
except (ValidationError, ValueError) as e:
context.error = e
@given("I have a valid Resource")
def step_have_valid_resource(context: Context) -> None:
"""Create a valid Resource for mutation testing."""
context.resource = Resource(
resource_id=VALID_ULID,
name="my-repo",
type=ResourceType.GIT_REPOSITORY,
location="/tmp/repo",
)
@when("I try to modify the resource name")
def step_try_modify_resource_name(context: Context) -> None:
"""Attempt to modify a frozen model field."""
context.error = None
try:
context.resource.name = "new-name" # type: ignore[misc]
except ValidationError as e:
context.error = e
@given(
'a remote Resource with id "{resource_id}" name "{name}" type "{rtype}" location "{location}"'
)
def step_create_resource_remote(
context: Context, resource_id: str, name: str, rtype: str, location: str
) -> None:
"""Create a remote Resource."""
context.error = None
try:
context.resource = Resource(
resource_id=resource_id,
name=name,
type=ResourceType(rtype),
location=location,
is_remote=True,
)
except (ValidationError, ValueError) as e:
context.error = e
@given(
'a read-only Resource with id "{resource_id}" name "{name}" type "{rtype}" location "{location}"'
)
def step_create_resource_read_only(
context: Context, resource_id: str, name: str, rtype: str, location: str
) -> None:
"""Create a read-only Resource."""
context.error = None
try:
context.resource = Resource(
resource_id=resource_id,
name=name,
type=ResourceType(rtype),
location=location,
read_only=True,
)
except (ValidationError, ValueError) as e:
context.error = e
@given(
'a Resource with id "{resource_id}" name "{name}" type "{rtype}" location "{location}" strategy "{strategy}"'
)
def step_create_resource_with_strategy(
context: Context,
resource_id: str,
name: str,
rtype: str,
location: str,
strategy: str,
) -> None:
"""Create a Resource with a specific sandbox strategy."""
context.error = None
try:
context.resource = Resource(
resource_id=resource_id,
name=name,
type=ResourceType(rtype),
location=location,
sandbox_strategy=SandboxStrategy(strategy),
)
except (ValidationError, ValueError) as e:
context.error = e
@given(
'a Resource with id "{resource_id}" name "{name}" type "{rtype}" location "{location}" metadata "{meta_str}"'
)
def step_create_resource_with_metadata(
context: Context,
resource_id: str,
name: str,
rtype: str,
location: str,
meta_str: str,
) -> None:
"""Create a Resource with metadata parsed from key=value pairs."""
metadata = {}
for pair in meta_str.split(","):
key, value = pair.strip().split("=", 1)
metadata[key.strip()] = value.strip()
context.error = None
try:
context.resource = Resource(
resource_id=resource_id,
name=name,
type=ResourceType(rtype),
location=location,
metadata=metadata,
)
except (ValidationError, ValueError) as e:
context.error = e
@when(
'I try to create a Resource with id "{resource_id}" name "{name}" type "{rtype}" location "{location}"'
)
def step_try_create_resource(
context: Context, resource_id: str, name: str, rtype: str, location: str
) -> None:
"""Attempt to create a Resource that may fail validation."""
context.error = None
try:
context.resource = Resource(
resource_id=resource_id,
name=name,
type=ResourceType(rtype),
location=location,
)
except (ValidationError, ValueError) as e:
context.error = e
@when("I try to create a Resource with an empty name")
def step_try_create_resource_empty_name(context: Context) -> None:
"""Attempt to create a Resource with empty name."""
context.error = None
try:
context.resource = Resource(
resource_id=VALID_ULID,
name="",
type=ResourceType.GIT_REPOSITORY,
location="/tmp/repo",
)
except (ValidationError, ValueError) as e:
context.error = e
@when("I try to create a Resource with an empty location")
def step_try_create_resource_empty_location(context: Context) -> None:
"""Attempt to create a Resource with empty location."""
context.error = None
try:
context.resource = Resource(
resource_id=VALID_ULID,
name="my-repo",
type=ResourceType.GIT_REPOSITORY,
location="",
)
except (ValidationError, ValueError) as e:
context.error = e
# Resource field assertion steps
@then('the resource name should be "{expected}"')
def step_check_resource_name(context: Context, expected: str) -> None:
"""Verify resource name."""
assert context.resource.name == expected, (
f"Expected name '{expected}', got '{context.resource.name}'"
)
@then("the resource type should be ResourceType.GIT_REPOSITORY")
def step_check_resource_type_git(context: Context) -> None:
"""Verify resource type is GIT_REPOSITORY."""
assert context.resource.type == ResourceType.GIT_REPOSITORY
@then('the resource location should be "{expected}"')
def step_check_resource_location(context: Context, expected: str) -> None:
"""Verify resource location."""
assert context.resource.location == expected, (
f"Expected location '{expected}', got '{context.resource.location}'"
)
@then("the resource is_remote should be false")
def step_check_resource_not_remote(context: Context) -> None:
"""Verify resource is not remote."""
assert context.resource.is_remote is False
@then("the resource is_remote should be true")
def step_check_resource_is_remote(context: Context) -> None:
"""Verify resource is remote."""
assert context.resource.is_remote is True
@then("the resource sandbox_strategy should be SandboxStrategy.NONE")
def step_check_resource_strategy_none(context: Context) -> None:
"""Verify default sandbox strategy is NONE."""
assert context.resource.sandbox_strategy == SandboxStrategy.NONE
@then("the resource sandbox_strategy should be SandboxStrategy.GIT_WORKTREE")
def step_check_resource_strategy_worktree(context: Context) -> None:
"""Verify sandbox strategy is GIT_WORKTREE."""
assert context.resource.sandbox_strategy == SandboxStrategy.GIT_WORKTREE
@then("the resource read_only should be false")
def step_check_resource_not_readonly(context: Context) -> None:
"""Verify resource is not read-only."""
assert context.resource.read_only is False
@then("the resource read_only should be true")
def step_check_resource_is_readonly(context: Context) -> None:
"""Verify resource is read-only."""
assert context.resource.read_only is True
@then("the resource metadata should be empty")
def step_check_resource_metadata_empty(context: Context) -> None:
"""Verify resource metadata is empty dict."""
assert context.resource.metadata == {}, (
f"Expected empty metadata, got {context.resource.metadata}"
)
@then("the resource created_at should be set")
def step_check_resource_created_at(context: Context) -> None:
"""Verify resource has a created_at timestamp."""
assert context.resource.created_at is not None
@then('the resource metadata should contain key "{key}" with value "{value}"')
def step_check_resource_metadata_key(context: Context, key: str, value: str) -> None:
"""Verify resource metadata contains expected key-value pair."""
assert key in context.resource.metadata, (
f"Expected key '{key}' in metadata, got {context.resource.metadata}"
)
assert context.resource.metadata[key] == value, (
f"Expected metadata['{key}'] = '{value}', got '{context.resource.metadata[key]}'"
)
# Note: "a validation error should be raised" step is defined in domain_models_steps.py
# Resource property assertion steps
@then("the resource supports_sandbox should be true")
def step_check_supports_sandbox_true(context: Context) -> None:
"""Verify resource supports sandbox."""
assert context.resource.supports_sandbox is True
@then("the resource supports_sandbox should be false")
def step_check_supports_sandbox_false(context: Context) -> None:
"""Verify resource does not support sandbox."""
assert context.resource.supports_sandbox is False
@then("the resource can_write should be true")
def step_check_can_write_true(context: Context) -> None:
"""Verify resource can be written to."""
assert context.resource.can_write is True
@then("the resource can_write should be false")
def step_check_can_write_false(context: Context) -> None:
"""Verify resource cannot be written to."""
assert context.resource.can_write is False
@then('the resource get_sandbox_path with base "{base}" should end with "{suffix}"')
def step_check_sandbox_path(context: Context, base: str, suffix: str) -> None:
"""Verify get_sandbox_path returns expected path."""
result = context.resource.get_sandbox_path(Path(base))
assert str(result).endswith(suffix), (
f"Expected path ending with '{suffix}', got '{result}'"
)