OrderedIDs scanned qso_date straight into a time.Time. The column holds a formatted STRING — the repo writes it with Format(isoMillis) and reads it back through parseTimeLoose everywhere else — so the scan failed on every call, the index was never built, and omitempty then dropped the zero from the JSON. A column that shipped blank. Scans the string and parses only the newest row; the ordering already came from SQL, so 30 000 parses were never needed. Tested against a real SQLite file with the date stored exactly as the repo writes it, and with the ids running opposite to the dates — the imported-ADIF case that is the whole reason this is not the id. It fails without the fix with the scan error itself.
65 lines
1.9 KiB
Go
65 lines
1.9 KiB
Go
package qso
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
_ "modernc.org/sqlite"
|
|
)
|
|
|
|
// OrderedIDs against a real SQLite file, with qso_date stored the way the repo
|
|
// actually writes it.
|
|
//
|
|
// The first version scanned that column straight into a time.Time. It compiles,
|
|
// it reads correctly, and it fails at run time — the column holds a formatted
|
|
// STRING. The error was swallowed into "the column will be empty", which is
|
|
// exactly what shipped: a column that was always blank.
|
|
func TestOrderedIDsAgainstRealSQLite(t *testing.T) {
|
|
conn, err := sql.Open("sqlite", "file:"+filepath.Join(t.TempDir(), "t.db"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { conn.Close() })
|
|
if _, err := conn.Exec(`CREATE TABLE qso (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
callsign TEXT NOT NULL,
|
|
qso_date TEXT NOT NULL
|
|
)`); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Inserted NEWEST first, so the ids run opposite to the dates — what an
|
|
// imported ADIF produces, and the whole reason the id is not the number.
|
|
for _, row := range []struct {
|
|
call string
|
|
at time.Time
|
|
}{
|
|
{"NEWEST", time.Date(2026, 8, 13, 10, 0, 0, 0, time.UTC)},
|
|
{"MIDDLE", time.Date(2020, 1, 1, 10, 0, 0, 0, time.UTC)},
|
|
{"OLDEST", time.Date(1999, 5, 5, 10, 0, 0, 0, time.UTC)},
|
|
} {
|
|
if _, err := conn.Exec(`INSERT INTO qso (callsign, qso_date) VALUES (?, ?)`,
|
|
row.call, row.at.UTC().Format(isoMillis)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
ids, newest, err := NewRepo(conn).OrderedIDs(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("OrderedIDs: %v — this is the failure that emptied the column", err)
|
|
}
|
|
if len(ids) != 3 {
|
|
t.Fatalf("got %d ids, want 3", len(ids))
|
|
}
|
|
// Rows 1,2,3 were inserted newest→oldest, so chronological order is 3,2,1.
|
|
if ids[0] != 3 || ids[1] != 2 || ids[2] != 1 {
|
|
t.Errorf("order = %v, want the oldest contact first (3,2,1)", ids)
|
|
}
|
|
if newest.Year() != 2026 {
|
|
t.Errorf("newest = %v, want the 2026 contact", newest)
|
|
}
|
|
}
|