115 lines
3.7 KiB
Go
115 lines
3.7 KiB
Go
package qso
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"encoding/json"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
_ "modernc.org/sqlite"
|
|
)
|
|
|
|
// openBulkTestDB builds the minimum of the qso table this needs. It does not go
|
|
// through db.Open (that would pull the whole migration set and an import cycle);
|
|
// the columns BulkSetExtra touches are extras_json and updated_at.
|
|
func openBulkTestDB(t *testing.T) *sql.DB {
|
|
t.Helper()
|
|
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,
|
|
extras_json TEXT,
|
|
updated_at TEXT
|
|
)`); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return conn
|
|
}
|
|
|
|
func extras(t *testing.T, conn *sql.DB, id int64) map[string]any {
|
|
t.Helper()
|
|
var raw sql.NullString
|
|
if err := conn.QueryRow(`SELECT extras_json FROM qso WHERE id = ?`, id).Scan(&raw); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !raw.Valid || raw.String == "" {
|
|
return map[string]any{}
|
|
}
|
|
var m map[string]any
|
|
if err := json.Unmarshal([]byte(raw.String), &m); err != nil {
|
|
t.Fatalf("extras_json is not valid JSON (%q): %v", raw.String, err)
|
|
}
|
|
return m
|
|
}
|
|
|
|
// OWNER_CALLSIGN has no promoted column, so bulk-editing it means merging a key
|
|
// into extras_json. The thing that must not happen is collateral damage: the
|
|
// other ADIF extras on the same QSO have to survive.
|
|
func TestBulkSetExtraPreservesOtherExtras(t *testing.T) {
|
|
conn := openBulkTestDB(t)
|
|
r := &Repo{db: conn}
|
|
ctx := context.Background()
|
|
|
|
// Two QSOs with existing extras, one with none at all (NULL column).
|
|
conn.Exec(`INSERT INTO qso (callsign, extras_json) VALUES ('F5LIT', '{"SILENT_KEY":"Y","ANT_PATH":"S"}')`)
|
|
conn.Exec(`INSERT INTO qso (callsign, extras_json) VALUES ('PA3EYF', '{"ANT_PATH":"L"}')`)
|
|
conn.Exec(`INSERT INTO qso (callsign, extras_json) VALUES ('F4BPO', NULL)`)
|
|
|
|
n, err := r.BulkSetExtra(ctx, []int64{1, 2, 3}, "OWNER_CALLSIGN", "TM2Q")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if n != 3 {
|
|
t.Errorf("updated %d rows, want 3", n)
|
|
}
|
|
|
|
e1 := extras(t, conn, 1)
|
|
if e1["OWNER_CALLSIGN"] != "TM2Q" {
|
|
t.Errorf("row 1 OWNER_CALLSIGN = %v, want TM2Q", e1["OWNER_CALLSIGN"])
|
|
}
|
|
if e1["SILENT_KEY"] != "Y" || e1["ANT_PATH"] != "S" {
|
|
t.Errorf("row 1 lost its other extras: %v", e1)
|
|
}
|
|
// A NULL extras_json must become a valid object, not stay null or hold "null".
|
|
if e3 := extras(t, conn, 3); e3["OWNER_CALLSIGN"] != "TM2Q" {
|
|
t.Errorf("row 3 (extras_json was NULL) = %v, want OWNER_CALLSIGN=TM2Q", e3)
|
|
}
|
|
}
|
|
|
|
// Clearing the field must REMOVE the key: a blank extra would otherwise be
|
|
// carried into every ADIF export from then on.
|
|
func TestBulkSetExtraEmptyRemovesKey(t *testing.T) {
|
|
conn := openBulkTestDB(t)
|
|
r := &Repo{db: conn}
|
|
conn.Exec(`INSERT INTO qso (callsign, extras_json) VALUES ('F5LIT', '{"OWNER_CALLSIGN":"TM2Q","ANT_PATH":"S"}')`)
|
|
|
|
if _, err := r.BulkSetExtra(context.Background(), []int64{1}, "OWNER_CALLSIGN", ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
e := extras(t, conn, 1)
|
|
if _, present := e["OWNER_CALLSIGN"]; present {
|
|
t.Errorf("OWNER_CALLSIGN should be gone, got %v", e)
|
|
}
|
|
if e["ANT_PATH"] != "S" {
|
|
t.Errorf("clearing one extra removed another: %v", e)
|
|
}
|
|
}
|
|
|
|
// The frontend field id must resolve to the ADIF key, and nothing else must slip
|
|
// through — this map is the whitelist guarding a spliced JSON path.
|
|
func TestBulkExtraKeyWhitelist(t *testing.T) {
|
|
if got := BulkExtraKey("owner_callsign"); got != "OWNER_CALLSIGN" {
|
|
t.Errorf(`BulkExtraKey("owner_callsign") = %q, want "OWNER_CALLSIGN"`, got)
|
|
}
|
|
for _, bad := range []string{"", "callsign", "notes", "OWNER_CALLSIGN", "owner_callsign'"} {
|
|
if got := BulkExtraKey(bad); got != "" {
|
|
t.Errorf("BulkExtraKey(%q) = %q, want empty", bad, got)
|
|
}
|
|
}
|
|
}
|