"""Step definitions for Automation Profile domain model tests.""" from typing import Any from behave import then, when from behave.runner import Context from pydantic import ValidationError from cleveragents.domain.models.core.automation_profile import ( BUILTIN_PROFILES, AutomationProfile, get_builtin_profile, ) # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def _make_profile(**overrides: Any) -> AutomationProfile: """Create a profile with sensible defaults.""" defaults: dict[str, Any] = { "name": "test/default", } defaults.update(overrides) return AutomationProfile(**defaults) # --------------------------------------------------------------------------- # Profile creation with specific thresholds # --------------------------------------------------------------------------- @when("I create a profile with decompose_task {value:g}") def step_create_profile_decompose_task(context: Context, value: float) -> None: """Create a profile with a specific decompose_task.""" context.profile_model = _make_profile(decompose_task=value) context.profile_error = None @when("I try to create a profile with decompose_task {value:g}") def step_try_create_profile_decompose_task(context: Context, value: float) -> None: """Try creating a profile with invalid decompose_task.""" context.profile_error = None context.profile_model = None try: context.profile_model = _make_profile(decompose_task=value) except ValidationError as e: context.profile_error = e @when("I try to create a profile with create_tool {value:g}") def step_try_create_profile_create_tool(context: Context, value: float) -> None: """Try creating a profile with invalid create_tool.""" context.profile_error = None context.profile_model = None try: context.profile_model = _make_profile(create_tool=value) except ValidationError as e: context.profile_error = e @when("I try to create a profile with select_tool {value:g}") def step_try_create_profile_select_tool(context: Context, value: float) -> None: """Try creating with invalid select_tool.""" context.profile_error = None context.profile_model = None try: context.profile_model = _make_profile(select_tool=value) except ValidationError as e: context.profile_error = e @when("I try to create a profile with edit_code {value:g}") def step_try_create_profile_auto_dec_strat(context: Context, value: float) -> None: """Try creating with invalid edit_code.""" context.profile_error = None context.profile_model = None try: context.profile_model = _make_profile(edit_code=value) except ValidationError as e: context.profile_error = e @when("I try to create a profile with execute_command {value:g}") def step_try_create_profile_auto_dec_exec(context: Context, value: float) -> None: """Try creating with invalid execute_command.""" context.profile_error = None context.profile_model = None try: context.profile_model = _make_profile(execute_command=value) except ValidationError as e: context.profile_error = e @when("I try to create a profile with create_file {value:g}") def step_try_create_profile_auto_val_fix(context: Context, value: float) -> None: """Try creating with invalid create_file.""" context.profile_error = None context.profile_model = None try: context.profile_model = _make_profile(create_file=value) except ValidationError as e: context.profile_error = e @when("I try to create a profile with delete_content {value:g}") def step_try_create_profile_auto_strat_rev(context: Context, value: float) -> None: """Try creating with invalid delete_content.""" context.profile_error = None context.profile_model = None try: context.profile_model = _make_profile(delete_content=value) except ValidationError as e: context.profile_error = e @when("I try to create a profile with access_network {value:g}") def step_try_create_profile_auto_rev_apply(context: Context, value: float) -> None: """Try creating with invalid access_network.""" context.profile_error = None context.profile_model = None try: context.profile_model = _make_profile(access_network=value) except ValidationError as e: context.profile_error = e @when("I try to create a profile with install_dependency {value:g}") def step_try_create_profile_auto_child(context: Context, value: float) -> None: """Try creating with invalid install_dependency.""" context.profile_error = None context.profile_model = None try: context.profile_model = _make_profile(install_dependency=value) except ValidationError as e: context.profile_error = e @when("I try to create a profile with modify_config {value:g}") def step_try_create_profile_auto_retry(context: Context, value: float) -> None: """Try creating with invalid modify_config.""" context.profile_error = None context.profile_model = None try: context.profile_model = _make_profile(modify_config=value) except ValidationError as e: context.profile_error = e @when("I try to create a profile with approve_plan {value:g}") def step_try_create_profile_auto_ckpt(context: Context, value: float) -> None: """Try creating with invalid approve_plan.""" context.profile_error = None context.profile_model = None try: context.profile_model = _make_profile(approve_plan=value) except ValidationError as e: context.profile_error = e # --------------------------------------------------------------------------- # Profile assertions # --------------------------------------------------------------------------- @then("the profile model should be created") def step_check_profile_created(context: Context) -> None: """Verify the profile was created.""" assert context.profile_model is not None, "Profile should be created" @then("the profile decompose_task should be {expected:g}") def step_check_decompose_task(context: Context, expected: float) -> None: """Check decompose_task value.""" actual = context.profile_model.decompose_task assert actual == expected, f"Expected decompose_task {expected}, got {actual}" @then("the profile create_tool should be {expected:g}") def step_check_create_tool(context: Context, expected: float) -> None: """Check create_tool value.""" actual = context.profile_model.create_tool assert actual == expected, f"Expected create_tool {expected}, got {actual}" @then("the profile select_tool should be {expected:g}") def step_check_select_tool(context: Context, expected: float) -> None: """Check select_tool value.""" actual = context.profile_model.select_tool assert actual == expected, f"Expected select_tool {expected}, got {actual}" @then("the profile edit_code should be {expected:g}") def step_check_auto_dec_strat(context: Context, expected: float) -> None: """Check edit_code value.""" actual = context.profile_model.edit_code assert actual == expected, f"Expected {expected}, got {actual}" @then("the profile execute_command should be {expected:g}") def step_check_auto_dec_exec(context: Context, expected: float) -> None: """Check execute_command value.""" actual = context.profile_model.execute_command assert actual == expected, f"Expected {expected}, got {actual}" @then("the profile create_file should be {expected:g}") def step_check_auto_val_fix(context: Context, expected: float) -> None: """Check create_file value.""" actual = context.profile_model.create_file assert actual == expected, f"Expected {expected}, got {actual}" @then("the profile delete_content should be {expected:g}") def step_check_auto_strat_rev(context: Context, expected: float) -> None: """Check delete_content value.""" actual = context.profile_model.delete_content assert actual == expected, f"Expected {expected}, got {actual}" @then("the profile access_network should be {expected:g}") def step_check_auto_rev_apply(context: Context, expected: float) -> None: """Check access_network value.""" actual = context.profile_model.access_network assert actual == expected, f"Expected {expected}, got {actual}" @then("the profile install_dependency should be {expected:g}") def step_check_auto_child(context: Context, expected: float) -> None: """Check install_dependency value.""" actual = context.profile_model.install_dependency assert actual == expected, f"Expected {expected}, got {actual}" @then("the profile modify_config should be {expected:g}") def step_check_auto_retry(context: Context, expected: float) -> None: """Check modify_config value.""" actual = context.profile_model.modify_config assert actual == expected, f"Expected {expected}, got {actual}" @then("the profile approve_plan should be {expected:g}") def step_check_auto_ckpt(context: Context, expected: float) -> None: """Check approve_plan value.""" actual = context.profile_model.approve_plan assert actual == expected, f"Expected {expected}, got {actual}" @then("the profile safety require_sandbox should be {expected}") def step_check_safety_require_sandbox(context: Context, expected: str) -> None: """Check safety.require_sandbox value via composed SafetyProfile.""" exp_bool = expected.lower() == "true" actual = context.profile_model.safety.require_sandbox assert actual is exp_bool, ( f"Expected safety.require_sandbox {exp_bool}, got {actual}" ) @then("the profile safety require_checkpoints should be {expected}") def step_check_safety_require_checkpoints(context: Context, expected: str) -> None: """Check safety.require_checkpoints value via composed SafetyProfile.""" exp_bool = expected.lower() == "true" actual = context.profile_model.safety.require_checkpoints assert actual is exp_bool, ( f"Expected safety.require_checkpoints {exp_bool}, got {actual}" ) @then("the profile safety allow_unsafe_tools should be {expected}") def step_check_safety_allow_unsafe(context: Context, expected: str) -> None: """Check safety.allow_unsafe_tools value via composed SafetyProfile.""" exp_bool = expected.lower() == "true" actual = context.profile_model.safety.allow_unsafe_tools assert actual is exp_bool, ( f"Expected safety.allow_unsafe_tools {exp_bool}, got {actual}" ) # --------------------------------------------------------------------------- # Validation errors # --------------------------------------------------------------------------- @then("a profile validation error should be raised") def step_check_profile_validation_error( context: Context, ) -> None: """Verify that a validation error was raised.""" assert context.profile_error is not None, "Expected a validation error to be raised" @then('the profile error should mention "{text}"') def step_check_profile_error_message(context: Context, text: str) -> None: """Check the error message contains expected text.""" error_str = str(context.profile_error) assert text in error_str, f"Expected error to mention '{text}', got: {error_str}" # --------------------------------------------------------------------------- # Built-in profiles # --------------------------------------------------------------------------- @when('I load the built-in profile "{name}"') def step_load_builtin_profile(context: Context, name: str) -> None: """Load a built-in profile by name.""" context.profile_model = get_builtin_profile(name) context.profile_error = None @then("there should be {count:d} built-in profiles") def step_check_builtin_count(context: Context, count: int) -> None: """Check the number of built-in profiles.""" actual = len(BUILTIN_PROFILES) assert actual == count, f"Expected {count} built-in profiles, got {actual}" @then('built-in profile "{name}" should exist') def step_check_builtin_exists(context: Context, name: str) -> None: """Check a built-in profile exists.""" assert name in BUILTIN_PROFILES, f"Expected built-in profile '{name}' to exist" # --------------------------------------------------------------------------- # Custom profile from config # --------------------------------------------------------------------------- @when('I load a profile from config with name "{name}" and select_tool {value:g}') def step_load_profile_from_config(context: Context, name: str, value: float) -> None: """Load a profile from config dict.""" config = { "name": name, "description": "Custom profile", "select_tool": value, } context.profile_model = AutomationProfile.from_config(config) context.profile_error = None @when("I try to load a profile from config missing name") def step_try_load_profile_config_missing_name( context: Context, ) -> None: """Try loading profile config without name.""" context.profile_config_error = None try: AutomationProfile.from_config({"description": "No name"}) except ValueError as e: context.profile_config_error = e @then('a profile config error should be raised with "{field}"') def step_check_profile_config_error(context: Context, field: str) -> None: """Check config error mentions field.""" assert context.profile_config_error is not None, ( f"Expected ValueError for missing '{field}'" ) assert field in str(context.profile_config_error), ( f"Expected error to mention '{field}', got: {context.profile_config_error}" ) # --------------------------------------------------------------------------- # Name validation # --------------------------------------------------------------------------- @when('I create a profile with name "{name}"') def step_create_profile_by_name(context: Context, name: str) -> None: """Create a profile with specific name.""" context.profile_model = AutomationProfile(name=name) context.profile_error = None @when('I try to create a profile with invalid name "{name}"') def step_try_create_profile_invalid_name(context: Context, name: str) -> None: """Attempt to create a profile with invalid name.""" context.profile_error = None context.profile_model = None try: context.profile_model = AutomationProfile(name=name) except ValidationError as e: context.profile_error = e @when("I try to create a profile with empty name") def step_try_create_profile_empty_name( context: Context, ) -> None: """Attempt to create a profile with empty name.""" context.profile_error = None context.profile_model = None try: context.profile_model = AutomationProfile(name="") except ValidationError as e: context.profile_error = e @then('the profile name should be "{expected}"') def step_check_profile_name(context: Context, expected: str) -> None: """Check profile name.""" assert context.profile_model.name == expected, ( f"Expected name '{expected}', got '{context.profile_model.name}'" ) # --------------------------------------------------------------------------- # get_builtin_profile helper # --------------------------------------------------------------------------- @when('I call get_builtin_profile with "{name}"') def step_call_get_builtin(context: Context, name: str) -> None: """Call get_builtin_profile.""" context.profile_model = get_builtin_profile(name) context.profile_error = None @when('I try to call get_builtin_profile with "{name}"') def step_try_call_get_builtin(context: Context, name: str) -> None: """Try calling get_builtin_profile with bad name.""" context.profile_key_error = None try: get_builtin_profile(name) except KeyError as e: context.profile_key_error = e @then("a profile key error should be raised") def step_check_profile_key_error( context: Context, ) -> None: """Verify KeyError was raised.""" assert context.profile_key_error is not None, "Expected a KeyError to be raised" # --------------------------------------------------------------------------- # Schema version # --------------------------------------------------------------------------- @then('the profile schema_version should be "{expected}"') def step_check_schema_version(context: Context, expected: str) -> None: """Check profile schema_version.""" actual = context.profile_model.schema_version assert actual == expected, f"Expected schema_version '{expected}', got '{actual}'" @when('I create a profile with schema_version "{version}"') def step_create_profile_with_version(context: Context, version: str) -> None: """Create a profile with custom schema_version.""" context.profile_model = AutomationProfile( name="test-profile", schema_version=version ) context.profile_error = None # --------------------------------------------------------------------------- # Description # --------------------------------------------------------------------------- @then('the profile description should be "{expected}"') def step_check_profile_description(context: Context, expected: str) -> None: """Check profile description.""" actual = context.profile_model.description assert actual == expected, f"Expected description '{expected}', got '{actual}'" @then("the profile description should be empty") def step_check_profile_description_empty( context: Context, ) -> None: """Check profile description is empty.""" actual = context.profile_model.description assert actual == "", f"Expected empty description, got '{actual}'" @when('I create a profile with description "{desc}"') def step_create_profile_with_desc(context: Context, desc: str) -> None: """Create a profile with custom description.""" context.profile_model = AutomationProfile(name="desc-test", description=desc) context.profile_error = None # --------------------------------------------------------------------------- # validate_assignment # --------------------------------------------------------------------------- @when("I try to assign decompose_task {value:g} on the profile") def step_try_assign_threshold(context: Context, value: float) -> None: """Try to assign an invalid threshold on existing profile.""" context.profile_error = None try: context.profile_model.decompose_task = value except ValidationError as e: context.profile_error = e # --------------------------------------------------------------------------- # model_dump # --------------------------------------------------------------------------- @when("I create a profile and dump it with select_tool {value:g}") def step_create_profile_with_select_tool(context: Context, value: float) -> None: """Create a profile and store model dump.""" context.profile_model = AutomationProfile(name="dump-test", select_tool=value) context.profile_dump = context.profile_model.model_dump() context.profile_error = None @then('the profile model dump should have key "{key}"') def step_check_dump_key(context: Context, key: str) -> None: """Check profile model dump has key.""" assert key in context.profile_dump, ( f"Expected key '{key}' in model dump, keys: {list(context.profile_dump)}" ) @then("the profile model dump select_tool should be {expected:g}") def step_check_dump_select_tool(context: Context, expected: float) -> None: """Check select_tool in model dump.""" actual = context.profile_dump["select_tool"] assert actual == expected, f"Expected select_tool {expected}, got {actual}"