test(acms): cover context add skip branches
This commit is contained in:
committed by
Forgejo
parent
cfadb68f5a
commit
f1a417cab0
@@ -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"
|
||||
|
||||
@@ -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, (
|
||||
|
||||
Reference in New Issue
Block a user