test: prove the status filter filters; fix a regex escape in the filter builder
An operator reports that "QRZ.com received status = N" returns rows showing both N and Y (issue #5 follow-up). The SQL is a plain col = ?, but reading it cannot distinguish a wrong query from a UI that kept the previous rows after an error — so this runs it: five QSOs inserted, filtered, and both the list and the count asserted. The backend filters correctly, list and count agree. The fault is not in the query. Found while looking: the filter builder tested an ADIF date with /^d{8}$/ instead of /^\d{8}$/. The escape was missing, so the branch never matched and the calendar input was handed "20260728", which type=date rejects — an empty box over a value that was really stored.
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
package qso
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"hamlog/internal/db"
|
||||
)
|
||||
|
||||
// End-to-end proof that filtering a confirmation STATUS actually filters.
|
||||
//
|
||||
// Reported from the field: "QRZ.com received status = N" returned rows showing
|
||||
// both N and Y. The SQL is a plain `col = ?`, so either the query is right and
|
||||
// the fault is elsewhere (the UI keeping the previous rows on an error, say), or
|
||||
// it is wrong here. Reading the code cannot tell those apart — running it can.
|
||||
func TestFilterOnConfirmationStatus(t *testing.T) {
|
||||
conn, err := db.Open(filepath.Join(t.TempDir(), "logbook.db"))
|
||||
if err != nil {
|
||||
t.Fatalf("open: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
r := NewRepo(conn)
|
||||
ctx := context.Background()
|
||||
|
||||
for _, c := range []struct{ call, status string }{
|
||||
{"W1AAA", "Y"},
|
||||
{"W1BBB", "N"},
|
||||
{"W1CCC", "Y"},
|
||||
{"W1DDD", "N"},
|
||||
{"W1EEE", ""}, // never touched by a download — NULL/empty, neither Y nor N
|
||||
} {
|
||||
q := QSO{Callsign: c.call, Band: "20m", Mode: "SSB", QRZComDownloadStatus: c.status}
|
||||
if _, err := r.Add(ctx, q); err != nil {
|
||||
t.Fatalf("insert %s: %v", c.call, err)
|
||||
}
|
||||
}
|
||||
|
||||
got, err := r.ListFiltered(ctx, QueryFilter{
|
||||
Conditions: []Condition{{Field: "qrzcom_qso_download_status", Op: "eq", Value: "N"}},
|
||||
Match: "AND",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("ListFiltered: %v", err)
|
||||
}
|
||||
if len(got) != 2 {
|
||||
t.Fatalf("filter = N returned %d rows, want 2", len(got))
|
||||
}
|
||||
for _, q := range got {
|
||||
if q.QRZComDownloadStatus != "N" {
|
||||
t.Errorf("filter = N returned %s with status %q", q.Callsign, q.QRZComDownloadStatus)
|
||||
}
|
||||
}
|
||||
|
||||
// A count that disagreed with the list would show "2 matches" above a grid of
|
||||
// five rows — the exact shape of the report.
|
||||
n, err := r.CountFiltered(ctx, QueryFilter{
|
||||
Conditions: []Condition{{Field: "qrzcom_qso_download_status", Op: "eq", Value: "N"}},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("CountFiltered: %v", err)
|
||||
}
|
||||
if n != 2 {
|
||||
t.Errorf("CountFiltered = %d, want 2", n)
|
||||
}
|
||||
}
|
||||
|
||||
// An unknown field must ERROR rather than be silently dropped: a dropped
|
||||
// condition returns the whole logbook, which reads as "the filter did nothing".
|
||||
func TestFilterRejectsUnknownField(t *testing.T) {
|
||||
if _, _, err := conditionSQL(Condition{Field: "not_a_column", Op: "eq", Value: "N"}); err == nil {
|
||||
t.Error("unknown filter field accepted — it would silently return every QSO")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user