"""Step definitions for resource_cli_coverage_r3.feature. Covers the remaining uncovered lines in ``src/cleveragents/cli/commands/resource.py``: - Lines 159, 162: _resource_dict str() fallback for dates - Lines 280-281: type_remove ResourceTypeParentRemovalError - Lines 288-289: type_remove generic Exception - Lines 310-311: type_list empty - Lines 347-351: type_list generic Exception - Lines 388-392: type_show generic Exception - Lines 420-422: _print_type_panel chain resolution failure - Lines 465-471: _format_properties mounts JSON - Lines 622, 624-626, 629-630: resource_add image / mount validation - Lines 729-733: resource_list generic Exception - Lines 786-790: resource_show generic Exception - Lines 847-851: resource_tree generic Exception - Line 950: resource_inspect empty tree_data - Lines 1004-1008: resource_inspect generic Exception - Lines 1019, 1028: _read_resource_file path from props / traversal - Lines 1091-1095: resource_link_child generic Exception - Lines 1162-1163: resource_unlink_child generic Exception - Lines 1314-1316: resource_stop confirmation prompt - Lines 1333-1340: resource_stop CleverAgentsError / generic Exception - Lines 1389-1390: resource_rebuild path from properties - Lines 1397-1399: resource_rebuild confirmation prompt - Lines 1412-1419: resource_rebuild CleverAgentsError / generic Exception """ from __future__ import annotations import json import os import tempfile from io import StringIO from typing import Any from unittest.mock import MagicMock, patch from behave import given, then, when # type: ignore[import-untyped] from behave.runner import Context # type: ignore[import-untyped] from rich.console import Console from cleveragents.core.exceptions import CleverAgentsError from cleveragents.domain.models.core.container_lifecycle import ( ContainerLifecycleState, ) from cleveragents.resource.inheritance import ResourceTypeParentRemovalError # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- _PATCH_SVC = "cleveragents.cli.commands.resource._get_registry_service" _PATCH_CONSOLE = "cleveragents.cli.commands.resource.console" _PATCH_STOP = "cleveragents.cli.commands.resource.stop_container" _PATCH_REBUILD = "cleveragents.cli.commands.resource.rebuild_container" _PATCH_TRACKER = "cleveragents.cli.commands.resource.get_lifecycle_tracker" def _plain_console() -> Console: """A Console that never emits ANSI codes.""" return Console(no_color=True, highlight=False, force_terminal=False) def _capture(func: Any, *args: Any, **kwargs: Any) -> tuple[str, bool]: """Run a CLI function, capture its rich console output and success flag.""" import contextlib buf = StringIO() console = Console( file=buf, width=200, no_color=True, highlight=False, force_terminal=False ) import cleveragents.cli.commands.resource as mod orig = mod.console mod.console = console failed = False try: with contextlib.redirect_stdout(buf): func(*args, **kwargs) except SystemExit: failed = True except Exception: failed = True finally: mod.console = orig return buf.getvalue(), failed def _mock_resource( *, type_name: str = "git-checkout", location: str | None = "/tmp/mock", properties: dict[str, Any] | None = None, resource_id: str = "01HXYZ1234567890ABCDEFGHIJ", name: str = "local/mock-res", ) -> MagicMock: """Build a mock Resource domain object.""" res = MagicMock() res.resource_id = resource_id res.name = name res.resource_type_name = type_name res.classification = "physical" res.description = "A mock resource" res.location = location res.properties = properties if properties is not None else {"path": "/tmp/mock"} res.created_at = "2025-01-01T00:00:00" res.updated_at = "2025-01-01T00:00:00" return res def _mock_type_spec( *, built_in: bool = False, inherits: str | None = None ) -> MagicMock: """Build a mock ResourceTypeSpec.""" spec = MagicMock() spec.name = "test/mock-type" spec.built_in = built_in spec.description = "mock type" spec.resource_kind = "physical" spec.sandbox_strategy = "copy_on_write" spec.user_addable = True spec.cli_args = [] spec.parent_types = [] spec.child_types = [] spec.handler = None spec.capabilities = [] spec.inherits = inherits spec.equivalence = None return spec # =================================================================== # _resource_dict: str() fallback for dates (lines 159, 162) # =================================================================== @given("rccov3 a mock resource whose dates lack isoformat") def step_rccov3_resource_no_isoformat(context: Context) -> None: """Create a mock resource whose created_at/updated_at are plain ints (no isoformat).""" res = _mock_resource() # Replace datetime-like attrs with plain ints that have no isoformat method res.created_at = 1234567890 res.updated_at = 9876543210 context.rccov3_resource = res @when("rccov3 I call _resource_dict on it") def step_rccov3_call_resource_dict(context: Context) -> None: from cleveragents.cli.commands.resource import _resource_dict context.rccov3_result = _resource_dict(context.rccov3_resource) @then("rccov3 the result created_at should be a plain string") def step_rccov3_created_at_str(context: Context) -> None: val = context.rccov3_result["created_at"] assert val == "1234567890", f"Expected '1234567890', got {val!r}" @then("rccov3 the result updated_at should be a plain string") def step_rccov3_updated_at_str(context: Context) -> None: val = context.rccov3_result["updated_at"] assert val == "9876543210", f"Expected '9876543210', got {val!r}" # =================================================================== # type_remove: ResourceTypeParentRemovalError (lines 280-281) # =================================================================== @given("rccov3 a service whose remove_type raises ResourceTypeParentRemovalError") def step_rccov3_svc_parent_removal(context: Context) -> None: svc = MagicMock() svc.show_type.return_value = _mock_type_spec(built_in=False) svc.remove_type.side_effect = ResourceTypeParentRemovalError( "Cannot remove: type has subtypes" ) context.rccov3_svc = svc @when("rccov3 I invoke type_remove with yes flag") def step_rccov3_invoke_type_remove_yes(context: Context) -> None: from cleveragents.cli.commands.resource import type_remove with patch(_PATCH_SVC, return_value=context.rccov3_svc): output, failed = _capture(type_remove, name="test/mock-type", yes=True) context.rccov3_output = output context.rccov3_failed = failed # =================================================================== # type_remove: generic Exception (lines 288-289) # =================================================================== @given("rccov3 a service whose remove_type raises a RuntimeError") def step_rccov3_svc_remove_runtime(context: Context) -> None: svc = MagicMock() svc.show_type.return_value = _mock_type_spec(built_in=False) svc.remove_type.side_effect = RuntimeError("boom") context.rccov3_svc = svc # (reuses: rccov3 I invoke type_remove with yes flag) # =================================================================== # type_list: empty list (lines 310-311) # =================================================================== @given("rccov3 a service whose list_types returns empty") def step_rccov3_svc_list_empty(context: Context) -> None: svc = MagicMock() svc.list_types.return_value = [] context.rccov3_svc = svc @when("rccov3 I invoke type_list") def step_rccov3_invoke_type_list(context: Context) -> None: from cleveragents.cli.commands.resource import type_list with patch(_PATCH_SVC, return_value=context.rccov3_svc): output, failed = _capture(type_list, fmt="rich") context.rccov3_output = output context.rccov3_failed = failed # =================================================================== # type_list: generic Exception (lines 347-351) # =================================================================== @given("rccov3 a service whose list_types raises a RuntimeError") def step_rccov3_svc_list_runtime(context: Context) -> None: svc = MagicMock() svc.list_types.side_effect = RuntimeError("boom") context.rccov3_svc = svc # (reuses: rccov3 I invoke type_list) # =================================================================== # type_show: generic Exception (lines 388-392) # =================================================================== @given("rccov3 a service whose show_type raises a RuntimeError") def step_rccov3_svc_show_type_runtime(context: Context) -> None: svc = MagicMock() svc.show_type.side_effect = RuntimeError("boom") context.rccov3_svc = svc @when('rccov3 I invoke type_show for "{name}"') def step_rccov3_invoke_type_show(context: Context, name: str) -> None: from cleveragents.cli.commands.resource import type_show with patch(_PATCH_SVC, return_value=context.rccov3_svc): output, failed = _capture(type_show, name=name, fmt="rich") context.rccov3_output = output context.rccov3_failed = failed # =================================================================== # _print_type_panel: chain resolution failure (lines 420-422) # =================================================================== @given("rccov3 a type spec that inherits from another type") def step_rccov3_spec_inherits(context: Context) -> None: context.rccov3_spec = _mock_type_spec(inherits="base-type") @given("rccov3 a service whose resolve_type_inheritance_chain raises an exception") def step_rccov3_svc_chain_failure(context: Context) -> None: svc = MagicMock() svc.resolve_type_inheritance_chain.side_effect = RuntimeError("chain broken") context.rccov3_svc = svc @when("rccov3 I call _print_type_panel on the spec") def step_rccov3_call_print_type_panel(context: Context) -> None: from cleveragents.cli.commands.resource import _print_type_panel buf = StringIO() console = Console( file=buf, width=200, no_color=True, highlight=False, force_terminal=False ) import cleveragents.cli.commands.resource as mod orig = mod.console mod.console = console try: with patch(_PATCH_SVC, return_value=context.rccov3_svc): _print_type_panel(context.rccov3_spec) finally: mod.console = orig context.rccov3_output = buf.getvalue() @then('rccov3 the panel output should contain "..."') def step_rccov3_panel_contains_ellipsis(context: Context) -> None: assert "..." in context.rccov3_output, ( f"Expected '...' in panel output, got:\n{context.rccov3_output}" ) # =================================================================== # _format_properties: mounts JSON rendering (lines 465-471) # =================================================================== @when("rccov3 I call _format_properties with valid mounts JSON") def step_rccov3_format_props_mounts(context: Context) -> None: from cleveragents.cli.commands.resource import _format_properties mounts = [ {"source": "local/api-repo", "target": "/workspace", "mode": "rw"}, {"source": "/var/config", "target": "/config"}, ] props = {"mounts": json.dumps(mounts), "other_key": "other_val"} context.rccov3_formatted = _format_properties(props) @then('rccov3 the formatted output should contain "mount:"') def step_rccov3_formatted_mount(context: Context) -> None: assert "mount:" in context.rccov3_formatted, ( f"Expected 'mount:' in output:\n{context.rccov3_formatted}" ) @then('rccov3 the formatted output should contain "/workspace"') def step_rccov3_formatted_workspace(context: Context) -> None: assert "/workspace" in context.rccov3_formatted, ( f"Expected '/workspace' in output:\n{context.rccov3_formatted}" ) @when("rccov3 I call _format_properties with invalid mounts JSON") def step_rccov3_format_props_bad_mounts(context: Context) -> None: from cleveragents.cli.commands.resource import _format_properties props = {"mounts": "not-valid-json{{{"} context.rccov3_formatted = _format_properties(props) @then('rccov3 the formatted output should contain "mounts:"') def step_rccov3_formatted_mounts_key(context: Context) -> None: assert "mounts:" in context.rccov3_formatted, ( f"Expected 'mounts:' in output:\n{context.rccov3_formatted}" ) # =================================================================== # resource_add: image property (line 622) # =================================================================== @given("rccov3 a service that successfully registers a resource") def step_rccov3_svc_register_ok(context: Context) -> None: svc = MagicMock() svc.register_resource.return_value = _mock_resource( type_name="container-instance", name="local/img-test", ) context.rccov3_svc = svc @when('rccov3 I invoke resource_add with image "ubuntu:latest"') def step_rccov3_invoke_add_image(context: Context) -> None: from cleveragents.cli.commands.resource import resource_add with patch(_PATCH_SVC, return_value=context.rccov3_svc): output, failed = _capture( resource_add, type_name="container-instance", name="local/img-test", path=None, branch=None, description=None, image="ubuntu:latest", mount=None, clone_into=None, read_only=False, fmt="rich", ) context.rccov3_output = output context.rccov3_failed = failed @then("rccov3 the service register_resource was called with image in properties") def step_rccov3_verify_image_prop(context: Context) -> None: call_kwargs = context.rccov3_svc.register_resource.call_args props = call_kwargs.kwargs.get("properties") or call_kwargs[1].get("properties") assert props is not None and "image" in props, ( f"Expected 'image' in properties, got: {props}" ) assert props["image"] == "ubuntu:latest" # =================================================================== # resource_add: mount on non-container type (lines 624-626, 629-630) # =================================================================== @when("rccov3 I invoke resource_add with mount on git-checkout type") def step_rccov3_invoke_add_mount_bad_type(context: Context) -> None: from cleveragents.cli.commands.resource import resource_add with patch(_PATCH_SVC, return_value=context.rccov3_svc): output, failed = _capture( resource_add, type_name="git-checkout", name="local/bad-mount", path="/tmp/x", branch=None, description=None, image=None, mount=["local/repo:/workspace"], clone_into=None, read_only=False, fmt="rich", ) context.rccov3_output = output context.rccov3_failed = failed # =================================================================== # resource_list: generic Exception (lines 729-733) # =================================================================== @given("rccov3 a service whose list_resources raises a RuntimeError") def step_rccov3_svc_list_resources_runtime(context: Context) -> None: svc = MagicMock() svc.list_resources.side_effect = RuntimeError("boom") context.rccov3_svc = svc @when("rccov3 I invoke resource_list") def step_rccov3_invoke_resource_list(context: Context) -> None: from cleveragents.cli.commands.resource import resource_list with patch(_PATCH_SVC, return_value=context.rccov3_svc): output, failed = _capture(resource_list, type_filter=None, fmt="rich") context.rccov3_output = output context.rccov3_failed = failed # =================================================================== # resource_show: generic Exception (lines 786-790) # =================================================================== @given("rccov3 a service whose show_resource raises a RuntimeError") def step_rccov3_svc_show_resource_runtime(context: Context) -> None: svc = MagicMock() svc.show_resource.side_effect = RuntimeError("boom") context.rccov3_svc = svc @when('rccov3 I invoke resource_show for "{name}"') def step_rccov3_invoke_resource_show(context: Context, name: str) -> None: from cleveragents.cli.commands.resource import resource_show with patch(_PATCH_SVC, return_value=context.rccov3_svc): output, failed = _capture(resource_show, resource=name, fmt="rich") context.rccov3_output = output context.rccov3_failed = failed # =================================================================== # resource_tree: generic Exception (lines 847-851) # =================================================================== @given("rccov3 a service whose get_resource_tree raises a RuntimeError") def step_rccov3_svc_tree_runtime(context: Context) -> None: svc = MagicMock() svc.get_resource_tree.side_effect = RuntimeError("boom") context.rccov3_svc = svc @when('rccov3 I invoke resource_tree for "{name}"') def step_rccov3_invoke_resource_tree(context: Context, name: str) -> None: from cleveragents.cli.commands.resource import resource_tree with patch(_PATCH_SVC, return_value=context.rccov3_svc): output, failed = _capture( resource_tree, resource=name, depth=-1, type_filter=None, fmt="rich" ) context.rccov3_output = output context.rccov3_failed = failed # =================================================================== # resource_inspect: empty tree_data (line 950) # =================================================================== @given( "rccov3 a service whose show_resource returns a mock and get_resource_tree returns empty" ) def step_rccov3_svc_inspect_empty_tree(context: Context) -> None: svc = MagicMock() svc.show_resource.return_value = _mock_resource() svc.get_resource_tree.return_value = [] # empty tree context.rccov3_svc = svc @when("rccov3 I invoke resource_inspect with tree flag and json format") def step_rccov3_invoke_inspect_tree_json(context: Context) -> None: from cleveragents.cli.commands.resource import resource_inspect with patch(_PATCH_SVC, return_value=context.rccov3_svc): output, failed = _capture( resource_inspect, resource="local/mock-res", tree=True, file=None, fmt="json", ) context.rccov3_output = output context.rccov3_failed = failed # =================================================================== # resource_inspect: generic Exception (lines 1004-1008) # =================================================================== @when('rccov3 I invoke resource_inspect for "{name}"') def step_rccov3_invoke_resource_inspect(context: Context, name: str) -> None: from cleveragents.cli.commands.resource import resource_inspect with patch(_PATCH_SVC, return_value=context.rccov3_svc): output, failed = _capture( resource_inspect, resource=name, tree=False, file=None, fmt="rich" ) context.rccov3_output = output context.rccov3_failed = failed # =================================================================== # _read_resource_file: path from properties (line 1019) # =================================================================== @given("rccov3 a mock resource with no location but properties path") def step_rccov3_resource_no_location(context: Context) -> None: # Create a temp directory with a test file tmpdir = tempfile.mkdtemp() test_file = os.path.join(tmpdir, "hello.txt") with open(test_file, "w") as f: f.write("hello from test") context.rccov3_resource = _mock_resource( location=None, properties={"path": tmpdir}, ) context.rccov3_tmpdir = tmpdir @when("rccov3 I call _read_resource_file with a valid relative path") def step_rccov3_call_read_resource_file(context: Context) -> None: from cleveragents.cli.commands.resource import _read_resource_file context.rccov3_file_result = _read_resource_file( context.rccov3_resource, "hello.txt" ) @then("rccov3 the file read result should contain expected content") def step_rccov3_file_result_ok(context: Context) -> None: assert "hello from test" in context.rccov3_file_result, ( f"Expected 'hello from test', got: {context.rccov3_file_result!r}" ) # =================================================================== # _read_resource_file: path traversal rejection (line 1028) # =================================================================== @given("rccov3 a mock resource with a known location") def step_rccov3_resource_known_location(context: Context) -> None: tmpdir = tempfile.mkdtemp() context.rccov3_resource = _mock_resource(location=tmpdir) @when("rccov3 I call _read_resource_file with a traversal path") def step_rccov3_call_read_file_traversal(context: Context) -> None: from cleveragents.cli.commands.resource import _read_resource_file context.rccov3_file_result = _read_resource_file( context.rccov3_resource, "../../etc/passwd" ) @then('rccov3 the file read result should contain "path traversal rejected"') def step_rccov3_file_traversal_rejected(context: Context) -> None: assert "path traversal rejected" in context.rccov3_file_result, ( f"Expected 'path traversal rejected', got: {context.rccov3_file_result!r}" ) # =================================================================== # resource_link_child: generic Exception (lines 1091-1095) # =================================================================== @given("rccov3 a service whose link_child raises a RuntimeError") def step_rccov3_svc_link_runtime(context: Context) -> None: svc = MagicMock() svc.link_child.side_effect = RuntimeError("boom") context.rccov3_svc = svc @when("rccov3 I invoke resource_link_child") def step_rccov3_invoke_link_child(context: Context) -> None: from cleveragents.cli.commands.resource import resource_link_child with patch(_PATCH_SVC, return_value=context.rccov3_svc): output, failed = _capture( resource_link_child, parent="local/a", child="local/b", fmt="rich" ) context.rccov3_output = output context.rccov3_failed = failed # =================================================================== # resource_unlink_child: generic Exception (lines 1162-1163) # =================================================================== @given("rccov3 a service whose unlink_child raises a RuntimeError") def step_rccov3_svc_unlink_runtime(context: Context) -> None: svc = MagicMock() svc.unlink_child.side_effect = RuntimeError("boom") context.rccov3_svc = svc @when("rccov3 I invoke resource_unlink_child with yes flag") def step_rccov3_invoke_unlink_child(context: Context) -> None: from cleveragents.cli.commands.resource import resource_unlink_child with patch(_PATCH_SVC, return_value=context.rccov3_svc): output, failed = _capture( resource_unlink_child, parent="local/a", child="local/b", yes=True, fmt="rich", ) context.rccov3_output = output context.rccov3_failed = failed # =================================================================== # resource_stop: confirmation prompt (lines 1314-1316) # =================================================================== def _make_running_devcontainer_svc() -> tuple[MagicMock, MagicMock]: """Return (service, tracker) for a running devcontainer resource.""" svc = MagicMock() res = _mock_resource(type_name="devcontainer-instance", name="local/dc") svc.show_resource.return_value = res tracker = MagicMock() tracker.current_state = ContainerLifecycleState.RUNNING return svc, tracker @given("rccov3 a running devcontainer resource for stop") def step_rccov3_running_dc_stop(context: Context) -> None: svc, tracker = _make_running_devcontainer_svc() context.rccov3_svc = svc context.rccov3_tracker = tracker @when("rccov3 I invoke resource_stop without yes flag and confirm") def step_rccov3_invoke_stop_confirm(context: Context) -> None: from cleveragents.cli.commands.resource import resource_stop with ( patch(_PATCH_SVC, return_value=context.rccov3_svc), patch(_PATCH_TRACKER, return_value=context.rccov3_tracker), patch(_PATCH_STOP), patch("cleveragents.cli.commands.resource.typer.confirm"), ): output, failed = _capture(resource_stop, name="local/dc", yes=False) context.rccov3_output = output context.rccov3_failed = failed # =================================================================== # resource_stop: CleverAgentsError (lines 1333-1335) # =================================================================== @given("rccov3 a service whose show_resource raises CleverAgentsError") def step_rccov3_svc_show_clever_error(context: Context) -> None: svc = MagicMock() svc.show_resource.side_effect = CleverAgentsError(message="service failure") context.rccov3_svc = svc @when('rccov3 I invoke resource_stop for "{name}"') def step_rccov3_invoke_stop(context: Context, name: str) -> None: from cleveragents.cli.commands.resource import resource_stop with patch(_PATCH_SVC, return_value=context.rccov3_svc): output, failed = _capture(resource_stop, name=name, yes=True) context.rccov3_output = output context.rccov3_failed = failed # =================================================================== # resource_stop: generic Exception (lines 1336-1340) # — reuses "rccov3 a service whose show_resource raises a RuntimeError" # — reuses "rccov3 I invoke resource_stop for ..." # =================================================================== # =================================================================== # resource_stop: generic Exception (lines 1336-1340) # — needs a non-RuntimeError, non-ValueError, non-CleverAgentsError # exception so it falls through to the bare except handler. # =================================================================== @given("rccov3 a service whose show_resource raises a TypeError for stop") def step_rccov3_svc_show_typeerror_stop(context: Context) -> None: svc = MagicMock() svc.show_resource.side_effect = TypeError("unexpected type") context.rccov3_svc = svc # (reuses: rccov3 I invoke resource_stop for ...) # =================================================================== # resource_rebuild: generic Exception (lines 1415-1419) # =================================================================== @given("rccov3 a service whose show_resource raises a TypeError for rebuild") def step_rccov3_svc_show_typeerror_rebuild(context: Context) -> None: svc = MagicMock() svc.show_resource.side_effect = TypeError("unexpected type") context.rccov3_svc = svc # (reuses: rccov3 I invoke resource_rebuild for ...) # =================================================================== # resource_rebuild: path from properties (lines 1389-1390) # =================================================================== def _make_stopped_devcontainer_svc( *, location: str | None = "/project", properties: dict[str, Any] | None = None ) -> tuple[MagicMock, MagicMock]: """Return (service, tracker) for a stopped devcontainer resource.""" svc = MagicMock() props = properties if properties is not None else {"path": "/project"} res = _mock_resource( type_name="devcontainer-instance", name="local/dc", location=location, properties=props, ) svc.show_resource.return_value = res tracker = MagicMock() tracker.current_state = ContainerLifecycleState.STOPPED return svc, tracker @given("rccov3 a stopped devcontainer resource with no location but properties path") def step_rccov3_stopped_dc_no_location(context: Context) -> None: svc, tracker = _make_stopped_devcontainer_svc( location=None, properties={"path": "/project/from/props"}, ) context.rccov3_svc = svc context.rccov3_tracker = tracker @when("rccov3 I invoke resource_rebuild with yes flag") def step_rccov3_invoke_rebuild_yes(context: Context) -> None: from cleveragents.cli.commands.resource import resource_rebuild with ( patch(_PATCH_SVC, return_value=context.rccov3_svc), patch(_PATCH_TRACKER, return_value=context.rccov3_tracker), patch(_PATCH_REBUILD), ): output, failed = _capture(resource_rebuild, name="local/dc", yes=True) context.rccov3_output = output context.rccov3_failed = failed # =================================================================== # resource_rebuild: confirmation prompt (lines 1397-1399) # =================================================================== @given("rccov3 a stopped devcontainer resource for rebuild") def step_rccov3_stopped_dc_rebuild(context: Context) -> None: svc, tracker = _make_stopped_devcontainer_svc() context.rccov3_svc = svc context.rccov3_tracker = tracker @when("rccov3 I invoke resource_rebuild without yes flag and confirm") def step_rccov3_invoke_rebuild_confirm(context: Context) -> None: from cleveragents.cli.commands.resource import resource_rebuild with ( patch(_PATCH_SVC, return_value=context.rccov3_svc), patch(_PATCH_TRACKER, return_value=context.rccov3_tracker), patch(_PATCH_REBUILD), patch("cleveragents.cli.commands.resource.typer.confirm"), ): output, failed = _capture(resource_rebuild, name="local/dc", yes=False) context.rccov3_output = output context.rccov3_failed = failed # =================================================================== # resource_rebuild: CleverAgentsError (lines 1412-1414) # =================================================================== # Reuses: "rccov3 a service whose show_resource raises CleverAgentsError" @when('rccov3 I invoke resource_rebuild for "{name}"') def step_rccov3_invoke_rebuild(context: Context, name: str) -> None: from cleveragents.cli.commands.resource import resource_rebuild with patch(_PATCH_SVC, return_value=context.rccov3_svc): output, failed = _capture(resource_rebuild, name=name, yes=True) context.rccov3_output = output context.rccov3_failed = failed # =================================================================== # resource_rebuild: generic Exception (lines 1415-1419) # — reuses "rccov3 a service whose show_resource raises a RuntimeError" # — reuses "rccov3 I invoke resource_rebuild for ..." # =================================================================== # =================================================================== # Shared Then steps # =================================================================== @then("rccov3 the command should have failed") def step_rccov3_failed(context: Context) -> None: assert context.rccov3_failed, ( f"Expected command to fail, output was:\n{context.rccov3_output}" ) @then('rccov3 the output should contain "{text}"') def step_rccov3_output_contains(context: Context, text: str) -> None: output = context.rccov3_output assert text.lower() in output.lower(), ( f"Expected '{text}' in output, got:\n{output}" )