test(acms): cover context add skip branches

This commit is contained in:
CleverAgents Bot
2026-06-18 06:54:51 -04:00
committed by Forgejo
parent cfadb68f5a
commit f1a417cab0
2 changed files with 100 additions and 0 deletions
+18
View File
@@ -54,6 +54,18 @@ Feature: ACMS context add command with --tag and --policy flags
When I traverse the directory recursively with ChunkedFileTraverser
Then the acms traversal should yield only the visible Python file
Scenario: ChunkedFileTraverser skips files larger than max_file_size
Given a temporary directory with a single Python file "large.py"
When I traverse the file with max_file_size 1
Then the acms traversal should yield 0 entries
Scenario: ChunkedFileTraverser skips unreadable file metadata and content
Given a temporary directory with a single Python file "broken.py"
When I read the file with a mocked stat OSError
Then the acms read result should be skipped
When I read the file with a mocked read_text OSError
Then the acms read result should be skipped
Scenario: ChunkedFileTraverser raises FileNotFoundError for missing path
When I traverse a non-existent path with ChunkedFileTraverser
Then an acms FileNotFoundError should be raised
@@ -106,6 +118,12 @@ Feature: ACMS context add command with --tag and --policy flags
When I invoke context add with directory "src/"
Then the acms context add command should succeed
Scenario: add_command persists paths when traversal raises
Given a mocked context service for acms add
And a temporary file "src/main.py" exists
When I call add_command with a mocked traverser ValueError
Then the acms programmatic context add should persist the file
Scenario: context add --help shows usage examples
When I invoke context add --help
Then the acms help output should contain "--tag"
+82
View File
@@ -19,6 +19,7 @@ from behave.runner import Context
from typer.testing import CliRunner
from cleveragents.acms.index import AcmsIndexEntry, ChunkedFileTraverser
from cleveragents.cli.commands.context import add_command
from cleveragents.cli.commands.context import app as context_app
# ---------------------------------------------------------------------------
@@ -129,6 +130,15 @@ def step_acms_traverse_dir_nonrecursive(context: Context) -> None:
)
@when("I traverse the file with max_file_size {max_file_size:d}")
def step_acms_traverse_file_with_max_size(
context: Context, max_file_size: int
) -> None:
"""Traverse a single file with a low max_file_size limit."""
traverser = ChunkedFileTraverser(max_file_size=max_file_size)
context.acms_entries = list(traverser.traverse(context.acms_target_path))
@when('I traverse with tags "{tag1}" and "{tag2}"')
def step_acms_traverse_with_tags(context: Context, tag1: str, tag2: str) -> None:
"""Traverse with multiple tags."""
@@ -174,6 +184,38 @@ def step_acms_traverse_nonexistent(context: Context) -> None:
context.acms_error = exc
@when("I read the file with a mocked stat OSError")
def step_acms_read_file_stat_oserror(context: Context) -> None:
"""Call _read_file while stat raises OSError."""
target = context.acms_target_path
original_stat = Path.stat
def failing_stat(self: Path, *args: object, **kwargs: object) -> object:
if self == target:
raise OSError("stat failed")
return original_stat(self, *args, **kwargs)
traverser = ChunkedFileTraverser()
with patch.object(Path, "stat", failing_stat):
context.acms_read_result = traverser._read_file(target)
@when("I read the file with a mocked read_text OSError")
def step_acms_read_file_read_text_oserror(context: Context) -> None:
"""Call _read_file while read_text raises OSError after stat succeeds."""
target = context.acms_target_path
original_read_text = Path.read_text
def failing_read_text(self: Path, *args: object, **kwargs: object) -> str:
if self == target:
raise OSError("read failed")
return original_read_text(self, *args, **kwargs)
traverser = ChunkedFileTraverser()
with patch.object(Path, "read_text", failing_read_text):
context.acms_read_result = traverser._read_file(target)
@when("I create a ChunkedFileTraverser with chunk_size 0")
def step_acms_invalid_chunk_size(context: Context) -> None:
"""Create a ChunkedFileTraverser with invalid chunk_size."""
@@ -317,6 +359,13 @@ def step_acms_only_visible_py_file(context: Context) -> None:
)
@then("the acms read result should be skipped")
def step_acms_read_result_skipped(context: Context) -> None:
assert context.acms_read_result is None, (
f"Expected skipped read result, got {context.acms_read_result!r}"
)
@then("an acms FileNotFoundError should be raised")
def step_acms_file_not_found(context: Context) -> None:
assert isinstance(context.acms_error, FileNotFoundError), (
@@ -474,6 +523,30 @@ def step_acms_invoke_add_directory(context: Context) -> None:
context.acms_result = result
@when("I call add_command with a mocked traverser ValueError")
def step_acms_call_add_command_traverser_value_error(context: Context) -> None:
"""Call the programmatic wrapper while traversal raises ValueError."""
filepath = context.acms_temp_file
container = _mock_acms_container(added_files=[filepath])
class FailingTraverser:
def __init__(self, *args: object, **kwargs: object) -> None:
pass
def traverse(self, *args: object, **kwargs: object) -> list[object]:
raise ValueError("not a regular file")
with (
patch("cleveragents.application.container.get_container", return_value=container),
patch("cleveragents.acms.index.ChunkedFileTraverser", FailingTraverser),
):
add_command([str(filepath)], recursive=True, tags=["api"], policy="strict")
context.acms_context_service = container.context_service.return_value
context.acms_project = container.project_service.return_value.get_current_project()
context.acms_added_path = filepath.resolve()
@when("I invoke context add --help")
def step_acms_invoke_help(context: Context) -> None:
"""Invoke context add --help."""
@@ -488,6 +561,15 @@ def step_acms_add_success(context: Context) -> None:
)
@then("the acms programmatic context add should persist the file")
def step_acms_programmatic_add_persisted(context: Context) -> None:
context.acms_context_service.add_to_context.assert_called_once_with(
context.acms_project,
context.acms_added_path,
recursive=True,
)
@then('the acms output should mention "{text}"')
def step_acms_output_mentions(context: Context, text: str) -> None:
assert text in context.acms_result.output, (