Feat: Added in plan and project commands and functionality
This commit is contained in:
@@ -4,6 +4,8 @@ from __future__ import annotations
|
||||
|
||||
import re
|
||||
import tempfile
|
||||
from contextlib import contextmanager
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
@@ -12,6 +14,7 @@ from typer.testing import CliRunner
|
||||
|
||||
from cleveragents.cli.commands import context as context_app
|
||||
from cleveragents.core.exceptions import CleverAgentsError, FileSystemError
|
||||
from cleveragents.domain.models.core import Context, ContextType
|
||||
|
||||
|
||||
def strip_ansi_codes(text):
|
||||
@@ -21,6 +24,21 @@ def strip_ansi_codes(text):
|
||||
return ansi_escape.sub("", text)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def patched_container(context, project):
|
||||
"""Patch the CleverAgents container to provide mocked services."""
|
||||
with patch(
|
||||
"cleveragents.application.container.get_container"
|
||||
) as mock_get_container:
|
||||
container = MagicMock()
|
||||
container.context_service.return_value = context.mock_service
|
||||
project_service = MagicMock()
|
||||
project_service.get_current_project.return_value = project
|
||||
container.project_service.return_value = project_service
|
||||
mock_get_container.return_value = container
|
||||
yield container
|
||||
|
||||
|
||||
@given("a mock context service is configured")
|
||||
def step_mock_context_service(context):
|
||||
"""Set up a mock context service."""
|
||||
@@ -61,6 +79,24 @@ def step_mock_zero_removals(context):
|
||||
context.mock_service.remove_from_context.side_effect = [0, 0]
|
||||
|
||||
|
||||
@given("a mock context service with already tracked files")
|
||||
def step_mock_already_tracked(context):
|
||||
"""Set up mock that reports already tracked files."""
|
||||
context.mock_service = MagicMock()
|
||||
context.temp_dir = tempfile.mkdtemp()
|
||||
context.already_paths = [
|
||||
Path(context.temp_dir) / f"existing_{i}.txt" for i in range(12)
|
||||
]
|
||||
context.mock_service.add_to_context.return_value = ([], context.already_paths)
|
||||
|
||||
|
||||
@given("a mock context service with partial removals")
|
||||
def step_mock_partial_removals(context):
|
||||
"""Set up mock that removes some but not all files."""
|
||||
context.mock_service = MagicMock()
|
||||
context.mock_service.remove_from_context.side_effect = [1, 0]
|
||||
|
||||
|
||||
@given("a mock context service with files")
|
||||
def step_mock_with_files(context):
|
||||
"""Set up mock with file list."""
|
||||
@@ -72,6 +108,17 @@ def step_mock_with_files(context):
|
||||
context.mock_service.list_context.return_value = context.mock_files
|
||||
|
||||
|
||||
@given("a mock context service with numeric file sizes")
|
||||
def step_mock_numeric_sizes(context):
|
||||
"""Set up mock with numeric size values."""
|
||||
context.mock_service = MagicMock()
|
||||
context.mock_files = [
|
||||
{"path": "/src/data.bin", "size": 0, "added_at": "2024-02-01"},
|
||||
{"path": "/src/large.bin", "size": 2048, "added_at": "2024-02-02"},
|
||||
]
|
||||
context.mock_service.list_context.return_value = context.mock_files
|
||||
|
||||
|
||||
@given("a mock context service with no files")
|
||||
def step_mock_no_files(context):
|
||||
"""Set up mock with empty context."""
|
||||
@@ -104,6 +151,32 @@ def step_mock_multiple_files(context):
|
||||
context.mock_service.list_context.return_value = context.mock_files
|
||||
|
||||
|
||||
@given("a mock context service with single file content")
|
||||
def step_mock_single_file_content(context):
|
||||
"""Set up mock with a single file containing content."""
|
||||
context.mock_service = MagicMock()
|
||||
context.single_file_entry = {
|
||||
"path": "docs/report.txt",
|
||||
"content": "Generated summary",
|
||||
"size": 512,
|
||||
"added_at": "2024-03-01",
|
||||
}
|
||||
context.mock_service.list_context.return_value = [context.single_file_entry]
|
||||
|
||||
|
||||
@given("a mock context service with single file lacking content")
|
||||
def step_mock_single_file_blank(context):
|
||||
"""Set up mock with a single file but no stored content."""
|
||||
context.mock_service = MagicMock()
|
||||
context.single_file_entry = {
|
||||
"path": "docs/empty.txt",
|
||||
"content": None,
|
||||
"size": 128,
|
||||
"added_at": "2024-03-02",
|
||||
}
|
||||
context.mock_service.list_context.return_value = [context.single_file_entry]
|
||||
|
||||
|
||||
@given("a mock context service with clearable files")
|
||||
def step_mock_clearable(context):
|
||||
"""Set up mock for clear operations."""
|
||||
@@ -122,7 +195,8 @@ def step_exec_context_add_valid(context):
|
||||
test_file.write_text("content")
|
||||
|
||||
with patch("cleveragents.application.container.get_container") as mock_container:
|
||||
context.mock_service.add_to_context.return_value = [test_file]
|
||||
# add_to_context should return a tuple: (added_files, already_in_context)
|
||||
context.mock_service.add_to_context.return_value = ([test_file], [])
|
||||
mock_container.return_value.context_service.return_value = context.mock_service
|
||||
|
||||
result = runner.invoke(context_app.app, ["add", str(test_file)])
|
||||
@@ -141,6 +215,41 @@ def step_exec_context_add_nonexistent(context):
|
||||
context.result = result
|
||||
|
||||
|
||||
@when("the context add command is executed without an active project")
|
||||
def step_exec_context_add_no_project(context):
|
||||
"""Execute context add when no project is configured."""
|
||||
runner = CliRunner()
|
||||
|
||||
with patch("cleveragents.application.container.get_container") as mock_container:
|
||||
container = mock_container.return_value
|
||||
container.context_service.return_value = context.mock_service
|
||||
project_service = MagicMock()
|
||||
project_service.get_current_project.return_value = None
|
||||
container.project_service.return_value = project_service
|
||||
|
||||
result = runner.invoke(context_app.app, ["add", "placeholder.txt"])
|
||||
context.result = result
|
||||
|
||||
|
||||
@when("the context add command processes already tracked paths")
|
||||
def step_exec_context_add_already(context):
|
||||
"""Execute context add when files are already tracked."""
|
||||
runner = CliRunner()
|
||||
|
||||
test_file = Path(context.temp_dir) / "tracked.txt"
|
||||
test_file.write_text("content")
|
||||
|
||||
with patch("cleveragents.application.container.get_container") as mock_container:
|
||||
container = mock_container.return_value
|
||||
container.context_service.return_value = context.mock_service
|
||||
project_service = MagicMock()
|
||||
project_service.get_current_project.return_value = object()
|
||||
container.project_service.return_value = project_service
|
||||
|
||||
result = runner.invoke(context_app.app, ["add", str(test_file)])
|
||||
context.result = result
|
||||
|
||||
|
||||
@when("the context add command is executed and fails")
|
||||
def step_exec_context_add_fails(context):
|
||||
"""Execute context add that fails."""
|
||||
@@ -166,7 +275,8 @@ def step_exec_context_load(context):
|
||||
test_file.write_text("content")
|
||||
|
||||
with patch("cleveragents.application.container.get_container") as mock_container:
|
||||
context.mock_service.add_to_context.return_value = [test_file]
|
||||
# add_to_context should return a tuple: (added_files, already_in_context)
|
||||
context.mock_service.add_to_context.return_value = ([test_file], [])
|
||||
mock_container.return_value.context_service.return_value = context.mock_service
|
||||
|
||||
result = runner.invoke(context_app.app, ["load", str(test_file)])
|
||||
@@ -179,19 +289,109 @@ def step_exec_context_remove(context):
|
||||
runner = CliRunner()
|
||||
|
||||
with patch("cleveragents.application.container.get_container") as mock_container:
|
||||
mock_container.return_value.context_service.return_value = context.mock_service
|
||||
container = mock_container.return_value
|
||||
container.context_service.return_value = context.mock_service
|
||||
project_service = MagicMock()
|
||||
project_service.get_current_project.return_value = object()
|
||||
container.project_service.return_value = project_service
|
||||
|
||||
result = runner.invoke(context_app.app, ["rm", "/file1.txt", "/file2.txt"])
|
||||
context.result = result
|
||||
|
||||
|
||||
@when("the context remove command is executed without an active project")
|
||||
def step_exec_context_remove_no_project(context):
|
||||
"""Execute context remove when no project is configured."""
|
||||
runner = CliRunner()
|
||||
|
||||
with patch("cleveragents.application.container.get_container") as mock_container:
|
||||
container = mock_container.return_value
|
||||
container.context_service.return_value = context.mock_service
|
||||
project_service = MagicMock()
|
||||
project_service.get_current_project.return_value = None
|
||||
container.project_service.return_value = project_service
|
||||
|
||||
result = runner.invoke(context_app.app, ["rm", "/file1.txt"])
|
||||
context.result = result
|
||||
|
||||
|
||||
@when("the context remove command is executed for relative paths")
|
||||
def step_exec_context_remove_relative(context):
|
||||
"""Execute context remove for relative file names."""
|
||||
runner = CliRunner()
|
||||
|
||||
with patch("cleveragents.application.container.get_container") as mock_container:
|
||||
container = mock_container.return_value
|
||||
container.context_service.return_value = context.mock_service
|
||||
project_service = MagicMock()
|
||||
project_service.get_current_project.return_value = object()
|
||||
container.project_service.return_value = project_service
|
||||
|
||||
result = runner.invoke(context_app.app, ["rm", "file1.txt", "file2.txt"])
|
||||
context.result = result
|
||||
|
||||
|
||||
@when("the context remove command is executed with service failure")
|
||||
def step_exec_context_remove_failure(context):
|
||||
"""Execute context remove that raises CleverAgentsError."""
|
||||
runner = CliRunner()
|
||||
|
||||
with patch("cleveragents.application.container.get_container") as mock_container:
|
||||
container = mock_container.return_value
|
||||
container.context_service.return_value = context.mock_service
|
||||
context.mock_service.remove_from_context.side_effect = context.mock_error
|
||||
project_service = MagicMock()
|
||||
project_service.get_current_project.return_value = object()
|
||||
container.project_service.return_value = project_service
|
||||
|
||||
result = runner.invoke(context_app.app, ["rm", "/file1.txt"])
|
||||
context.result = result
|
||||
|
||||
|
||||
@when("the context list command is executed")
|
||||
def step_exec_context_list(context):
|
||||
"""Execute context list."""
|
||||
runner = CliRunner()
|
||||
|
||||
with patch("cleveragents.application.container.get_container") as mock_container:
|
||||
mock_container.return_value.context_service.return_value = context.mock_service
|
||||
container = mock_container.return_value
|
||||
container.context_service.return_value = context.mock_service
|
||||
project_service = MagicMock()
|
||||
project_service.get_current_project.return_value = object()
|
||||
container.project_service.return_value = project_service
|
||||
|
||||
result = runner.invoke(context_app.app, ["list"])
|
||||
context.result = result
|
||||
|
||||
|
||||
@when("the context list command is executed without an active project")
|
||||
def step_exec_context_list_no_project(context):
|
||||
"""Execute context list when no project is configured."""
|
||||
runner = CliRunner()
|
||||
|
||||
with patch("cleveragents.application.container.get_container") as mock_container:
|
||||
container = mock_container.return_value
|
||||
container.context_service.return_value = context.mock_service
|
||||
project_service = MagicMock()
|
||||
project_service.get_current_project.return_value = None
|
||||
container.project_service.return_value = project_service
|
||||
|
||||
result = runner.invoke(context_app.app, ["list"])
|
||||
context.result = result
|
||||
|
||||
|
||||
@when("the context list command is executed with service failure")
|
||||
def step_exec_context_list_failure(context):
|
||||
"""Execute context list when the service raises an error."""
|
||||
runner = CliRunner()
|
||||
|
||||
with patch("cleveragents.application.container.get_container") as mock_container:
|
||||
container = mock_container.return_value
|
||||
container.context_service.return_value = context.mock_service
|
||||
context.mock_service.list_context.side_effect = context.mock_error
|
||||
project_service = MagicMock()
|
||||
project_service.get_current_project.return_value = object()
|
||||
container.project_service.return_value = project_service
|
||||
|
||||
result = runner.invoke(context_app.app, ["list"])
|
||||
context.result = result
|
||||
@@ -221,13 +421,50 @@ def step_exec_context_show_without_path(context):
|
||||
context.result = result
|
||||
|
||||
|
||||
@when("the context show command is executed without an active project")
|
||||
def step_exec_context_show_no_project(context):
|
||||
"""Execute context show when no project is configured."""
|
||||
runner = CliRunner()
|
||||
|
||||
with patch("cleveragents.application.container.get_container") as mock_container:
|
||||
container = mock_container.return_value
|
||||
container.context_service.return_value = context.mock_service
|
||||
project_service = MagicMock()
|
||||
project_service.get_current_project.return_value = None
|
||||
container.project_service.return_value = project_service
|
||||
|
||||
result = runner.invoke(context_app.app, ["show"])
|
||||
context.result = result
|
||||
|
||||
|
||||
@when("the context show command is executed with service failure")
|
||||
def step_exec_context_show_failure(context):
|
||||
"""Execute context show when the service raises an error."""
|
||||
runner = CliRunner()
|
||||
|
||||
with patch("cleveragents.application.container.get_container") as mock_container:
|
||||
container = mock_container.return_value
|
||||
container.context_service.return_value = context.mock_service
|
||||
context.mock_service.get_context_content.side_effect = context.mock_error
|
||||
project_service = MagicMock()
|
||||
project_service.get_current_project.return_value = object()
|
||||
container.project_service.return_value = project_service
|
||||
|
||||
result = runner.invoke(context_app.app, ["show", "/test.txt"])
|
||||
context.result = result
|
||||
|
||||
|
||||
@when("the context clear command is executed with yes flag")
|
||||
def step_exec_context_clear_yes(context):
|
||||
"""Execute context clear with --yes."""
|
||||
runner = CliRunner()
|
||||
|
||||
with patch("cleveragents.application.container.get_container") as mock_container:
|
||||
mock_container.return_value.context_service.return_value = context.mock_service
|
||||
container = mock_container.return_value
|
||||
container.context_service.return_value = context.mock_service
|
||||
project_service = MagicMock()
|
||||
project_service.get_current_project.return_value = object()
|
||||
container.project_service.return_value = project_service
|
||||
|
||||
result = runner.invoke(context_app.app, ["clear", "--yes"])
|
||||
context.result = result
|
||||
@@ -239,7 +476,11 @@ def step_exec_context_clear_cancel(context):
|
||||
runner = CliRunner()
|
||||
|
||||
with patch("cleveragents.application.container.get_container") as mock_container:
|
||||
mock_container.return_value.context_service.return_value = context.mock_service
|
||||
container = mock_container.return_value
|
||||
container.context_service.return_value = context.mock_service
|
||||
project_service = MagicMock()
|
||||
project_service.get_current_project.return_value = object()
|
||||
container.project_service.return_value = project_service
|
||||
|
||||
result = runner.invoke(context_app.app, ["clear"], input="n\n")
|
||||
context.result = result
|
||||
@@ -251,12 +492,50 @@ def step_exec_context_clear(context):
|
||||
runner = CliRunner()
|
||||
|
||||
with patch("cleveragents.application.container.get_container") as mock_container:
|
||||
mock_container.return_value.context_service.return_value = context.mock_service
|
||||
container = mock_container.return_value
|
||||
container.context_service.return_value = context.mock_service
|
||||
project_service = MagicMock()
|
||||
project_service.get_current_project.return_value = object()
|
||||
container.project_service.return_value = project_service
|
||||
|
||||
result = runner.invoke(context_app.app, ["clear"])
|
||||
context.result = result
|
||||
|
||||
|
||||
@when("the context clear command is executed without an active project")
|
||||
def step_exec_context_clear_no_project(context):
|
||||
"""Execute context clear when no project is configured."""
|
||||
runner = CliRunner()
|
||||
|
||||
with patch("cleveragents.application.container.get_container") as mock_container:
|
||||
container = mock_container.return_value
|
||||
container.context_service.return_value = context.mock_service
|
||||
project_service = MagicMock()
|
||||
project_service.get_current_project.return_value = None
|
||||
container.project_service.return_value = project_service
|
||||
|
||||
result = runner.invoke(context_app.app, ["clear"])
|
||||
context.result = result
|
||||
|
||||
|
||||
@when("the context clear command is executed with service failure")
|
||||
def step_exec_context_clear_failure(context):
|
||||
"""Execute context clear when the service raises an error."""
|
||||
runner = CliRunner()
|
||||
|
||||
with patch("cleveragents.application.container.get_container") as mock_container:
|
||||
container = mock_container.return_value
|
||||
container.context_service.return_value = context.mock_service
|
||||
context.mock_service.list_context.return_value = [{"path": "pending.txt"}]
|
||||
context.mock_service.clear_context.side_effect = context.mock_error
|
||||
project_service = MagicMock()
|
||||
project_service.get_current_project.return_value = object()
|
||||
container.project_service.return_value = project_service
|
||||
|
||||
result = runner.invoke(context_app.app, ["clear", "--yes"])
|
||||
context.result = result
|
||||
|
||||
|
||||
@when("context command is invoked without subcommand")
|
||||
def step_exec_context_no_subcommand(context):
|
||||
"""Execute context without subcommand."""
|
||||
@@ -294,6 +573,24 @@ def step_check_add_success(context):
|
||||
)
|
||||
|
||||
|
||||
@then("already tracked file details should be reported")
|
||||
def step_check_already_tracked(context):
|
||||
"""Verify output when files are already tracked."""
|
||||
assert context.result.exit_code == 0, (
|
||||
f"Command failed with exit code {context.result.exit_code}\n"
|
||||
f"Output: {context.result.output}"
|
||||
)
|
||||
clean_output = strip_ansi_codes(context.result.output)
|
||||
assert "File(s) already in context" in clean_output, (
|
||||
f"Expected warning about already tracked files.\n"
|
||||
f"Output: {context.result.output}"
|
||||
)
|
||||
assert "... and 2 more files" in clean_output, (
|
||||
f"Expected summary of remaining already tracked files.\n"
|
||||
f"Output: {context.result.output}"
|
||||
)
|
||||
|
||||
|
||||
@then("path does not exist error should be shown")
|
||||
def step_check_path_error(context):
|
||||
"""Check path error."""
|
||||
@@ -308,6 +605,19 @@ def step_check_path_error(context):
|
||||
)
|
||||
|
||||
|
||||
@then("a no active project error should be shown")
|
||||
def step_check_no_project_error(context):
|
||||
"""Ensure commands abort when no project is active."""
|
||||
clean_output = strip_ansi_codes(context.result.output)
|
||||
assert context.result.exit_code != 0, (
|
||||
f"Expected non-zero exit code when project is missing.\n"
|
||||
f"Output: {context.result.output}"
|
||||
)
|
||||
assert "No active project" in clean_output, (
|
||||
f"Expected 'No active project' message.\nOutput: {context.result.output}"
|
||||
)
|
||||
|
||||
|
||||
@then("FileSystemError should be displayed and command aborted")
|
||||
def step_check_filesystem_error(context):
|
||||
"""Check filesystem error."""
|
||||
@@ -407,6 +717,20 @@ def step_check_no_remove(context):
|
||||
)
|
||||
|
||||
|
||||
@then("missing context files should trigger an abort")
|
||||
def step_check_missing_context_abort(context):
|
||||
"""Ensure missing files cause the command to abort."""
|
||||
clean_output = strip_ansi_codes(context.result.output)
|
||||
assert context.result.exit_code != 0, (
|
||||
f"Expected abort when files are missing from context.\n"
|
||||
f"Output: {context.result.output}"
|
||||
)
|
||||
assert "File(s) not in context" in clean_output, (
|
||||
f"Expected message about missing context files.\n"
|
||||
f"Output: {context.result.output}"
|
||||
)
|
||||
|
||||
|
||||
@then("a formatted table of files should be displayed")
|
||||
def step_check_table_display(context):
|
||||
"""Check table display."""
|
||||
@@ -427,6 +751,19 @@ def step_check_table_display(context):
|
||||
)
|
||||
|
||||
|
||||
@then("numeric sizes should be rendered as bytes")
|
||||
def step_check_numeric_sizes(context):
|
||||
"""Ensure numeric sizes are humanized as byte strings."""
|
||||
assert context.result.exit_code == 0, (
|
||||
f"Command failed with exit code {context.result.exit_code}\n"
|
||||
f"Output: {context.result.output}"
|
||||
)
|
||||
clean_output = strip_ansi_codes(context.result.output)
|
||||
assert "bytes" in clean_output, (
|
||||
f"Expected sizes to be formatted in bytes.\nOutput: {context.result.output}"
|
||||
)
|
||||
|
||||
|
||||
@then("empty context message should be shown with help")
|
||||
def step_check_empty_message(context):
|
||||
"""Check empty message."""
|
||||
@@ -508,6 +845,36 @@ def step_check_summary(context):
|
||||
)
|
||||
|
||||
|
||||
@then("the show command should report an empty context")
|
||||
def step_check_show_empty_context(context):
|
||||
"""Verify show command informs when no files exist."""
|
||||
clean_output = strip_ansi_codes(context.result.output)
|
||||
assert context.result.exit_code == 0, (
|
||||
f"Expected successful exit for empty context message.\n"
|
||||
f"Output: {context.result.output}"
|
||||
)
|
||||
assert "No files in context" in clean_output, (
|
||||
f"Expected 'No files in context' message.\nOutput: {context.result.output}"
|
||||
)
|
||||
|
||||
|
||||
@then("the show command should report missing content")
|
||||
def step_check_show_missing_content(context):
|
||||
"""Ensure show command highlights missing file content."""
|
||||
clean_output = strip_ansi_codes(context.result.output)
|
||||
assert context.result.exit_code == 0, (
|
||||
f"Expected successful exit when content is missing.\n"
|
||||
f"Output: {context.result.output}"
|
||||
)
|
||||
assert "No content available for" in clean_output, (
|
||||
f"Expected message indicating missing content.\nOutput: {context.result.output}"
|
||||
)
|
||||
|
||||
assert "No content available" in clean_output, (
|
||||
f"Expected message indicating missing content.\nOutput: {context.result.output}"
|
||||
)
|
||||
|
||||
|
||||
@then("context should be cleared with success message")
|
||||
def step_check_clear_success(context):
|
||||
"""Check clear success."""
|
||||
@@ -577,3 +944,264 @@ def step_check_default_list(context):
|
||||
f"Raw output: '{context.result.output}'\n"
|
||||
f"Clean output (no color codes): '{clean_output}'"
|
||||
)
|
||||
|
||||
|
||||
@given("a dictionary-style context entry with missing metadata")
|
||||
def step_given_dict_entry(context):
|
||||
"""Prepare dictionary entry missing optional fields."""
|
||||
context.dict_entry = {
|
||||
"path": "relative/file.txt",
|
||||
"type": "NOTE",
|
||||
"size": "5 KB",
|
||||
}
|
||||
|
||||
|
||||
@given("a domain Context model entry with metadata")
|
||||
def step_given_context_model_entry(context):
|
||||
"""Prepare a Context model instance for normalization."""
|
||||
context.dict_entry = Context(
|
||||
id=7,
|
||||
plan_id=42,
|
||||
type=ContextType.NOTE,
|
||||
path="/project/docs/readme.md",
|
||||
content="Example context body",
|
||||
file_hash="abc123",
|
||||
size=2048,
|
||||
added_at=datetime(2024, 1, 5, 15, 30, 45),
|
||||
)
|
||||
|
||||
|
||||
@when("the normalization helper processes the context entry")
|
||||
def step_when_normalize_entry(context):
|
||||
"""Normalize dictionary-based context entry."""
|
||||
context.normalized_entry = context_app._normalize_context_entry(context.dict_entry)
|
||||
|
||||
|
||||
@then("the normalized context tuple should include placeholder defaults")
|
||||
def step_then_normalized_defaults(context):
|
||||
"""Verify normalization uses defaults for missing fields."""
|
||||
path_text, type_label, size, added_at, content_text = context.normalized_entry
|
||||
assert path_text == "relative/file.txt"
|
||||
assert type_label == "NOTE"
|
||||
assert size == "5 KB"
|
||||
assert added_at == "Unknown"
|
||||
assert content_text == ""
|
||||
|
||||
|
||||
@then("the normalized context tuple should match the model attributes")
|
||||
def step_then_normalized_model(context):
|
||||
"""Verify normalization preserves Context model details."""
|
||||
path_text, type_label, size, added_at, content_text = context.normalized_entry
|
||||
assert path_text == "/project/docs/readme.md"
|
||||
assert type_label == "note"
|
||||
assert size == 2048
|
||||
assert added_at == "2024-01-05 15:30:45"
|
||||
assert content_text == "Example context body"
|
||||
|
||||
|
||||
@when("the programmatic add command runs with a missing file path")
|
||||
def step_when_programmatic_add_missing(context):
|
||||
"""Run add_command with a path that does not exist."""
|
||||
missing_path = Path(context.temp_dir) / "missing.txt"
|
||||
context.programmatic_exception = None
|
||||
context.mock_service.add_to_context.reset_mock()
|
||||
with patched_container(context, project=object()):
|
||||
try:
|
||||
context_app.add_command([str(missing_path)])
|
||||
except Exception as exc: # pragma: no cover - captured for assertions
|
||||
context.programmatic_exception = exc
|
||||
context.programmatic_result = None
|
||||
|
||||
|
||||
@then("no files are sent to the context service")
|
||||
def step_then_no_files_sent(context):
|
||||
"""Ensure add_to_context is not called for missing files."""
|
||||
assert context.programmatic_exception is None
|
||||
context.mock_service.add_to_context.assert_not_called()
|
||||
|
||||
|
||||
@when("the programmatic add command runs without an active project")
|
||||
def step_when_programmatic_add_no_project(context):
|
||||
"""Run add_command without an active project."""
|
||||
test_file = Path(context.temp_dir) / "active.txt"
|
||||
test_file.write_text("data")
|
||||
context.programmatic_exception = None
|
||||
with patched_container(context, project=None):
|
||||
try:
|
||||
context_app.add_command([str(test_file)])
|
||||
except Exception as exc:
|
||||
context.programmatic_exception = exc
|
||||
|
||||
|
||||
@then("a CleverAgentsError is raised for programmatic add")
|
||||
def step_then_add_requires_project(context):
|
||||
"""Ensure add_command raises when project is missing."""
|
||||
assert isinstance(context.programmatic_exception, CleverAgentsError)
|
||||
|
||||
|
||||
@when("the programmatic list command is executed")
|
||||
def step_when_programmatic_list(context):
|
||||
"""Execute list_command with active project."""
|
||||
context.programmatic_exception = None
|
||||
with patched_container(context, project=object()):
|
||||
context.programmatic_result = context_app.list_command()
|
||||
|
||||
|
||||
@then("the programmatic list command should return those files")
|
||||
def step_then_list_returns_files(context):
|
||||
"""Verify list_command returns mock files."""
|
||||
assert context.programmatic_result == context.mock_files
|
||||
|
||||
|
||||
@when("the programmatic list command runs without an active project")
|
||||
def step_when_programmatic_list_no_project(context):
|
||||
"""Execute list_command without active project."""
|
||||
context.programmatic_exception = None
|
||||
with patched_container(context, project=None):
|
||||
try:
|
||||
context_app.list_command()
|
||||
except Exception as exc:
|
||||
context.programmatic_exception = exc
|
||||
|
||||
|
||||
@then("a CleverAgentsError is raised for programmatic list")
|
||||
def step_then_list_requires_project(context):
|
||||
"""Ensure list_command raises when project is missing."""
|
||||
assert isinstance(context.programmatic_exception, CleverAgentsError)
|
||||
|
||||
|
||||
@when("the programmatic show command runs without an active project")
|
||||
def step_when_programmatic_show_no_project(context):
|
||||
"""Execute show_command without an active project configured."""
|
||||
context.programmatic_exception = None
|
||||
context.programmatic_result = None
|
||||
with patched_container(context, project=None):
|
||||
try:
|
||||
context_app.show_command()
|
||||
except Exception as exc:
|
||||
context.programmatic_exception = exc
|
||||
|
||||
|
||||
@when("the programmatic show command runs without a path")
|
||||
def step_when_programmatic_show_summary(context):
|
||||
"""Execute show_command when listing context entries."""
|
||||
context.programmatic_exception = None
|
||||
with patched_container(context, project=object()):
|
||||
context.programmatic_result = context_app.show_command()
|
||||
|
||||
|
||||
@then("the programmatic show command should return a summary string")
|
||||
def step_then_show_returns_summary(context):
|
||||
"""Verify show_command returns summary when no path provided."""
|
||||
expected = f"Total files: {len(context.mock_files)}"
|
||||
assert context.programmatic_result == expected
|
||||
|
||||
|
||||
@then("a CleverAgentsError is raised for programmatic show")
|
||||
def step_then_show_requires_project(context):
|
||||
"""Ensure show_command raises when no project is active."""
|
||||
assert isinstance(context.programmatic_exception, CleverAgentsError)
|
||||
|
||||
|
||||
@then("the programmatic show command should return no content summary")
|
||||
def step_then_show_returns_none(context):
|
||||
"""Ensure show_command returns None when context has no files."""
|
||||
assert context.programmatic_exception is None
|
||||
assert context.programmatic_result is None
|
||||
|
||||
|
||||
@when("the programmatic show command runs for a specific path")
|
||||
def step_when_programmatic_show_specific(context):
|
||||
"""Execute show_command for a single file path."""
|
||||
context.programmatic_exception = None
|
||||
with patched_container(context, project=object()):
|
||||
context.programmatic_result = context_app.show_command("/test.txt")
|
||||
|
||||
|
||||
@then("the programmatic show command should return that content")
|
||||
def step_then_show_returns_content(context):
|
||||
"""Ensure show_command returns content for requested file."""
|
||||
assert context.programmatic_result == context.mock_content
|
||||
|
||||
|
||||
@when("the programmatic remove command runs without an active project")
|
||||
def step_when_programmatic_remove_no_project(context):
|
||||
"""Execute rm_command without an active project configured."""
|
||||
context.programmatic_exception = None
|
||||
with patched_container(context, project=None):
|
||||
try:
|
||||
context_app.rm_command(["missing.txt"])
|
||||
except Exception as exc:
|
||||
context.programmatic_exception = exc
|
||||
|
||||
|
||||
@when("the programmatic remove command runs for files")
|
||||
def step_when_programmatic_remove(context):
|
||||
"""Execute rm_command where files are missing."""
|
||||
context.programmatic_exception = None
|
||||
with patched_container(context, project=object()):
|
||||
try:
|
||||
context_app.rm_command(["missing.txt", "other.txt"])
|
||||
except Exception as exc:
|
||||
context.programmatic_exception = exc
|
||||
|
||||
|
||||
@when("the programmatic remove command runs with removable files")
|
||||
def step_when_programmatic_remove_success(context):
|
||||
"""Execute rm_command when files are present in context."""
|
||||
context.programmatic_exception = None
|
||||
context.mock_service.remove_from_context.reset_mock()
|
||||
with patched_container(context, project=object()):
|
||||
try:
|
||||
context_app.rm_command(["/file1.txt", "/file2.txt"])
|
||||
except Exception as exc:
|
||||
context.programmatic_exception = exc
|
||||
|
||||
|
||||
@then("a CleverAgentsError is raised for programmatic remove")
|
||||
def step_then_remove_requires_presence(context):
|
||||
"""Ensure rm_command raises when files are not in context."""
|
||||
assert isinstance(context.programmatic_exception, CleverAgentsError)
|
||||
|
||||
|
||||
@then("the programmatic remove command should complete successfully")
|
||||
def step_then_remove_success(context):
|
||||
"""Ensure rm_command succeeds when files are removed."""
|
||||
assert context.programmatic_exception is None
|
||||
assert context.mock_service.remove_from_context.call_count == 2
|
||||
|
||||
|
||||
@when("the programmatic clear command runs without an active project")
|
||||
def step_when_programmatic_clear_no_project(context):
|
||||
"""Execute clear_command without an active project."""
|
||||
context.programmatic_exception = None
|
||||
with patched_container(context, project=None):
|
||||
try:
|
||||
context_app.clear_command()
|
||||
except Exception as exc:
|
||||
context.programmatic_exception = exc
|
||||
|
||||
|
||||
@then("a CleverAgentsError is raised for programmatic clear")
|
||||
def step_then_clear_requires_project(context):
|
||||
"""Ensure clear_command raises when project is missing."""
|
||||
assert isinstance(context.programmatic_exception, CleverAgentsError)
|
||||
|
||||
|
||||
@when("the programmatic clear command runs with an active project")
|
||||
def step_when_programmatic_clear_active(context):
|
||||
"""Execute clear_command with active project."""
|
||||
context.programmatic_exception = None
|
||||
context.mock_service.clear_context.reset_mock()
|
||||
with patched_container(context, project=object()):
|
||||
try:
|
||||
context_app.clear_command()
|
||||
except Exception as exc:
|
||||
context.programmatic_exception = exc
|
||||
|
||||
|
||||
@then("the context service clear function should be called")
|
||||
def step_then_clear_calls_service(context):
|
||||
"""Verify clear_command calls the context service clear method."""
|
||||
assert context.programmatic_exception is None
|
||||
context.mock_service.clear_context.assert_called_once()
|
||||
|
||||
Reference in New Issue
Block a user