package watchlist import ( "encoding/json" "os" "path/filepath" "testing" ) func TestPrefixMatch(t *testing.T) { s := New(filepath.Join(t.TempDir(), "watchlist.json")) if err := s.Add("RI0SP", false); err != nil { t.Fatal(err) } // The reason prefix matching exists: expeditions sign portable. for _, call := range []string{"RI0SP", "RI0SP/MM", "RI0SP/P"} { if _, ok := s.Match(call); !ok { t.Errorf("Match(%q) = false, want true", call) } } if _, ok := s.Match("RI0S"); ok { t.Error("a SHORTER call must not match the entry") } if _, ok := s.Match("F4BPO"); ok { t.Error("an unrelated call matched") } } func TestDXHunterFileRoundTrips(t *testing.T) { // A real DXHunter entry, ClubLog block included. It must survive // load → save byte-meaningfully: same keys, values preserved. src := `[{"callsign":"C5SP","lastSeen":"0001-01-01T00:00:00Z","lastSeenStr":"Never", "addedAt":"2026-01-17T00:18:43.89Z","spotCount":7,"isContest":true,"notify":true, "isExpedition":true,"clubLogQSOs24h":120,"clubLogTotalQSOs":30500, "clubLogHasOQRS":true,"clubLogLiveStream":true,"clubLogUpdatedAt":"2026-08-28T22:32:18Z"}]` dir := t.TempDir() path := filepath.Join(dir, "watchlist.json") if err := os.WriteFile(path, []byte(src), 0o644); err != nil { t.Fatal(err) } s := New(path) s.Flush() out, err := os.ReadFile(path) if err != nil { t.Fatal(err) } var list []map[string]any if err := json.Unmarshal(out, &list); err != nil { t.Fatal(err) } if len(list) != 1 { t.Fatalf("got %d entries", len(list)) } e := list[0] for k, want := range map[string]any{ "callsign": "C5SP", "isContest": true, "notify": true, "isExpedition": true, "clubLogQSOs24h": float64(120), "clubLogTotalQSOs": float64(30500), "clubLogHasOQRS": true, } { if e[k] != want { t.Errorf("%s = %v, want %v", k, e[k], want) } } } func TestMarkSeenAndNotify(t *testing.T) { s := New(filepath.Join(t.TempDir(), "watchlist.json")) _ = s.Add("HB040A", false) _ = s.SetNotify("HB040A", true) entry, notify, ok := s.MarkSeen("HB040A") if !ok || !notify || entry != "HB040A" { t.Fatalf("MarkSeen = %q %v %v", entry, notify, ok) } list := s.Entries() if list[0].SpotCount != 1 || list[0].LastSeenStr != "Just now" { t.Errorf("entry after MarkSeen: %+v", list[0]) } }