Files
freemo 2980b14e7b test: boost combined branch coverage to 97% with additional behave scenarios
Add ~60 new behave scenarios across output rendering, tool router, file ops,
and skill search features targeting uncovered lines and branches. Key additions:
- ElementHandle validation guards (empty id, type, negative index, None args)
- Handle close/context-manager edge cases
- Table list-row and batch-row coverage
- Color/box-draw/JSON/YAML materializer edge cases
- Format selection paths (detect capabilities, explicit flag, empty format)
- Session ColumnDef, double-close guard, force-close open handles
- Tool router schema export and provider format scenarios
- File ops edge cases and search stat-failure/glob-include tests

All nox checks pass: lint, typecheck (0 errors), unit_tests (4730 scenarios),
coverage_report (97.0% >= 97% threshold).
2026-02-21 10:23:33 -05:00

250 lines
11 KiB
Gherkin

Feature: Skill Search and Directory Tools
As a developer
I want skill-level directory listing and search tools
So that agents can list directories, match files by glob, and
search file contents with regex
# ---- ListDir ----
Scenario: ListDir lists directory contents
Given a skill search sandbox directory
And a search sandbox file "a.txt" with content "a"
And a search sandbox file "b.py" with content "b"
When I list-dir "." in the search sandbox
Then the search tool result should be successful
And the search output "total" should be greater than or equal to 2
And the search output "truncated" should be boolean false
Scenario: ListDir with type filter for files only
Given a skill search sandbox directory
And a search sandbox file "file.txt" with content "content"
And a search sandbox subdirectory "subdir"
When I list-dir-typed "." with type_filter "files"
Then the search tool result should be successful
And all search entries should not be directories
Scenario: ListDir with type filter for dirs only
Given a skill search sandbox directory
And a search sandbox file "file.txt" with content "content"
And a search sandbox subdirectory "subdir"
When I list-dir-typed "." with type_filter "dirs"
Then the search tool result should be successful
And all search entries should be directories
Scenario: ListDir with ignore patterns
Given a skill search sandbox directory
And a search sandbox file "keep.txt" with content "keep"
And a search sandbox file "ignore.log" with content "ignore"
When I list-dir-ignoring "." ignoring "*.log"
Then the search tool result should be successful
And no search entry should have name "ignore.log"
Scenario: ListDir with size limit
Given a skill search sandbox directory
And a search sandbox file "1.txt" with content "one"
And a search sandbox file "2.txt" with content "two"
And a search sandbox file "3.txt" with content "three"
When I list-dir-limited "." with limit 2
Then the search tool result should be successful
And the search output "truncated" should be boolean true
And the search output "total" should equal 2
Scenario: ListDir on non-directory raises error
Given a skill search sandbox directory
And a search sandbox file "notadir.txt" with content "x"
When I list-dir "notadir.txt" in the search sandbox
Then the search tool result should not be successful
And the search tool error should mention "not a directory"
Scenario: ListDir with invalid type_filter raises error
Given a skill search sandbox directory
When I list-dir-typed "." with type_filter "invalid"
Then the search tool result should not be successful
And the search tool error should mention "Invalid type_filter"
# ---- Glob ----
Scenario: Glob matches files by pattern
Given a skill search sandbox directory
And a search sandbox file "main.py" with content "code"
And a search sandbox file "test.py" with content "test"
And a search sandbox file "readme.md" with content "docs"
When I glob-match "." for "*.py"
Then the search tool result should be successful
And the search glob total should be 2
Scenario: Glob with exclude patterns
Given a skill search sandbox directory
And a search sandbox file "main.py" with content "code"
And a search sandbox file "test.py" with content "test"
When I glob-exclude "." for "*.py" excluding "test*"
Then the search tool result should be successful
And the search glob total should be 1
And no search match should have name "test.py"
Scenario: Glob with include filter
Given a skill search sandbox directory
And a search sandbox file "main.py" with content "code"
And a search sandbox file "test.py" with content "test"
When I glob-include "." for "*" including "*.py"
Then the search tool result should be successful
And the search glob total should be at least 2
Scenario: Glob returns deterministic sorted results
Given a skill search sandbox directory
And a search sandbox file "c.txt" with content "c"
And a search sandbox file "a.txt" with content "a"
And a search sandbox file "b.txt" with content "b"
When I glob-match "." for "*.txt"
Then the search tool result should be successful
And the search glob results should be sorted by name
Scenario: Glob on non-directory raises error
Given a skill search sandbox directory
And a search sandbox file "notadir.txt" with content "x"
When I glob-match "notadir.txt" for "*.txt"
Then the search tool result should not be successful
And the search tool error should mention "not a directory"
Scenario: Glob with limit truncation
Given a skill search sandbox directory
And a search sandbox file "1.txt" with content "one"
And a search sandbox file "2.txt" with content "two"
And a search sandbox file "3.txt" with content "three"
When I glob-limited "." for "*.txt" with limit 2
Then the search tool result should be successful
And the search output "truncated" should be boolean true
# ---- Grep ----
Scenario: Grep finds matching lines
Given a skill search sandbox directory
And a search sandbox file "haystack.txt" with content "needle in a haystack"
When I grep-search "." for "needle"
Then the search tool result should be successful
And the search grep total should be at least 1
Scenario: Grep with context lines
Given a skill search sandbox directory
And a search sandbox file "context.txt" with lines "before,match,after"
When I grep-context "." for "match" with context 1
Then the search tool result should be successful
And the search grep first match should have context_before
And the search grep first match should have context_after
Scenario: Grep skips binary files
Given a skill search sandbox directory
And a search sandbox binary file "data.bin"
And a search sandbox file "text.txt" with content "findme"
When I grep-search "." for "findme"
Then the search tool result should be successful
And the search grep matches should not reference "data.bin"
Scenario: Grep with per-file match cap
Given a skill search sandbox directory
And a search sandbox file "many.txt" with lines "match,match,match,match,match"
When I grep-capped "." for "match" with per_file_cap 2
Then the search tool result should be successful
And the search grep total should equal 2
Scenario: Grep with total match limit
Given a skill search sandbox directory
And a search sandbox file "m1.txt" with lines "hit,hit,hit"
And a search sandbox file "m2.txt" with lines "hit,hit,hit"
When I grep-maxed "." for "hit" with max_matches 3
Then the search tool result should be successful
And the search grep total should equal 3
And the search output "truncated" should be boolean true
Scenario: Grep with invalid regex raises error
Given a skill search sandbox directory
When I grep-search "." for "[invalid"
Then the search tool result should not be successful
And the search tool error should mention "Invalid regex"
Scenario: Grep on non-directory raises error
Given a skill search sandbox directory
And a search sandbox file "notadir.txt" with content "x"
When I grep-search "notadir.txt" for "x"
Then the search tool result should not be successful
And the search tool error should mention "not a directory"
# ---- Path Traversal ----
Scenario: Path traversal in list-dir is rejected
Given a skill search sandbox directory
When I list-dir "../../etc" in the search sandbox
Then the search tool result should not be successful
And the search tool error should mention "traversal"
Scenario: Path traversal in glob is rejected
Given a skill search sandbox directory
When I glob-match "../../etc" for "*"
Then the search tool result should not be successful
And the search tool error should mention "traversal"
Scenario: Path traversal in grep is rejected
Given a skill search sandbox directory
When I grep-search "../../etc" for "root"
Then the search tool result should not be successful
And the search tool error should mention "traversal"
# ---- Read-Only Capability ----
Scenario: ListDir has read_only capability
Given a skill search tools registry
Then the search tool "skill/list-dir" should have read_only capability
Scenario: Glob has read_only capability
Given a skill search tools registry
Then the search tool "skill/glob" should have read_only capability
Scenario: Grep has read_only capability
Given a skill search tools registry
Then the search tool "skill/grep" should have read_only capability
# ---- Registration ----
Scenario: Register all skill search tools
Given a skill search tools registry
Then the search registry should contain 3 tools
And the search registry should contain tool "skill/list-dir"
And the search registry should contain tool "skill/glob"
And the search registry should contain tool "skill/grep"
# ---- Coverage: OSError fallbacks, include patterns, directory skip ----
Scenario: ListDir handles stat failure gracefully
Given a skill search sandbox directory
And a search sandbox file "statfail.txt" with content "test"
When I execute list-dir with a stat failure mock
Then the list-dir result should contain "statfail.txt" with size 0
Scenario: Glob filters by include pattern
Given a skill search sandbox directory
And a search sandbox file "keep.txt" with content "a"
And a search sandbox file "skip.py" with content "b"
When I execute glob with pattern "*" and include "*.txt"
Then the glob results should contain "keep.txt"
And the glob results should not contain "skip.py"
Scenario: Glob handles stat failure gracefully
Given a skill search sandbox directory
And a search sandbox file "globstat.txt" with content "test"
When I execute glob with a stat failure mock and pattern "*.txt"
Then the glob result should contain "globstat.txt" with size 0
Scenario: Grep skips directories in rglob
Given a skill search sandbox directory
And a search sandbox subdirectory "subdir"
And a search sandbox file "subdir/match.txt" with content "findme here"
When I execute grep with pattern "findme" and include "*"
Then the grep results should contain "match.txt"
And the grep results should not contain a directory entry
Scenario: Grep skips unreadable files gracefully
Given a skill search sandbox directory
And a search sandbox file "unreadable.txt" with content "content"
When I execute grep with a read_text failure mock and pattern "content"
Then the grep result should be empty