fd6d41b371
CI / lint (pull_request) Successful in 14s
CI / typecheck (pull_request) Successful in 26s
CI / security (pull_request) Successful in 21s
CI / quality (pull_request) Successful in 16s
CI / integration_tests (pull_request) Successful in 4m15s
CI / build (pull_request) Successful in 16s
CI / unit_tests (pull_request) Successful in 7m41s
CI / docker (pull_request) Successful in 39s
CI / lint (push) Successful in 13s
CI / coverage (pull_request) Successful in 5m38s
CI / typecheck (push) Successful in 26s
CI / security (push) Successful in 22s
CI / quality (push) Successful in 15s
CI / integration_tests (push) Successful in 4m10s
CI / build (push) Successful in 15s
CI / unit_tests (push) Successful in 8m17s
CI / docker (push) Successful in 41s
CI / coverage (push) Successful in 5m30s
982 lines
37 KiB
Python
982 lines
37 KiB
Python
"""Step definitions for Action domain model tests."""
|
|
|
|
from typing import Any
|
|
|
|
from behave import given, then, when
|
|
from behave.runner import Context
|
|
from pydantic import ValidationError
|
|
|
|
from cleveragents.domain.models.core.action import (
|
|
Action,
|
|
ActionArgument,
|
|
ActionState,
|
|
ArgumentRequirement,
|
|
ArgumentType,
|
|
)
|
|
from cleveragents.domain.models.core.plan import NamespacedName
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helper
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def _create_default_action(
|
|
name: str = "local/test-action",
|
|
definition_of_done: str = "Tests pass",
|
|
**kwargs: Any,
|
|
) -> Action:
|
|
"""Helper to create an action with defaults."""
|
|
parsed_name = NamespacedName.parse(name)
|
|
return Action(
|
|
namespaced_name=parsed_name,
|
|
description=kwargs.pop("description", "Default test action"),
|
|
definition_of_done=definition_of_done,
|
|
strategy_actor=kwargs.pop("strategy_actor", "openai/gpt-4"),
|
|
execution_actor=kwargs.pop("execution_actor", "openai/gpt-4"),
|
|
**kwargs,
|
|
)
|
|
|
|
|
|
def _full_config(**overrides: Any) -> dict[str, Any]:
|
|
"""Return a minimal valid config dict for Action.from_config, with overrides."""
|
|
cfg: dict[str, Any] = {
|
|
"name": "local/my-action",
|
|
"description": "Run tests",
|
|
"strategy_actor": "openai/gpt-4",
|
|
"execution_actor": "openai/gpt-4",
|
|
"definition_of_done": "All tests green",
|
|
}
|
|
cfg.update(overrides)
|
|
return cfg
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# ActionArgument Parsing Steps
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when('I parse the action argument "{arg_string}"')
|
|
def step_parse_action_argument(context: Context, arg_string: str) -> None:
|
|
"""Parse an action argument string."""
|
|
context.argument = ActionArgument.parse(arg_string)
|
|
context.parse_error = None
|
|
|
|
|
|
@when('I try to parse the action argument "{arg_string}"')
|
|
def step_try_parse_action_argument(context: Context, arg_string: str) -> None:
|
|
"""Attempt to parse an action argument string that might fail."""
|
|
context.parse_error = None
|
|
try:
|
|
context.argument = ActionArgument.parse(arg_string)
|
|
except ValueError as e:
|
|
context.parse_error = e
|
|
|
|
|
|
@then('the argument name should be "{expected}"')
|
|
def step_check_argument_name(context: Context, expected: str) -> None:
|
|
"""Check the argument name matches expected."""
|
|
assert context.argument.name == expected, (
|
|
f"Expected name '{expected}', got '{context.argument.name}'"
|
|
)
|
|
|
|
|
|
@then('the argument type should be "{expected}"')
|
|
def step_check_argument_type(context: Context, expected: str) -> None:
|
|
"""Check the argument type matches expected."""
|
|
actual = context.argument.arg_type.value
|
|
assert actual == expected, f"Expected type '{expected}', got '{actual}'"
|
|
|
|
|
|
@then("the argument should be required")
|
|
def step_check_argument_required(context: Context) -> None:
|
|
"""Check the argument is required."""
|
|
assert context.argument.requirement == ArgumentRequirement.REQUIRED, (
|
|
f"Expected argument to be required, got {context.argument.requirement}"
|
|
)
|
|
|
|
|
|
@then("the argument should be optional")
|
|
def step_check_argument_optional(context: Context) -> None:
|
|
"""Check the argument is optional."""
|
|
assert context.argument.requirement == ArgumentRequirement.OPTIONAL, (
|
|
f"Expected argument to be optional, got {context.argument.requirement}"
|
|
)
|
|
|
|
|
|
@then('the argument description should be "{expected}"')
|
|
def step_check_argument_description(context: Context, expected: str) -> None:
|
|
"""Check the argument description matches expected."""
|
|
assert context.argument.description == expected, (
|
|
f"Expected description '{expected}', got '{context.argument.description}'"
|
|
)
|
|
|
|
|
|
@then("the argument description should be empty")
|
|
def step_check_argument_description_empty(context: Context) -> None:
|
|
"""Check the argument description is empty."""
|
|
assert context.argument.description == "", (
|
|
f"Expected empty description, got '{context.argument.description}'"
|
|
)
|
|
|
|
|
|
@then("an argument parse error should be raised")
|
|
def step_check_argument_parse_error(context: Context) -> None:
|
|
"""Verify that a parse error was raised."""
|
|
assert context.parse_error is not None, "Expected a parse error"
|
|
|
|
|
|
@when('I try to create an action argument with invalid name "{name}"')
|
|
def step_try_create_argument_with_invalid_name(context: Context, name: str) -> None:
|
|
"""Attempt to create an action argument with an invalid identifier name."""
|
|
context.error = None
|
|
try:
|
|
context.argument = ActionArgument(
|
|
name=name,
|
|
arg_type=ArgumentType.STRING,
|
|
requirement=ArgumentRequirement.REQUIRED,
|
|
description="Invalid name",
|
|
)
|
|
except ValidationError as e:
|
|
context.error = e
|
|
|
|
|
|
@when("I stringify the action argument")
|
|
def step_stringify_action_argument(context: Context) -> None:
|
|
"""Convert the action argument to its definition string."""
|
|
context.argument_string = str(context.argument)
|
|
|
|
|
|
@then('the argument definition string should be "{expected}"')
|
|
def step_check_argument_definition_string(context: Context, expected: str) -> None:
|
|
"""Verify the string representation of the action argument."""
|
|
assert context.argument_string == expected, (
|
|
f"Expected definition string '{expected}', got '{context.argument_string}'"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Action Creation Steps
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when('I create an action with name "{name}" and definition of done "{dod}"')
|
|
def step_create_action_with_name_dod(context: Context, name: str, dod: str) -> None:
|
|
"""Create an action with specified name and definition of done."""
|
|
context.action = _create_default_action(name=name, definition_of_done=dod)
|
|
|
|
|
|
@when("I create a new action")
|
|
def step_create_new_action(context: Context) -> None:
|
|
"""Create a new action with default values."""
|
|
context.action = _create_default_action()
|
|
|
|
|
|
@given("I have action arguments")
|
|
def step_have_action_arguments(context: Context) -> None:
|
|
"""Store action arguments from table."""
|
|
context.action_arguments = []
|
|
assert context.table is not None, "Expected table data for action arguments"
|
|
for row in context.table:
|
|
arg = ActionArgument(
|
|
name=row["name"],
|
|
arg_type=ArgumentType(row["type"]),
|
|
requirement=ArgumentRequirement(row["requirement"]),
|
|
description=row["description"],
|
|
)
|
|
context.action_arguments.append(arg)
|
|
|
|
|
|
@when("I create an action with these arguments")
|
|
def step_create_action_with_arguments(context: Context) -> None:
|
|
"""Create an action with the stored arguments."""
|
|
context.action = _create_default_action(arguments=context.action_arguments)
|
|
|
|
|
|
@then("the action should be created")
|
|
def step_check_action_created(context: Context) -> None:
|
|
"""Verify the action was created."""
|
|
assert context.action is not None, "Action should be created"
|
|
|
|
|
|
@then('the action namespace should be "{expected}"')
|
|
def step_check_action_namespace(context: Context, expected: str) -> None:
|
|
"""Check the action namespace matches expected."""
|
|
actual = context.action.namespaced_name.namespace
|
|
assert actual == expected, f"Expected namespace '{expected}', got '{actual}'"
|
|
|
|
|
|
@then('the action name should be "{expected}"')
|
|
def step_check_action_name(context: Context, expected: str) -> None:
|
|
"""Check the action name matches expected."""
|
|
actual = context.action.namespaced_name.name
|
|
assert actual == expected, f"Expected name '{expected}', got '{actual}'"
|
|
|
|
|
|
@then('the action state should be "{expected}"')
|
|
def step_check_action_state(context: Context, expected: str) -> None:
|
|
"""Check the action state matches expected."""
|
|
actual = context.action.state.value
|
|
assert actual == expected, f"Expected state '{expected}', got '{actual}'"
|
|
|
|
|
|
@then("the action should have {count:d} arguments")
|
|
def step_check_argument_count(context: Context, count: int) -> None:
|
|
"""Check the number of arguments."""
|
|
actual = len(context.action.arguments)
|
|
assert actual == count, f"Expected {count} arguments, got {actual}"
|
|
|
|
|
|
@then("the action should have {count:d} required argument")
|
|
def step_check_required_argument_count(context: Context, count: int) -> None:
|
|
"""Check the number of required arguments."""
|
|
actual = len(context.action.required_arguments)
|
|
assert actual == count, f"Expected {count} required arguments, got {actual}"
|
|
|
|
|
|
@then("the action should have {count:d} optional argument")
|
|
def step_check_optional_argument_count(context: Context, count: int) -> None:
|
|
"""Check the number of optional arguments."""
|
|
actual = len(context.action.optional_arguments)
|
|
assert actual == count, f"Expected {count} optional arguments, got {actual}"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Argument Validation Steps
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given('I have an action with required argument "{arg_string}"')
|
|
def step_have_action_with_required_arg(context: Context, arg_string: str) -> None:
|
|
"""Create an action with a required argument."""
|
|
arg = ActionArgument.parse(arg_string)
|
|
context.action = _create_default_action(arguments=[arg])
|
|
|
|
|
|
@when("I validate arguments with target {value:d}")
|
|
def step_validate_arguments_with_target(context: Context, value: int) -> None:
|
|
"""Validate arguments with target value."""
|
|
context.errors = context.action.validate_arguments({"target": value})
|
|
|
|
|
|
@when("I validate arguments without target")
|
|
def step_validate_arguments_without_target(context: Context) -> None:
|
|
"""Validate arguments without providing target."""
|
|
context.errors = context.action.validate_arguments({})
|
|
|
|
|
|
@when('I validate arguments with target {value:d} and extra "{extra}"')
|
|
def step_validate_arguments_with_extra(
|
|
context: Context, value: int, extra: str
|
|
) -> None:
|
|
"""Validate arguments with an extra unknown argument."""
|
|
context.errors = context.action.validate_arguments(
|
|
{
|
|
"target": value,
|
|
"extra": extra,
|
|
}
|
|
)
|
|
|
|
|
|
@when('I validate arguments with target as string "{value}"')
|
|
def step_validate_arguments_with_string_target(context: Context, value: str) -> None:
|
|
"""Validate arguments with target as wrong type."""
|
|
context.errors = context.action.validate_arguments({"target": value})
|
|
|
|
|
|
@given(
|
|
'I have an action with bounded integer argument "{name}" min {min_value:d} max {max_value:d}'
|
|
)
|
|
def step_have_action_with_bounded_integer(
|
|
context: Context, name: str, min_value: int, max_value: int
|
|
) -> None:
|
|
"""Create an action with a bounded integer argument."""
|
|
arg = ActionArgument(
|
|
name=name,
|
|
arg_type=ArgumentType.INTEGER,
|
|
requirement=ArgumentRequirement.REQUIRED,
|
|
description="Bounded integer argument",
|
|
min_value=min_value,
|
|
max_value=max_value,
|
|
)
|
|
context.action = _create_default_action(arguments=[arg])
|
|
|
|
|
|
@given('I have an action with argument "{name}" of type "{arg_type}"')
|
|
def step_have_action_with_argument_type(
|
|
context: Context, name: str, arg_type: str
|
|
) -> None:
|
|
"""Create an action with an argument of a specific type."""
|
|
arg = ActionArgument(
|
|
name=name,
|
|
arg_type=ArgumentType(arg_type),
|
|
requirement=ArgumentRequirement.REQUIRED,
|
|
description=f"{arg_type} argument",
|
|
)
|
|
context.action = _create_default_action(arguments=[arg])
|
|
|
|
|
|
@when('I validate arguments with "{name}" set to {value:d}')
|
|
def step_validate_arguments_with_named_int(
|
|
context: Context, name: str, value: int
|
|
) -> None:
|
|
"""Validate arguments with a named integer value."""
|
|
context.errors = context.action.validate_arguments({name: value})
|
|
|
|
|
|
@when('I validate arguments with "{name}" set to "{value}"')
|
|
def step_validate_arguments_with_named_string(
|
|
context: Context, name: str, value: str
|
|
) -> None:
|
|
"""Validate arguments with a named string value."""
|
|
context.errors = context.action.validate_arguments({name: value})
|
|
|
|
|
|
@then("the validation should pass")
|
|
def step_check_validation_pass(context: Context) -> None:
|
|
"""Check that validation passed."""
|
|
assert len(context.errors) == 0, (
|
|
f"Expected validation to pass, got errors: {context.errors}"
|
|
)
|
|
|
|
|
|
@then('the validation should fail with "{expected_msg}"')
|
|
def step_check_validation_fail(context: Context, expected_msg: str) -> None:
|
|
"""Check that validation failed with expected message."""
|
|
assert len(context.errors) > 0, "Expected validation errors"
|
|
error_text = " ".join(context.errors)
|
|
assert expected_msg in error_text, (
|
|
f"Expected error containing '{expected_msg}', got: {context.errors}"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Action State Steps
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given("I have an available action")
|
|
def step_have_available_action(context: Context) -> None:
|
|
"""Create an action in available state."""
|
|
context.action = _create_default_action()
|
|
context.action.state = ActionState.AVAILABLE
|
|
|
|
|
|
@when("I set the action state to archived")
|
|
def step_set_action_archived(context: Context) -> None:
|
|
"""Set action state to archived."""
|
|
context.action.state = ActionState.ARCHIVED
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Actor Configuration Steps
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when("I try to create an action without strategy actor")
|
|
def step_try_create_action_without_strategy_actor(context: Context) -> None:
|
|
"""Attempt to create an action without strategy actor."""
|
|
context.error = None
|
|
try:
|
|
data = {
|
|
"namespaced_name": NamespacedName.parse("local/test"),
|
|
"description": "Test action",
|
|
"definition_of_done": "Test",
|
|
# Missing strategy_actor
|
|
"execution_actor": "openai/gpt-4",
|
|
}
|
|
context.action = Action.model_validate(data)
|
|
except ValidationError as e:
|
|
context.error = e
|
|
context.action = None
|
|
|
|
|
|
# Note: "a validation error should be raised" step is defined in domain_models_steps.py
|
|
|
|
|
|
@when('I create an action with review actor "{actor}"')
|
|
def step_create_action_with_review_actor(context: Context, actor: str) -> None:
|
|
"""Create an action with a review actor."""
|
|
context.action = _create_default_action(review_actor=actor)
|
|
|
|
|
|
@when('I create an action with estimation actor "{actor}"')
|
|
def step_create_action_with_estimation_actor(context: Context, actor: str) -> None:
|
|
"""Create an action with an estimation actor."""
|
|
context.action = _create_default_action(estimation_actor=actor)
|
|
|
|
|
|
@then("the action should have a review actor")
|
|
def step_check_has_review_actor(context: Context) -> None:
|
|
"""Check the action has a review actor."""
|
|
assert context.action.review_actor is not None, "Expected review actor"
|
|
|
|
|
|
@then("the action should have an estimation actor")
|
|
def step_check_has_estimation_actor(context: Context) -> None:
|
|
"""Check the action has an estimation actor."""
|
|
assert context.action.estimation_actor is not None, "Expected estimation actor"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Reusability Steps
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when("I create a non-reusable action")
|
|
def step_create_non_reusable_action(context: Context) -> None:
|
|
"""Create a non-reusable action."""
|
|
context.action = _create_default_action(reusable=False)
|
|
|
|
|
|
@then("the action should be reusable")
|
|
def step_check_action_reusable(context: Context) -> None:
|
|
"""Check the action is reusable."""
|
|
assert context.action.reusable is True, "Expected action to be reusable"
|
|
|
|
|
|
@then("the action should not be reusable")
|
|
def step_check_action_not_reusable(context: Context) -> None:
|
|
"""Check the action is not reusable."""
|
|
assert context.action.reusable is False, "Expected action to not be reusable"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Read-only Steps
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when("I create a read-only action")
|
|
def step_create_read_only_action(context: Context) -> None:
|
|
"""Create a read-only action."""
|
|
context.action = _create_default_action(read_only=True)
|
|
|
|
|
|
@then("the action should be read-only")
|
|
def step_check_action_read_only(context: Context) -> None:
|
|
"""Check the action is read-only."""
|
|
assert context.action.read_only is True, "Expected action to be read-only"
|
|
|
|
|
|
@then("the action should not be read-only")
|
|
def step_check_action_not_read_only(context: Context) -> None:
|
|
"""Check the action is not read-only."""
|
|
assert context.action.read_only is False, "Expected action to not be read-only"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Invariants Sanitization Steps
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when(
|
|
"I create an action model with invariants "
|
|
'" no secrets " and "" and " no secrets" and "keep data safe"'
|
|
)
|
|
def step_create_action_model_with_invariants(context: Context) -> None:
|
|
"""Create an action with invariants that need sanitization."""
|
|
context.action = _create_default_action(
|
|
invariants=[" no secrets ", "", " no secrets", "keep data safe"],
|
|
)
|
|
|
|
|
|
@then('the action model invariants should be "{first}" and "{second}"')
|
|
def step_check_action_model_invariants(
|
|
context: Context, first: str, second: str
|
|
) -> None:
|
|
"""Check the sanitized invariants list."""
|
|
assert context.action.invariants == [first, second], (
|
|
f"Expected [{first}, {second}], got {context.action.invariants}"
|
|
)
|
|
|
|
|
|
@when("I create an action model with null invariants")
|
|
def step_create_action_model_with_null_invariants(context: Context) -> None:
|
|
"""Create an action with None invariants (should default to [])."""
|
|
context.action = _create_default_action(invariants=None)
|
|
|
|
|
|
@then("the action model invariants count should be {count:d}")
|
|
def step_check_action_model_invariants_count(context: Context, count: int) -> None:
|
|
"""Check the invariants list length."""
|
|
actual = len(context.action.invariants)
|
|
assert actual == count, f"Expected {count} invariants, got {actual}"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# from_config Factory Steps
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when(
|
|
'I create an action model from config with name "{name}" '
|
|
'and description "{desc}" and dod "{dod}"'
|
|
)
|
|
def step_create_action_model_from_config(
|
|
context: Context, name: str, desc: str, dod: str
|
|
) -> None:
|
|
"""Create an action via from_config."""
|
|
config = _full_config(name=name, description=desc, definition_of_done=dod)
|
|
context.action = Action.from_config(config)
|
|
|
|
|
|
@then('the action model description should be "{expected}"')
|
|
def step_check_action_model_description(context: Context, expected: str) -> None:
|
|
"""Check the action description."""
|
|
assert context.action.description == expected, (
|
|
f"Expected description '{expected}', got '{context.action.description}'"
|
|
)
|
|
|
|
|
|
@when("I create an action model from config with arguments")
|
|
def step_create_action_model_from_config_with_args(context: Context) -> None:
|
|
"""Create an action via from_config with argument mappings."""
|
|
config = _full_config(
|
|
arguments=[
|
|
{
|
|
"name": "target",
|
|
"type": "integer",
|
|
"required": True,
|
|
"description": "Target value",
|
|
},
|
|
{
|
|
"name": "mode",
|
|
"type": "string",
|
|
"required": False,
|
|
"description": "Mode",
|
|
"default": "fast",
|
|
},
|
|
],
|
|
)
|
|
context.action = Action.from_config(config)
|
|
|
|
|
|
@when('I try to create an action model from config missing "{field}"')
|
|
def step_try_create_action_model_from_config_missing(
|
|
context: Context, field: str
|
|
) -> None:
|
|
"""Try from_config with a missing required field."""
|
|
config = _full_config()
|
|
del config[field]
|
|
context.action_model_config_error = None
|
|
try:
|
|
context.action = Action.from_config(config)
|
|
except ValueError as e:
|
|
context.action_model_config_error = e
|
|
|
|
|
|
@then('an action model config error should be raised with "{field}"')
|
|
def step_check_action_model_config_error(context: Context, field: str) -> None:
|
|
"""Check that a config error mentioning the field was raised."""
|
|
assert context.action_model_config_error is not None, (
|
|
f"Expected ValueError for missing '{field}'"
|
|
)
|
|
assert field in str(context.action_model_config_error), (
|
|
f"Expected error to mention '{field}', got: {context.action_model_config_error}"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# as_cli_dict Steps
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when("I create an action model and call as_cli_dict")
|
|
def step_create_action_model_and_cli_dict(context: Context) -> None:
|
|
"""Create an action and get its CLI dict."""
|
|
context.action = _create_default_action()
|
|
context.action_model_cli_dict = context.action.as_cli_dict()
|
|
|
|
|
|
@then('the action model cli dict should have key "{key}"')
|
|
def step_check_action_model_cli_dict_key(context: Context, key: str) -> None:
|
|
"""Check the CLI dict contains a key."""
|
|
assert key in context.action_model_cli_dict, (
|
|
f"Expected key '{key}' in cli dict, keys: {list(context.action_model_cli_dict)}"
|
|
)
|
|
|
|
|
|
@when("I create an action model with review actor and call as_cli_dict")
|
|
def step_create_action_model_with_review_and_cli_dict(context: Context) -> None:
|
|
"""Create an action with review actor and get CLI dict."""
|
|
context.action = _create_default_action(review_actor="local/reviewer")
|
|
context.action_model_cli_dict = context.action.as_cli_dict()
|
|
|
|
|
|
@when("I create an action model with arguments and call as_cli_dict")
|
|
def step_create_action_model_with_args_and_cli_dict(context: Context) -> None:
|
|
"""Create an action with arguments and get CLI dict."""
|
|
arg = ActionArgument(
|
|
name="target",
|
|
arg_type=ArgumentType.INTEGER,
|
|
requirement=ArgumentRequirement.REQUIRED,
|
|
description="Target value",
|
|
)
|
|
context.action = _create_default_action(arguments=[arg])
|
|
context.action_model_cli_dict = context.action.as_cli_dict()
|
|
|
|
|
|
@then("the action model cli dict arguments count should be {count:d}")
|
|
def step_check_action_model_cli_dict_args_count(context: Context, count: int) -> None:
|
|
"""Check the number of arguments in the CLI dict."""
|
|
actual = len(context.action_model_cli_dict["arguments"])
|
|
assert actual == count, f"Expected {count} arguments in cli dict, got {actual}"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# from_mapping Steps (ActionArgument)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when(
|
|
'I create an action argument from basic mapping "{name}" '
|
|
'type "{arg_type}" required {req}'
|
|
)
|
|
def step_create_action_argument_from_mapping(
|
|
context: Context, name: str, arg_type: str, req: str
|
|
) -> None:
|
|
"""Create an ActionArgument via from_mapping."""
|
|
required = req.lower() == "true"
|
|
context.argument = ActionArgument.from_mapping(
|
|
{"name": name, "type": arg_type, "required": required}
|
|
)
|
|
|
|
|
|
@when(
|
|
'I create an action argument from mapping with default "{name}" '
|
|
'type "{arg_type}" required {req} default "{default}"'
|
|
)
|
|
def step_create_action_argument_from_mapping_with_default(
|
|
context: Context, name: str, arg_type: str, req: str, default: str
|
|
) -> None:
|
|
"""Create an ActionArgument via from_mapping with a default value."""
|
|
required = req.lower() == "true"
|
|
context.argument = ActionArgument.from_mapping(
|
|
{"name": name, "type": arg_type, "required": required, "default": default}
|
|
)
|
|
|
|
|
|
@then('the action model argument default value should be "{expected}"')
|
|
def step_check_action_model_argument_default(context: Context, expected: str) -> None:
|
|
"""Check the argument default value."""
|
|
assert context.argument.default_value == expected, (
|
|
f"Expected default '{expected}', got '{context.argument.default_value}'"
|
|
)
|
|
|
|
|
|
@when("I try to create an action argument from mapping missing name")
|
|
def step_try_create_argument_from_mapping_missing_name(context: Context) -> None:
|
|
"""Try from_mapping without 'name'."""
|
|
context.action_model_arg_mapping_error = None
|
|
try:
|
|
ActionArgument.from_mapping({"type": "string"})
|
|
except ValueError as e:
|
|
context.action_model_arg_mapping_error = e
|
|
|
|
|
|
@when("I try to create an action argument from mapping missing type")
|
|
def step_try_create_argument_from_mapping_missing_type(context: Context) -> None:
|
|
"""Try from_mapping without 'type'."""
|
|
context.action_model_arg_mapping_error = None
|
|
try:
|
|
ActionArgument.from_mapping({"name": "foo"})
|
|
except ValueError as e:
|
|
context.action_model_arg_mapping_error = e
|
|
|
|
|
|
@then('an action model argument mapping error should be raised with "{field}"')
|
|
def step_check_action_model_arg_mapping_error(context: Context, field: str) -> None:
|
|
"""Check that a mapping error mentioning the field was raised."""
|
|
assert context.action_model_arg_mapping_error is not None, (
|
|
f"Expected ValueError for missing '{field}'"
|
|
)
|
|
assert field in str(context.action_model_arg_mapping_error), (
|
|
f"Expected error to mention '{field}', "
|
|
f"got: {context.action_model_arg_mapping_error}"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# coerce_value Steps
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given('I have an action model string argument named "{name}"')
|
|
def step_have_action_model_string_argument(context: Context, name: str) -> None:
|
|
"""Create a string ActionArgument."""
|
|
context.action_model_arg = ActionArgument(
|
|
name=name,
|
|
arg_type=ArgumentType.STRING,
|
|
requirement=ArgumentRequirement.REQUIRED,
|
|
)
|
|
|
|
|
|
@given('I have an action model integer argument named "{name}"')
|
|
def step_have_action_model_integer_argument(context: Context, name: str) -> None:
|
|
"""Create an integer ActionArgument."""
|
|
context.action_model_arg = ActionArgument(
|
|
name=name,
|
|
arg_type=ArgumentType.INTEGER,
|
|
requirement=ArgumentRequirement.REQUIRED,
|
|
)
|
|
|
|
|
|
@given('I have an action model float argument named "{name}"')
|
|
def step_have_action_model_float_argument(context: Context, name: str) -> None:
|
|
"""Create a float ActionArgument."""
|
|
context.action_model_arg = ActionArgument(
|
|
name=name,
|
|
arg_type=ArgumentType.FLOAT,
|
|
requirement=ArgumentRequirement.REQUIRED,
|
|
)
|
|
|
|
|
|
@given('I have an action model boolean argument named "{name}"')
|
|
def step_have_action_model_boolean_argument(context: Context, name: str) -> None:
|
|
"""Create a boolean ActionArgument."""
|
|
context.action_model_arg = ActionArgument(
|
|
name=name,
|
|
arg_type=ArgumentType.BOOLEAN,
|
|
requirement=ArgumentRequirement.REQUIRED,
|
|
)
|
|
|
|
|
|
@given('I have an action model list argument named "{name}"')
|
|
def step_have_action_model_list_argument(context: Context, name: str) -> None:
|
|
"""Create a list ActionArgument."""
|
|
context.action_model_arg = ActionArgument(
|
|
name=name,
|
|
arg_type=ArgumentType.LIST,
|
|
requirement=ArgumentRequirement.REQUIRED,
|
|
)
|
|
|
|
|
|
@when('I coerce action model value "{raw}" for the argument')
|
|
def step_coerce_action_model_value(context: Context, raw: str) -> None:
|
|
"""Coerce a raw string value using the stored argument."""
|
|
context.coerced_action_model_value = context.action_model_arg.coerce_value(raw)
|
|
|
|
|
|
@when('I try to coerce action model value "{raw}" for the argument')
|
|
def step_try_coerce_action_model_value(context: Context, raw: str) -> None:
|
|
"""Try to coerce a value that may fail."""
|
|
context.action_model_coerce_error = None
|
|
try:
|
|
context.coerced_action_model_value = context.action_model_arg.coerce_value(raw)
|
|
except ValueError as e:
|
|
context.action_model_coerce_error = e
|
|
|
|
|
|
@then('the coerced action model value should be string "{expected}"')
|
|
def step_check_coerced_action_model_string(context: Context, expected: str) -> None:
|
|
"""Check the coerced value is the expected string."""
|
|
assert context.coerced_action_model_value == expected, (
|
|
f"Expected '{expected}', got '{context.coerced_action_model_value}'"
|
|
)
|
|
|
|
|
|
@then("the coerced action model value should be integer {expected:d}")
|
|
def step_check_coerced_action_model_integer(context: Context, expected: int) -> None:
|
|
"""Check the coerced value is the expected integer."""
|
|
assert context.coerced_action_model_value == expected, (
|
|
f"Expected {expected}, got {context.coerced_action_model_value}"
|
|
)
|
|
assert isinstance(context.coerced_action_model_value, int)
|
|
|
|
|
|
@then("the coerced action model value should be float {expected:g}")
|
|
def step_check_coerced_action_model_float(context: Context, expected: float) -> None:
|
|
"""Check the coerced value is the expected float."""
|
|
assert abs(context.coerced_action_model_value - expected) < 1e-9, (
|
|
f"Expected {expected}, got {context.coerced_action_model_value}"
|
|
)
|
|
|
|
|
|
@then("the coerced action model value should be boolean true")
|
|
def step_check_coerced_action_model_bool_true(context: Context) -> None:
|
|
"""Check the coerced value is True."""
|
|
assert context.coerced_action_model_value is True, (
|
|
f"Expected True, got {context.coerced_action_model_value}"
|
|
)
|
|
|
|
|
|
@then("the coerced action model value should be boolean false")
|
|
def step_check_coerced_action_model_bool_false(context: Context) -> None:
|
|
"""Check the coerced value is False."""
|
|
assert context.coerced_action_model_value is False, (
|
|
f"Expected False, got {context.coerced_action_model_value}"
|
|
)
|
|
|
|
|
|
@then("an action model coerce error should be raised")
|
|
def step_check_action_model_coerce_error(context: Context) -> None:
|
|
"""Check that a coerce error was raised."""
|
|
assert context.action_model_coerce_error is not None, (
|
|
"Expected ValueError from coerce_value"
|
|
)
|
|
|
|
|
|
@then("the coerced action model value should be list with {count:d} items")
|
|
def step_check_coerced_action_model_list(context: Context, count: int) -> None:
|
|
"""Check the coerced value is a list of the expected length."""
|
|
assert isinstance(context.coerced_action_model_value, list), (
|
|
f"Expected list, got {type(context.coerced_action_model_value)}"
|
|
)
|
|
actual = len(context.coerced_action_model_value)
|
|
assert actual == count, f"Expected {count} items, got {actual}"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# render_template Steps
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when('I create an action model with description "{desc}"')
|
|
def step_create_action_model_with_description(context: Context, desc: str) -> None:
|
|
"""Create an action with a template description."""
|
|
context.action = _create_default_action(description=desc)
|
|
|
|
|
|
@when('I render action model description with target "{target}" and module "{module}"')
|
|
def step_render_action_model_description(
|
|
context: Context, target: str, module: str
|
|
) -> None:
|
|
"""Render the description template."""
|
|
context.rendered_action_model_desc = context.action.render_description(
|
|
{"target": target, "module": module}
|
|
)
|
|
|
|
|
|
@then('the rendered action model description should be "{expected}"')
|
|
def step_check_rendered_action_model_description(
|
|
context: Context, expected: str
|
|
) -> None:
|
|
"""Check the rendered description."""
|
|
assert context.rendered_action_model_desc == expected, (
|
|
f"Expected '{expected}', got '{context.rendered_action_model_desc}'"
|
|
)
|
|
|
|
|
|
@when('I try to render action model description with only target "{target}"')
|
|
def step_try_render_action_model_desc_incomplete(context: Context, target: str) -> None:
|
|
"""Try to render the description with missing placeholders."""
|
|
context.action_model_template_error = None
|
|
try:
|
|
context.action.render_description({"target": target})
|
|
except ValueError as e:
|
|
context.action_model_template_error = e
|
|
|
|
|
|
@then("an action model template error should be raised")
|
|
def step_check_action_model_template_error(context: Context) -> None:
|
|
"""Check that a template rendering error was raised."""
|
|
assert context.action_model_template_error is not None, (
|
|
"Expected ValueError from render_template"
|
|
)
|
|
|
|
|
|
@when("I create an action model with dod template for module and target")
|
|
def step_create_action_model_with_dod_template(context: Context) -> None:
|
|
"""Create an action with a template definition of done."""
|
|
context.action = _create_default_action(
|
|
definition_of_done="All tests in ${module} pass with ${target}% coverage",
|
|
)
|
|
|
|
|
|
@when('I render action model dod with target "{target}" and module "{module}"')
|
|
def step_render_action_model_dod(context: Context, target: str, module: str) -> None:
|
|
"""Render the definition of done template."""
|
|
context.rendered_action_model_dod = context.action.render_definition_of_done(
|
|
{"target": target, "module": module}
|
|
)
|
|
|
|
|
|
@then('the rendered action model dod should be "{expected}"')
|
|
def step_check_rendered_action_model_dod(context: Context, expected: str) -> None:
|
|
"""Check the rendered definition of done."""
|
|
assert context.rendered_action_model_dod == expected, (
|
|
f"Expected '{expected}', got '{context.rendered_action_model_dod}'"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# inputs_schema Validation Steps
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when(
|
|
'I create an action model with inputs schema type "{schema_type}" '
|
|
'and required "{req_field}"'
|
|
)
|
|
def step_create_action_model_with_inputs_schema(
|
|
context: Context, schema_type: str, req_field: str
|
|
) -> None:
|
|
"""Create an action with a valid inputs_schema dict."""
|
|
context.action = _create_default_action(
|
|
inputs_schema={"type": schema_type, "required": [req_field]},
|
|
)
|
|
|
|
|
|
@when("I create an action model with null inputs schema")
|
|
def step_create_action_model_with_null_inputs_schema(context: Context) -> None:
|
|
"""Create an action with inputs_schema=None."""
|
|
context.action = _create_default_action(inputs_schema=None)
|
|
|
|
|
|
@then("the action model inputs schema should not be none")
|
|
def step_check_action_model_inputs_schema_not_none(context: Context) -> None:
|
|
"""Check inputs_schema is set."""
|
|
assert context.action.inputs_schema is not None, "Expected inputs_schema to be set"
|
|
|
|
|
|
@then("the action model inputs schema should be none")
|
|
def step_check_action_model_inputs_schema_none(context: Context) -> None:
|
|
"""Check inputs_schema is None."""
|
|
assert context.action.inputs_schema is None, (
|
|
f"Expected inputs_schema to be None, got {context.action.inputs_schema}"
|
|
)
|
|
|
|
|
|
@when("I try to create an action model with inputs schema as string")
|
|
def step_try_create_action_model_with_bad_inputs_schema(context: Context) -> None:
|
|
"""Try to create an action with inputs_schema as a non-dict."""
|
|
context.error = None
|
|
try:
|
|
context.action = _create_default_action(inputs_schema="not a dict")
|
|
except ValidationError as e:
|
|
context.error = e
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# automation_profile Validation Steps
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when('I create an action model with automation profile "{profile}"')
|
|
def step_create_action_model_with_automation_profile(
|
|
context: Context, profile: str
|
|
) -> None:
|
|
"""Create an action with an automation_profile."""
|
|
context.action = _create_default_action(automation_profile=profile)
|
|
|
|
|
|
@then('the action model automation profile should be "{expected}"')
|
|
def step_check_action_model_automation_profile(context: Context, expected: str) -> None:
|
|
"""Check the automation_profile value."""
|
|
assert context.action.automation_profile == expected, (
|
|
f"Expected '{expected}', got '{context.action.automation_profile}'"
|
|
)
|
|
|
|
|
|
@then("the action model automation profile should be none")
|
|
def step_check_action_model_automation_profile_none(context: Context) -> None:
|
|
"""Check that automation_profile is None."""
|
|
assert context.action.automation_profile is None, (
|
|
f"Expected None, got '{context.action.automation_profile}'"
|
|
)
|
|
|
|
|
|
@when("I create an action model with null automation profile")
|
|
def step_create_action_model_with_null_automation_profile(context: Context) -> None:
|
|
"""Create an action with automation_profile=None."""
|
|
context.action = _create_default_action(automation_profile=None)
|