HAMLOG.online was added after most profiles were configured, so it had no entry in the shipped defaults and no stored value either: it came back blank, and blank is not a status anybody chose. Every service now starts the same way — the sent side at R, the received side at N — and a blank left by a service that did not exist when the operator last saved is filled in from that. A status they chose themselves is untouched. Two tests hold the line: no sent side may default to Y, and no field may be left without a default. Y means "already sent", so it makes the uploader skip the contact for ever — an operator with eQSL Sent at Y had a logbook that never reached eQSL, and the only trace was one line in the application log. Wiki, both from operator reports: QSL Management opens with Confirmations — what the page actually is (the status stamped on every new QSO, not an action), what each status does, and the warning about Y in the plainest words available, because it fails silently and by design. Digital Modes and GridTracker is new. Unicast and multicast explained from the operating problem rather than the networking: one letterbox that two programs watch, against a broadcast everyone can tune to. It carries the real evidence — two starts of one station an hour apart, decodes in the second and none in the first, the only difference being whether GridTracker or OpsLog reached port 2237 first — then the settings for WSJT-X, JTDX, MSHV, GridTracker and OpsLog, the 127.0.0.1-in-the-group-box mistake, what to do if unicast is unavoidable, and how to check it from the log. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
64 lines
2.3 KiB
Go
64 lines
2.3 KiB
Go
package main
|
|
|
|
import (
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// The sent side must never start at Y.
|
|
//
|
|
// Y means "already sent", so the uploader skips the contact for ever. An
|
|
// operator whose eQSL default was Y had a logbook that never reached eQSL and
|
|
// nothing on screen to explain it — the log said "not eligible, EQSLSent
|
|
// already Y" and that is the only place it was ever said.
|
|
func TestNoConfirmationDefaultsToAlreadySent(t *testing.T) {
|
|
d := defaultQSLDefaults()
|
|
v := reflect.ValueOf(d)
|
|
for i := 0; i < v.NumField(); i++ {
|
|
name := v.Type().Field(i).Name
|
|
val := strings.ToUpper(strings.TrimSpace(v.Field(i).String()))
|
|
if val == "Y" {
|
|
t.Errorf("%s defaults to Y — every new QSO would be skipped by its uploader for ever", name)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Every field has a default. A service added without one comes back blank,
|
|
// which is not a status anybody chose — HAMLOG.online arrived that way and
|
|
// every existing profile got nothing for it.
|
|
func TestEveryConfirmationHasADefault(t *testing.T) {
|
|
d := defaultQSLDefaults()
|
|
v := reflect.ValueOf(d)
|
|
for i := 0; i < v.NumField(); i++ {
|
|
if strings.TrimSpace(v.Field(i).String()) == "" {
|
|
t.Errorf("%s has no default — add it to defaultQSLDefaults", v.Type().Field(i).Name)
|
|
}
|
|
}
|
|
}
|
|
|
|
// A profile saved before a service existed has no value stored for it, and
|
|
// gets the shipped one rather than a blank.
|
|
func TestStoredBlanksAreFilledFromTheDefaults(t *testing.T) {
|
|
// What an old profile looks like: the services it knew about are set, the
|
|
// ones added later are empty.
|
|
stored := QSLDefaults{
|
|
QSLSent: "N", QSLRcvd: "N",
|
|
EQSLSent: "R", EQSLRcvd: "N",
|
|
LOTWSent: "R", LOTWRcvd: "N",
|
|
ClublogStatus: "R", ClublogCfm: "N", HRDLogStatus: "R",
|
|
QRZComStatus: "R", QRZComCfm: "N",
|
|
}
|
|
got := fillMissingQSLDefaults(stored)
|
|
if got.HamlogStatus != "R" || got.HamlogCfm != "N" || got.HamqthStatus != "R" {
|
|
t.Errorf("services added later were left blank: hamlog=%q/%q hamqth=%q",
|
|
got.HamlogStatus, got.HamlogCfm, got.HamqthStatus)
|
|
}
|
|
// And a value the operator DID choose is untouched.
|
|
chosen := QSLDefaults{QSLSent: "I", EQSLSent: "N"}
|
|
filled := fillMissingQSLDefaults(chosen)
|
|
if filled.QSLSent != "I" || filled.EQSLSent != "N" {
|
|
t.Errorf("a chosen status was overwritten: %q / %q", filled.QSLSent, filled.EQSLSent)
|
|
}
|
|
}
|