fix(cli): search limit counts visible results, filter before cap

Pass 0 (unlimited) to m.Search so the mail layer returns all matching
headers; the existing post-filter loop already caps at the caller's
limit, mirroring ListCmd. Add TestSearchLimitCountsVisibleOnly to prove
filtering happens before the cap.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 00:17:39 +01:00
parent dd181ef63c
commit 6061bd2a78
2 changed files with 33 additions and 1 deletions
+1 -1
View File
@@ -187,7 +187,7 @@ func SearchCmd(d Deps, account, folder string, sc mail.SearchCriteria, limit int
return d.emit(*fail) return d.emit(*fail)
} }
defer done() defer done()
headers, err := m.Search(folder, sc, limit) headers, err := m.Search(folder, sc, 0)
if err != nil { if err != nil {
return d.emit(Failure(CodeNetwork, err.Error())) return d.emit(Failure(CodeNetwork, err.Error()))
} }
+32
View File
@@ -152,6 +152,38 @@ func TestGetFilteredReturnsErrorForExit(t *testing.T) {
} }
} }
func TestSearchLimitCountsVisibleOnly(t *testing.T) {
// fakeMailer.Search returns all headers regardless of the limit passed in.
// Headers: UIDs 1,3,5 are visible (@trusted.com); UIDs 2,4 are filtered.
// With limit=2, SearchCmd must return 2 visible messages — not fewer, which
// would happen if the mail layer truncated to limit=2 before filtering.
fm := &fakeMailer{
uidValidity: 1, maxUID: 5,
headers: []mail.Header{
{UID: 1, From: "a@trusted.com", Subject: "one"},
{UID: 2, From: "x@evil.com", Subject: "spam1"}, // filtered
{UID: 3, From: "b@trusted.com", Subject: "two"},
{UID: 4, From: "y@evil.com", Subject: "spam2"}, // filtered
{UID: 5, From: "c@trusted.com", Subject: "three"},
},
}
d, buf := newDeps(t, fm)
if err := SearchCmd(d, "work", "INBOX", mail.SearchCriteria{}, 2); err != nil {
t.Fatalf("SearchCmd: %v", err)
}
res := decode(t, buf.Bytes())
if res["error"] != false {
t.Fatalf("unexpected error envelope: %v", res)
}
data := res["data"].(map[string]any)
msgs := data["messages"].([]any)
// With pre-filter cap (old bug): limit=2 would have fetched UIDs 1,2 then
// filtered, yielding only 1 visible. Correct behaviour: 2 visible (1,3).
if len(msgs) != 2 {
t.Fatalf("want 2 visible messages, got %d: %v", len(msgs), msgs)
}
}
func TestAckAdvancesStateAndFiltered(t *testing.T) { func TestAckAdvancesStateAndFiltered(t *testing.T) {
fm := &fakeMailer{ fm := &fakeMailer{
uidValidity: 1, maxUID: 100, uidValidity: 1, maxUID: 100,