Files
OpsLog/internal/adif/import_e2e_test.go
T
rouggy fee2303625 test: pin the whole ADIF import path, not just the conversion step
CONTEST_ID was reported as not imported. It is imported: this drives an
operator's own IOTA-contest record through a real SQLite logbook and reads the
row back, confirming contest_id=RSGB-IOTA and iota=EU005 in the database.

The conversion-level test was already green while the report stood, which is
exactly why it was not enough — a promoted field can be dropped at any of five
places between the file and the grid, and only the round trip says which. The
answer here turned out to be none of them: the Contest columns are simply hidden
by default in the QSO grid.
2026-07-30 15:07:48 +02:00

75 lines
2.0 KiB
Go

package adif_test
import (
"context"
"os"
"path/filepath"
"strings"
"testing"
"hamlog/internal/adif"
"hamlog/internal/db"
"hamlog/internal/qso"
)
// End to end: an operator's own record, through a real SQLite logbook and back.
//
// Written because CONTEST_ID was reported as "not imported". It is imported, and
// this pins the whole path — parse, remap, insert, read back — rather than the
// conversion step alone, which was already green while the report stood. A field
// can be lost at five places between the file and the grid; only the round trip
// says which.
func TestContestIDSurvivesImport(t *testing.T) {
dir := t.TempDir()
conn, err := db.Open(filepath.Join(dir, "logbook.db"))
if err != nil {
t.Fatalf("open: %v", err)
}
defer conn.Close()
const file = "<CALL:6>2E0CVN <QSO_DATE:8>20260725 <TIME_ON:6>120039 <STATE:5>EU005 <BAND:3>20M <FREQ:6>14.014 <MODE:2>CW <CONTEST_ID:9>RSGB-IOTA <RST_RCVD:3>599 <RST_SENT:3>599 <SRX_STRING:8>001EU005 <EOR>\n"
path := filepath.Join(dir, "in.adi")
if err := os.WriteFile(path, []byte(file), 0o644); err != nil {
t.Fatal(err)
}
repo := qso.NewRepo(conn)
im := &adif.Importer{Repo: repo, FieldMap: map[string]string{"state": "iota"}}
res, err := im.ImportFile(context.Background(), path)
if err != nil {
t.Fatalf("import: %v", err)
}
t.Logf("result: %+v", res)
rows, err := conn.Query("SELECT callsign, contest_id, iota, state FROM qso")
if err != nil {
t.Fatal(err)
}
defer rows.Close()
n := 0
for rows.Next() {
var call, contest, iota, state any
if err := rows.Scan(&call, &contest, &iota, &state); err != nil {
t.Fatal(err)
}
n++
t.Logf("stored: call=%v contest_id=%v iota=%v state=%v", call, contest, iota, state)
if s := strings.TrimSpace(toS(contest)); s != "RSGB-IOTA" {
t.Errorf("contest_id in the DATABASE = %q, want RSGB-IOTA", s)
}
}
if n == 0 {
t.Fatal("nothing was stored")
}
}
func toS(v any) string {
switch x := v.(type) {
case string:
return x
case []byte:
return string(x)
}
return ""
}