From fee23036251b8b4711a6a2e30de648ea930b2cf9 Mon Sep 17 00:00:00 2001 From: rouggy Date: Thu, 30 Jul 2026 15:07:48 +0200 Subject: [PATCH] test: pin the whole ADIF import path, not just the conversion step MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- internal/adif/import_e2e_test.go | 74 ++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 internal/adif/import_e2e_test.go diff --git a/internal/adif/import_e2e_test.go b/internal/adif/import_e2e_test.go new file mode 100644 index 0000000..e163dfc --- /dev/null +++ b/internal/adif/import_e2e_test.go @@ -0,0 +1,74 @@ +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 = "2E0CVN 20260725 120039 EU005 20M 14.014 CW RSGB-IOTA 599 599 001EU005 \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 "" +}