Files
freemo 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
feat(domain): align action model with spec
2026-02-13 09:41:28 -05:00

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)