test(detect): harden nested-ignore tests (#1847 followup)

Tighten the brittle negation test to use .py files so classification
lands in the deterministic `code` bucket (was checking a fuzzy
document+unclassified union), and add a composition test asserting a
nested `.gitignore` `!` re-include outranks both a root `.gitignore` and
`.git/info/exclude` (#1810) — locking the precedence across all three
ignore sources.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
safishamsi
2026-07-13 18:35:09 +01:00
co-authored by Claude Opus 4.8
parent 8a5287a979
commit 7ca43ab314
+32 -13
View File
@@ -269,23 +269,42 @@ def test_gitignore_nested_below_root_prunes_whole_directory(tmp_path):
def test_gitignore_nested_negation_overrides_broader_root_rule(tmp_path):
"""A closer (nested) .gitignore's `!` re-include wins over a root exclude,
matching git's closer-file-wins precedence."""
(tmp_path / ".gitignore").write_text("*.txt\n")
matching git's closer-file-wins precedence. Uses .py so classification lands
in the deterministic `code` bucket."""
(tmp_path / ".gitignore").write_text("*.py\n")
sub = tmp_path / "vendor" / "sub"
sub.mkdir(parents=True)
(sub / ".gitignore").write_text("!important.txt\n")
(tmp_path / "root.txt").write_text("a")
(sub / "important.txt").write_text("b")
(sub / "other.txt").write_text("c")
(sub / ".gitignore").write_text("!important.py\n")
(tmp_path / "root.py").write_text("a = 1")
(sub / "important.py").write_text("b = 1")
(sub / "other.py").write_text("c = 1")
result = detect(tmp_path)
# .txt is not a code extension so check the document bucket instead
doc_files = result["files"]["document"] if "document" in result["files"] else []
unclassified = result.get("unclassified", [])
all_seen = doc_files + unclassified
assert any("important.txt" in f for f in all_seen)
assert not any(f.endswith("root.txt") for f in all_seen)
assert not any(f.endswith("other.txt") for f in all_seen)
code = result["files"]["code"]
# nested `!important.py` re-includes it despite the root `*.py` exclude...
assert any("vendor/sub/important.py" in f for f in code)
# ...while the root-excluded and non-re-included files stay out
assert not any(f.endswith("root.py") for f in code)
assert not any(f.endswith("other.py") for f in code)
def test_nested_ignore_overrides_git_info_exclude_and_root(tmp_path):
"""Precedence across all three sources: a nested `.gitignore` `!` re-include
outranks both a root `.gitignore` and `.git/info/exclude` (lowest, from
#1810), while an info/exclude-only file with no re-include stays out."""
(tmp_path / ".git" / "info").mkdir(parents=True)
(tmp_path / ".git" / "info" / "exclude").write_text("*.py\n")
(tmp_path / ".gitignore").write_text("keep.py\n") # root also excludes it
sub = tmp_path / "a" / "b"
sub.mkdir(parents=True)
(sub / ".gitignore").write_text("!keep.py\n") # nearest wins -> re-included
(sub / "keep.py").write_text("x = 1")
(tmp_path / "drop.py").write_text("y = 1") # only info/exclude -> excluded
result = detect(tmp_path)
code = result["files"]["code"]
assert any("a/b/keep.py" in f for f in code), "nested ! must beat root + info/exclude"
assert not any(f.endswith("drop.py") for f in code)
def test_detect_handles_circular_symlinks(tmp_path):