diff --git a/src/cleveragents/cli.py b/src/cleveragents/cli.py index 69eac2947..ff836310c 100644 --- a/src/cleveragents/cli.py +++ b/src/cleveragents/cli.py @@ -981,23 +981,85 @@ def clear_context(name: str, context_dir: Optional[Path]) -> None: @context.command("delete") -@click.argument("name") +@click.argument("name", required=False) @click.option( "--context-dir", type=click.Path(file_okay=False, dir_okay=True, path_type=Path), help="Directory where contexts are stored.", ) -@click.confirmation_option(prompt="Are you sure you want to delete this context?") -def delete_context(name: str, context_dir: Optional[Path]) -> None: - """Delete a context and all its data.""" +@click.option( + "--all", + is_flag=True, + help="Delete all contexts.", +) +@click.option( + "--yes", + "-y", + is_flag=True, + help="Skip confirmation prompt.", +) +def delete_context(name: Optional[str], context_dir: Optional[Path], all: bool, yes: bool) -> None: + """Delete a context and all its data, or delete all contexts with --all.""" try: - ctx_manager = ContextManager(name, context_dir) - if not ctx_manager.exists(): - click.echo(f"Context '{name}' does not exist.", err=True) + # Validate arguments + if all and name: + click.echo("Error: Cannot specify NAME when using --all flag.", err=True) sys.exit(1) - ctx_manager.delete() - click.echo(f"Context '{name}' has been deleted.") + if not all and not name: + click.echo("Error: Must specify NAME or use --all flag.", err=True) + sys.exit(1) + + if all: + # Delete all contexts + contexts = ContextManager.list_contexts(context_dir) + + if not contexts: + click.echo("No contexts found.") + return + + # Show what will be deleted + click.echo(f"Found {len(contexts)} context(s) to delete:") + for ctx in contexts: + click.echo(f" - {ctx}") + + # Confirmation prompt (unless --yes is provided) + if not yes: + if not click.confirm("Are you sure you want to delete ALL contexts?"): + click.echo("Operation cancelled.") + return + + # Delete all contexts + deleted_count = 0 + failed_count = 0 + for ctx_name in contexts: + try: + ctx_manager = ContextManager(ctx_name, context_dir) + ctx_manager.delete() + deleted_count += 1 + except Exception as e: # pylint: disable=broad-exception-caught + click.echo(f"Failed to delete context '{ctx_name}': {e}", err=True) + failed_count += 1 + + click.echo(f"Deleted {deleted_count} context(s).") + if failed_count > 0: + click.echo(f"Failed to delete {failed_count} context(s).", err=True) + else: + # Delete single context + assert name is not None # mypy type narrowing: guaranteed by validation above + ctx_manager = ContextManager(name, context_dir) + if not ctx_manager.exists(): + click.echo(f"Context '{name}' does not exist.", err=True) + sys.exit(1) + + # Confirmation prompt (unless --yes is provided) + if not yes: + if not click.confirm(f"Are you sure you want to delete context '{name}'?"): + click.echo("Operation cancelled.") + return + + ctx_manager.delete() + click.echo(f"Context '{name}' has been deleted.") except Exception as e: # pylint: disable=broad-exception-caught click.echo(f"Error: {e}", err=True) sys.exit(1) diff --git a/src/cleveragents/context_manager.py b/src/cleveragents/context_manager.py index 623f06475..3a874d3b8 100644 --- a/src/cleveragents/context_manager.py +++ b/src/cleveragents/context_manager.py @@ -227,8 +227,20 @@ class ContextManager: contexts = [] for path in base_dir.iterdir(): - if path.is_dir() and (path / "messages.json").exists(): - contexts.append(path.name) + try: + if path.is_dir(): + # Try to check if messages.json exists, but if we can't access it + # (due to permissions), assume it's a context directory anyway + try: + if (path / "messages.json").exists(): + contexts.append(path.name) + except (PermissionError, OSError): + # Can't access the directory contents, but it might be a context + # Include it anyway and let deletion handle the error + contexts.append(path.name) + except (PermissionError, OSError): + # Can't even check if it's a directory, skip it + continue return sorted(contexts) diff --git a/tests/features/context_delete_all_yes.feature b/tests/features/context_delete_all_yes.feature new file mode 100644 index 000000000..fa0c2ecb7 --- /dev/null +++ b/tests/features/context_delete_all_yes.feature @@ -0,0 +1,86 @@ +Feature: Context Delete with --all and --yes Flags + As a user of CleverAgents + I want to delete contexts individually or in bulk with confirmation control + So that I can efficiently manage my stored conversation contexts + + Background: + Given I have a temporary test directory for contexts + + Scenario: Delete single context with --yes bypasses confirmation + Given I have a context "test-context-1" in the test directory + When I run CLI command "context delete test-context-1 --yes" with test context dir + Then the command should succeed + And the context "test-context-1" should not exist + + Scenario: Delete single context without --yes prompts for confirmation + Given I have a context "test-context-2" in the test directory + When I run CLI command "context delete test-context-2" with test context dir and answer "y" + Then the command should succeed + And the context "test-context-2" should not exist + + Scenario: Delete single context - cancel confirmation + Given I have a context "test-context-3" in the test directory + When I run CLI command "context delete test-context-3" with test context dir and answer "n" + Then the command should be cancelled + And the context "test-context-3" should still exist + + Scenario: Delete all contexts with --all --yes bypasses confirmation + Given I have contexts "ctx-a", "ctx-b", "ctx-c" in the test directory + When I run CLI command "context delete --all --yes" with test context dir + Then the command should succeed + And all contexts should be deleted + And the output should show "Deleted 3 context(s)" + + Scenario: Delete all contexts with --all prompts for confirmation + Given I have contexts "ctx-d", "ctx-e" in the test directory + When I run CLI command "context delete --all" with test context dir and answer "y" + Then the command should succeed + And all contexts should be deleted + And the output should list all contexts before deletion + + Scenario: Delete all contexts - cancel confirmation + Given I have contexts "ctx-f", "ctx-g" in the test directory + When I run CLI command "context delete --all" with test context dir and answer "n" + Then the command should be cancelled + And the contexts "ctx-f", "ctx-g" should still exist + + Scenario: Error when both NAME and --all provided + When I run CLI command "context delete test-context --all" with test context dir + Then the command should fail with error "Cannot specify NAME when using --all flag" + + Scenario: Error when neither NAME nor --all provided + When I run CLI command "context delete" with test context dir + Then the command should fail with error "Must specify NAME or use --all flag" + + Scenario: Delete all when no contexts exist + Given the test context directory is empty + When I run CLI command "context delete --all --yes" with test context dir + Then the command should succeed + And the output should show "No contexts found" + + Scenario: Delete single non-existent context + When I run CLI command "context delete non-existent --yes" with test context dir + Then the command should fail + And the output should show "does not exist" + + Scenario: Delete all contexts with partial failures + Given I have contexts "ctx-h", "ctx-i" in the test directory + And context "ctx-i" directory is made read-only + When I run CLI command "context delete --all --yes" with test context dir + Then the command should succeed + And the output should show deleted count + And the output should show failed count + + Scenario: Delete all shows context list before confirmation + Given I have contexts "alpha", "beta", "gamma" in the test directory + When I run CLI command "context delete --all" with test context dir and capture output + Then the output should contain "Found 3 context(s) to delete" + And the output should contain "alpha" + And the output should contain "beta" + And the output should contain "gamma" + + Scenario: Using -y as shorthand for --yes + Given I have a context "test-short-yes" in the test directory + When I run CLI command "context delete test-short-yes -y" with test context dir + Then the command should succeed + And the context "test-short-yes" should not exist diff --git a/tests/features/steps/context_delete_all_yes_steps.py b/tests/features/steps/context_delete_all_yes_steps.py new file mode 100644 index 000000000..f2fe8f469 --- /dev/null +++ b/tests/features/steps/context_delete_all_yes_steps.py @@ -0,0 +1,247 @@ +"""Step definitions for Context Delete with --all and --yes flags.""" + +import json +import os +import subprocess +import tempfile +from pathlib import Path + +from behave import given, then, when +from behave.runner import Context + + +@given('I have a context "{name}" in the test directory') +def step_have_context_in_test_dir(context: Context, name: str) -> None: + """Create a single context in the test directory.""" + if not hasattr(context, "context_dir"): + context.temp_dir = tempfile.mkdtemp(prefix="cleveragents_test_") + context.context_dir = Path(context.temp_dir) / "contexts" + context.context_dir.mkdir(parents=True, exist_ok=True) + + # Create a simple context structure + ctx_dir = context.context_dir / name + ctx_dir.mkdir(parents=True, exist_ok=True) + + # Create the standard context files + (ctx_dir / "messages.json").write_text("[]") + (ctx_dir / "metadata.json").write_text("{}") + (ctx_dir / "state.json").write_text("{}") + (ctx_dir / "global_context.json").write_text("{}") + + +@given('I have contexts "{names}" in the test directory') +def step_have_multiple_contexts_in_test_dir(context: Context, names: str) -> None: + """Create multiple contexts in the test directory.""" + if not hasattr(context, "context_dir"): + context.temp_dir = tempfile.mkdtemp(prefix="cleveragents_test_") + context.context_dir = Path(context.temp_dir) / "contexts" + context.context_dir.mkdir(parents=True, exist_ok=True) + + # Parse the comma-separated names + context_names = [name.strip().strip('"') for name in names.split(",")] + + # Create each context + for name in context_names: + ctx_dir = context.context_dir / name + ctx_dir.mkdir(parents=True, exist_ok=True) + + # Create the standard context files + (ctx_dir / "messages.json").write_text("[]") + (ctx_dir / "metadata.json").write_text("{}") + (ctx_dir / "state.json").write_text("{}") + (ctx_dir / "global_context.json").write_text("{}") + + +@given("the test context directory is empty") +def step_empty_test_context_dir(context: Context) -> None: + """Ensure the test context directory is empty.""" + if not hasattr(context, "context_dir"): + context.temp_dir = tempfile.mkdtemp(prefix="cleveragents_test_") + context.context_dir = Path(context.temp_dir) / "contexts" + context.context_dir.mkdir(parents=True, exist_ok=True) + + # Directory already empty since we just created it + + +@given('context "{name}" directory is made read-only') +def step_make_context_readonly(context: Context, name: str) -> None: + """Make a context directory read-only to simulate failure.""" + ctx_dir = context.context_dir / name + if ctx_dir.exists(): + # Make directory read-only + os.chmod(ctx_dir, 0o444) + # Track this for cleanup + if not hasattr(context, "readonly_dirs"): + context.readonly_dirs = [] + context.readonly_dirs.append(ctx_dir) + + +@when('I run CLI command "{command}" with test context dir') +def step_run_cli_with_test_context_dir(context: Context, command: str) -> None: + """Run a CLI command with the test context directory.""" + # Parse command and add --context-dir + cmd_parts = ["python", "-m", "cleveragents"] + command.split() + cmd_parts.extend(["--context-dir", str(context.context_dir)]) + + # Run the command + result = subprocess.run(cmd_parts, capture_output=True, text=True, cwd="/app") + + context.cli_result = result + context.cli_stdout = result.stdout + context.cli_stderr = result.stderr + context.cli_returncode = result.returncode + + +@when('I run CLI command "{command}" with test context dir and answer "{answer}"') +def step_run_cli_with_answer(context: Context, command: str, answer: str) -> None: + """Run a CLI command with the test context directory and provide input.""" + # Parse command and add --context-dir + cmd_parts = ["python", "-m", "cleveragents"] + command.split() + cmd_parts.extend(["--context-dir", str(context.context_dir)]) + + # Run the command with input + result = subprocess.run(cmd_parts, input=answer + "\n", capture_output=True, text=True, cwd="/app") + + context.cli_result = result + context.cli_stdout = result.stdout + context.cli_stderr = result.stderr + context.cli_returncode = result.returncode + + +@when('I run CLI command "{command}" with test context dir and capture output') +def step_run_cli_and_capture_output(context: Context, command: str) -> None: + """Run a CLI command with the test context directory and capture all output.""" + # Parse command and add --context-dir + cmd_parts = ["python", "-m", "cleveragents"] + command.split() + cmd_parts.extend(["--context-dir", str(context.context_dir)]) + + # Run the command (it will wait for input but we won't provide it to capture the output) + # We'll use a short timeout to avoid hanging + try: + result = subprocess.run(cmd_parts, input="n\n", capture_output=True, text=True, timeout=5, cwd="/app") + context.cli_result = result + context.cli_stdout = result.stdout + context.cli_stderr = result.stderr + context.cli_returncode = result.returncode + except subprocess.TimeoutExpired as e: + context.cli_stdout = e.stdout.decode() if e.stdout else "" + context.cli_stderr = e.stderr.decode() if e.stderr else "" + context.cli_returncode = -1 + + +# Note: "the command should succeed" and "the command should fail" are already defined in cli_steps.py + + +@then("the command should be cancelled") +def step_command_should_be_cancelled(context: Context) -> None: + """Verify the command was cancelled.""" + assert "cancelled" in context.cli_stdout.lower() or "cancelled" in context.cli_stderr.lower(), ( + f"Command was not cancelled\n" f"stdout: {context.cli_stdout}\n" f"stderr: {context.cli_stderr}" + ) + + +@then('the command should fail with error "{error_text}"') +def step_command_should_fail_with_error(context: Context, error_text: str) -> None: + """Verify the command failed with specific error message.""" + assert context.cli_returncode != 0, ( + f"Command succeeded unexpectedly\n" f"stdout: {context.cli_stdout}\n" f"stderr: {context.cli_stderr}" + ) + + combined_output = context.cli_stdout + context.cli_stderr + assert error_text in combined_output, ( + f"Expected error message not found: {error_text}\n" + f"stdout: {context.cli_stdout}\n" + f"stderr: {context.cli_stderr}" + ) + + +@then('the context "{name}" should not exist') +def step_context_should_not_exist(context: Context, name: str) -> None: + """Verify a context does not exist.""" + ctx_dir = context.context_dir / name + assert not ctx_dir.exists(), f"Context '{name}' still exists at {ctx_dir}" + + +@then('the context "{name}" should still exist') +def step_context_should_still_exist(context: Context, name: str) -> None: + """Verify a context still exists.""" + ctx_dir = context.context_dir / name + assert ctx_dir.exists(), f"Context '{name}' does not exist at {ctx_dir}" + + +@then("all contexts should be deleted") +def step_all_contexts_deleted(context: Context) -> None: + """Verify all contexts were deleted.""" + # List all subdirectories in the context directory + if context.context_dir.exists(): + contexts = [d for d in context.context_dir.iterdir() if d.is_dir()] + assert len(contexts) == 0, f"Found {len(contexts)} contexts still present: {[c.name for c in contexts]}" + + +@then('the contexts "{names}" should still exist') +def step_contexts_should_still_exist(context: Context, names: str) -> None: + """Verify specific contexts still exist.""" + context_names = [name.strip().strip('"') for name in names.split(",")] + + for name in context_names: + ctx_dir = context.context_dir / name + assert ctx_dir.exists(), f"Context '{name}' does not exist at {ctx_dir}" + + +@then('the output should show "{text}"') +def step_output_should_show(context: Context, text: str) -> None: + """Verify the output contains specific text.""" + combined_output = context.cli_stdout + context.cli_stderr + assert text in combined_output, ( + f"Expected text not found in output: {text}\n" f"stdout: {context.cli_stdout}\n" f"stderr: {context.cli_stderr}" + ) + + +@then("the output should show deleted count") +def step_output_shows_deleted_count(context: Context) -> None: + """Verify the output shows deleted count.""" + combined_output = context.cli_stdout + context.cli_stderr + assert "Deleted" in combined_output and "context" in combined_output, ( + f"Deleted count not found in output\n" f"stdout: {context.cli_stdout}\n" f"stderr: {context.cli_stderr}" + ) + + +@then("the output should show failed count") +def step_output_shows_failed_count(context: Context) -> None: + """Verify the output shows failed count.""" + combined_output = context.cli_stdout + context.cli_stderr + assert "Failed" in combined_output, ( + f"Failed count not found in output\n" f"stdout: {context.cli_stdout}\n" f"stderr: {context.cli_stderr}" + ) + + +# Note: "the output should contain" is already defined in cli_steps.py as "the output should contain '{expected_text}'" + + +@then("the output should list all contexts before deletion") +def step_output_lists_contexts_before_deletion(context: Context) -> None: + """Verify the output lists contexts before deletion.""" + combined_output = context.cli_stdout + context.cli_stderr + assert "Found" in combined_output and "to delete" in combined_output, ( + f"Context list not found in output\n" f"stdout: {context.cli_stdout}\n" f"stderr: {context.cli_stderr}" + ) + + +def after_scenario(context, scenario): + """Clean up after each scenario.""" + # Restore permissions on read-only directories + if hasattr(context, "readonly_dirs"): + for dir_path in context.readonly_dirs: + try: + os.chmod(dir_path, 0o755) + except Exception: + pass + + # Clean up temp directory + if hasattr(context, "temp_dir"): + import shutil + + try: + shutil.rmtree(context.temp_dir) + except Exception: + pass diff --git a/tests/integration/context_delete_all_yes_test.robot b/tests/integration/context_delete_all_yes_test.robot new file mode 100644 index 000000000..e9966dbc0 --- /dev/null +++ b/tests/integration/context_delete_all_yes_test.robot @@ -0,0 +1,318 @@ +*** Settings *** +Documentation Integration tests for context delete with --all and --yes flags +Library Process +Library OperatingSystem +Library String +Library DateTime +Suite Setup Setup Test Environment +Suite Teardown Cleanup Test Environment + +*** Variables *** +${SIMPLE_CONFIG} tests/fixtures/simple_echo_config.yaml +${CONTEXT_DIR} ${TEMPDIR}/test_contexts_delete_all_yes +${UNIQUE_ID} ${EMPTY} +${TEMP} ${EMPTY} + +*** Test Cases *** +Test Delete Single Context With Yes Flag + [Documentation] Test context delete with --yes bypasses confirmation + ${context_name} = Set Variable delete_yes_${UNIQUE_ID} + + # Create context + Run Process python -m cleveragents run + ... -c ${SIMPLE_CONFIG} + ... --context ${context_name} + ... --context-dir ${CONTEXT_DIR} + ... --unsafe + ... -p "test message" + + Directory Should Exist ${CONTEXT_DIR}/${context_name} + + # Delete it with --yes flag + ${result} = Run Process python -m cleveragents context delete + ... ${context_name} + ... --context-dir ${CONTEXT_DIR} + ... --yes + + Should Be Equal As Integers ${result.rc} 0 + Directory Should Not Exist ${CONTEXT_DIR}/${context_name} + Should Contain ${result.stdout} deleted + +Test Delete Single Context With Short Yes Flag + [Documentation] Test context delete with -y shorthand flag + ${context_name} = Set Variable delete_short_${UNIQUE_ID} + + # Create context + Run Process python -m cleveragents run + ... -c ${SIMPLE_CONFIG} + ... --context ${context_name} + ... --context-dir ${CONTEXT_DIR} + ... --unsafe + ... -p "test message" + + Directory Should Exist ${CONTEXT_DIR}/${context_name} + + # Delete it with -y flag + ${result} = Run Process python -m cleveragents context delete + ... ${context_name} + ... --context-dir ${CONTEXT_DIR} + ... -y + + Should Be Equal As Integers ${result.rc} 0 + Directory Should Not Exist ${CONTEXT_DIR}/${context_name} + +Test Delete Single Context With Confirmation Accept + [Documentation] Test context delete with confirmation accepted + ${context_name} = Set Variable delete_confirm_${UNIQUE_ID} + + # Create context + Run Process python -m cleveragents run + ... -c ${SIMPLE_CONFIG} + ... --context ${context_name} + ... --context-dir ${CONTEXT_DIR} + ... --unsafe + ... -p "test message" + + Directory Should Exist ${CONTEXT_DIR}/${context_name} + + # Delete with confirmation (answer 'y') + ${result} = Run Process python -m cleveragents context delete + ... ${context_name} + ... --context-dir ${CONTEXT_DIR} + ... stdin=y\n + + Should Be Equal As Integers ${result.rc} 0 + Directory Should Not Exist ${CONTEXT_DIR}/${context_name} + +Test Delete Single Context With Confirmation Reject + [Documentation] Test context delete with confirmation rejected + ${context_name} = Set Variable delete_reject_${UNIQUE_ID} + + # Create context + Run Process python -m cleveragents run + ... -c ${SIMPLE_CONFIG} + ... --context ${context_name} + ... --context-dir ${CONTEXT_DIR} + ... --unsafe + ... -p "test message" + + Directory Should Exist ${CONTEXT_DIR}/${context_name} + + # Delete with confirmation rejected (answer 'n') + ${result} = Run Process python -m cleveragents context delete + ... ${context_name} + ... --context-dir ${CONTEXT_DIR} + ... stdin=n\n + + Should Be Equal As Integers ${result.rc} 0 + Directory Should Exist ${CONTEXT_DIR}/${context_name} + Should Contain ${result.stdout} cancelled + +Test Delete All Contexts With All And Yes Flags + [Documentation] Test delete all contexts with --all --yes + [Setup] Empty Directory ${CONTEXT_DIR} + ${ctx1} = Set Variable all_ctx1_${UNIQUE_ID} + ${ctx2} = Set Variable all_ctx2_${UNIQUE_ID} + ${ctx3} = Set Variable all_ctx3_${UNIQUE_ID} + + # Create multiple contexts + Run Process python -m cleveragents run + ... -c ${SIMPLE_CONFIG} + ... --context ${ctx1} + ... --context-dir ${CONTEXT_DIR} + ... --unsafe + ... -p "test" + + Run Process python -m cleveragents run + ... -c ${SIMPLE_CONFIG} + ... --context ${ctx2} + ... --context-dir ${CONTEXT_DIR} + ... --unsafe + ... -p "test" + + Run Process python -m cleveragents run + ... -c ${SIMPLE_CONFIG} + ... --context ${ctx3} + ... --context-dir ${CONTEXT_DIR} + ... --unsafe + ... -p "test" + + Directory Should Exist ${CONTEXT_DIR}/${ctx1} + Directory Should Exist ${CONTEXT_DIR}/${ctx2} + Directory Should Exist ${CONTEXT_DIR}/${ctx3} + + # Delete all with --all --yes + ${result} = Run Process python -m cleveragents context delete + ... --all + ... --yes + ... --context-dir ${CONTEXT_DIR} + + Should Be Equal As Integers ${result.rc} 0 + Directory Should Not Exist ${CONTEXT_DIR}/${ctx1} + Directory Should Not Exist ${CONTEXT_DIR}/${ctx2} + Directory Should Not Exist ${CONTEXT_DIR}/${ctx3} + Should Contain ${result.stdout} Deleted 3 context + +Test Delete All With Confirmation Accept + [Documentation] Test delete all with confirmation accepted + [Setup] Empty Directory ${CONTEXT_DIR} + ${ctx1} = Set Variable all_confirm1_${UNIQUE_ID} + ${ctx2} = Set Variable all_confirm2_${UNIQUE_ID} + + # Create contexts + Run Process python -m cleveragents run + ... -c ${SIMPLE_CONFIG} + ... --context ${ctx1} + ... --context-dir ${CONTEXT_DIR} + ... --unsafe + ... -p "test" + + Run Process python -m cleveragents run + ... -c ${SIMPLE_CONFIG} + ... --context ${ctx2} + ... --context-dir ${CONTEXT_DIR} + ... --unsafe + ... -p "test" + + # Delete all with confirmation + ${result} = Run Process python -m cleveragents context delete + ... --all + ... --context-dir ${CONTEXT_DIR} + ... stdin=y\n + + Should Be Equal As Integers ${result.rc} 0 + Directory Should Not Exist ${CONTEXT_DIR}/${ctx1} + Directory Should Not Exist ${CONTEXT_DIR}/${ctx2} + Should Contain ${result.stdout} Found + Should Contain ${result.stdout} to delete + +Test Delete All With Confirmation Reject + [Documentation] Test delete all with confirmation rejected + [Setup] Empty Directory ${CONTEXT_DIR} + ${ctx1} = Set Variable all_reject1_${UNIQUE_ID} + ${ctx2} = Set Variable all_reject2_${UNIQUE_ID} + + # Create contexts + Run Process python -m cleveragents run + ... -c ${SIMPLE_CONFIG} + ... --context ${ctx1} + ... --context-dir ${CONTEXT_DIR} + ... --unsafe + ... -p "test" + + Run Process python -m cleveragents run + ... -c ${SIMPLE_CONFIG} + ... --context ${ctx2} + ... --context-dir ${CONTEXT_DIR} + ... --unsafe + ... -p "test" + + # Delete all with confirmation rejected + ${result} = Run Process python -m cleveragents context delete + ... --all + ... --context-dir ${CONTEXT_DIR} + ... stdin=n\n + + Should Be Equal As Integers ${result.rc} 0 + Directory Should Exist ${CONTEXT_DIR}/${ctx1} + Directory Should Exist ${CONTEXT_DIR}/${ctx2} + Should Contain ${result.stdout} cancelled + +Test Error When Both Name And All Provided + [Documentation] Test error when both NAME and --all are provided + ${result} = Run Process python -m cleveragents context delete + ... test_context + ... --all + ... --context-dir ${CONTEXT_DIR} + + Should Not Be Equal As Integers ${result.rc} 0 + Should Contain Any ${result.stderr} ${result.stdout} Cannot specify NAME when using --all + +Test Error When Neither Name Nor All Provided + [Documentation] Test error when neither NAME nor --all is provided + ${result} = Run Process python -m cleveragents context delete + ... --context-dir ${CONTEXT_DIR} + + Should Not Be Equal As Integers ${result.rc} 0 + Should Contain Any ${result.stderr} ${result.stdout} Must specify NAME or use --all + +Test Delete All When No Contexts Exist + [Documentation] Test delete all when directory is empty + # Ensure directory is empty + Empty Directory ${CONTEXT_DIR} + + ${result} = Run Process python -m cleveragents context delete + ... --all + ... --yes + ... --context-dir ${CONTEXT_DIR} + + Should Be Equal As Integers ${result.rc} 0 + Should Contain ${result.stdout} No contexts found + +Test Delete Non Existent Context + [Documentation] Test deleting a context that doesn't exist + ${result} = Run Process python -m cleveragents context delete + ... non_existent_context_${UNIQUE_ID} + ... --yes + ... --context-dir ${CONTEXT_DIR} + + Should Not Be Equal As Integers ${result.rc} 0 + Should Contain Any ${result.stderr} ${result.stdout} does not exist + +Test Delete All Shows Context List Before Deletion + [Documentation] Test that --all shows context list before confirmation + [Setup] Empty Directory ${CONTEXT_DIR} + ${ctx1} = Set Variable list_ctx1_${UNIQUE_ID} + ${ctx2} = Set Variable list_ctx2_${UNIQUE_ID} + ${ctx3} = Set Variable list_ctx3_${UNIQUE_ID} + + # Create contexts + Run Process python -m cleveragents run + ... -c ${SIMPLE_CONFIG} + ... --context ${ctx1} + ... --context-dir ${CONTEXT_DIR} + ... --unsafe + ... -p "test" + + Run Process python -m cleveragents run + ... -c ${SIMPLE_CONFIG} + ... --context ${ctx2} + ... --context-dir ${CONTEXT_DIR} + ... --unsafe + ... -p "test" + + Run Process python -m cleveragents run + ... -c ${SIMPLE_CONFIG} + ... --context ${ctx3} + ... --context-dir ${CONTEXT_DIR} + ... --unsafe + ... -p "test" + + # Delete all with confirmation to see list + ${result} = Run Process python -m cleveragents context delete + ... --all + ... --context-dir ${CONTEXT_DIR} + ... stdin=n\n + + Should Contain ${result.stdout} Found 3 context + Should Contain ${result.stdout} ${ctx1} + Should Contain ${result.stdout} ${ctx2} + Should Contain ${result.stdout} ${ctx3} + +*** Keywords *** +Setup Test Environment + ${timestamp} = Get Current Date result_format=%Y%m%d_%H%M%S + ${random} = Evaluate random.randint(1000, 9999) modules=random + Set Suite Variable ${UNIQUE_ID} ${timestamp}_${random} + Set Suite Variable ${TEMP} ${TEMPDIR}/ca_delete_test_${UNIQUE_ID} + Set Suite Variable ${CONTEXT_DIR} ${TEMP}/contexts + Create Directory ${TEMP} + Create Directory ${CONTEXT_DIR} + +Cleanup Test Environment + Run Keyword And Ignore Error Remove Directory ${TEMP} recursive=True + +Should Contain Any + [Arguments] ${text1} ${text2} ${expected} + ${combined} = Set Variable ${text1}${text2} + Should Contain ${combined} ${expected}