From 68ae825f1d4db446337549b2fabe0e244edd9203 Mon Sep 17 00:00:00 2001 From: "Robert D. Schoening" Date: Mon, 20 Jul 2026 22:29:42 -0400 Subject: [PATCH] fix(cli): --rank was swallowed as a search term (zero-match bug) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported: searching "bowling" found 351 matches with Sort by relevance OFF, but 0 with it ON. Root cause: the CLI detected `want_rank = "--rank" in args` but never removed `--rank` from args (unlike --no-index), so it leaked into the positional search terms. The query became `bowling AND "--rank"`, which in AND mode matches nothing → 0 results. (In OR mode it silently polluted the query without dropping the count.) Fix: strip `--rank` from args after detecting it, exactly like --no-index. The bug slipped through because slice-1 tests exercised the Python API (search(rank=True)) but never the CLI arg-parsing path. Added a CLI regression test (test_rank_flag_not_swallowed_as_search_term) that drives main() with --rank in both OR and AND mode; verified it FAILS without this fix and PASSES with it. No version bump. Co-Authored-By: Claude Opus 4.8 (1M context) Signed-off-by: Robert D. Schoening --- peekdocs/cli.py | 4 ++++ tests/test_cli.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/peekdocs/cli.py b/peekdocs/cli.py index c49e7d8d..82ac30d3 100644 --- a/peekdocs/cli.py +++ b/peekdocs/cli.py @@ -1951,6 +1951,10 @@ def _index_progress(done, total_count, filename): return 0 if total_matches > 0 else 1 want_rank = "--rank" in args + if want_rank: + # Strip it so it isn't swallowed as a positional search term + # (which would corrupt the query, e.g. AND-mode → zero matches). + args.remove("--rank") no_index = "--no-index" in args if no_index: args.remove("--no-index") diff --git a/tests/test_cli.py b/tests/test_cli.py index 9a26b758..ac82f189 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -78,6 +78,34 @@ def test_search_finds_matches(tmp_path, monkeypatch, capsys): assert highlighted_runs[1].text == "Hello" +def test_rank_flag_not_swallowed_as_search_term(tmp_path, monkeypatch, capsys): + # Regression: --rank must be stripped from args, not consumed as a + # positional search term. It once leaked into the query, so an AND search + # "term --rank" matched nothing (real report: 351 matches -> 0 with rank on). + # Ranking reorders results; it must never change the match COUNT. + (tmp_path / "a.txt").write_text("bowling night was great\n") + (tmp_path / "b.txt").write_text("more bowling night here\n") + monkeypatch.chdir(tmp_path) + main(["--index"]) + capsys.readouterr() + + # OR mode: --rank must not change the match count (2 files, both match). + assert main(["bowling"]) == 0 + plain = capsys.readouterr().out + assert main(["bowling", "--rank"]) == 0 + ranked = capsys.readouterr().out + assert f"{HIGHLIGHT}2{RESET} match(es)" in plain + assert f"{HIGHLIGHT}2{RESET} match(es)" in ranked # NOT 0 — query wasn't polluted + # The actual query echo (not the command echo) must be just the term. + assert "on [bowling --rank]" not in ranked.replace(HIGHLIGHT, "").replace(RESET, "") + + # AND mode — the exact case reported (351 -> 0). Return code 0 = matches + # found; before the fix "bowling AND night AND --rank" matched nothing (1). + assert main(["bowling", "night", "-a"]) == 0 + capsys.readouterr() + assert main(["bowling", "night", "-a", "--rank"]) == 0 + + def test_search_no_matches(tmp_path, monkeypatch, capsys): doc = Document() doc.add_paragraph("Nothing here")