This commit is contained in:
2026-06-06 00:02:56 +02:00
parent 51d3a734e8
commit 922a185208
10 changed files with 941 additions and 131 deletions
+63
View File
@@ -1,6 +1,7 @@
package award
import (
"sort"
"testing"
"hamlog/internal/qso"
@@ -66,6 +67,17 @@ func TestComputeDXCCAndConfirm(t *testing.T) {
}
}
func TestNatLess(t *testing.T) {
in := []string{"10", "2", "1", "20", "3", "D10", "D2A", "D2", "AL", "AK"}
want := []string{"1", "2", "3", "10", "20", "AK", "AL", "D2", "D2A", "D10"}
sort.Slice(in, func(i, j int) bool { return natLess(in[i], in[j]) })
for i := range want {
if in[i] != want[i] {
t.Fatalf("natLess order = %v, want %v", in, want)
}
}
}
func refCodes(r Result) []string {
out := make([]string, 0, len(r.Refs))
for _, rf := range r.Refs {
@@ -74,6 +86,57 @@ func refCodes(r Result) []string {
return out
}
// Legacy defs (Type=="", Valid==false, old DDFM pattern) get upgraded.
func TestMigrate(t *testing.T) {
legacy := []Def{
{Code: "DXCC", Field: "dxcc", Confirm: []string{"lotw", "qsl"}, Total: 340}, // Valid=false, Type=""
{Code: "DDFM", Field: "note", Pattern: `(?i)\bD(\d{1,2}[AB]?)\b`, Total: 96},
{Code: "MYAWARD", Field: "note"}, // custom legacy
}
out, changed := Migrate(legacy)
if !changed {
t.Fatal("expected migration to change legacy defs")
}
for _, d := range out {
if !d.Valid {
t.Errorf("%s should be enabled after migration", d.Code)
}
if d.Type == "" {
t.Errorf("%s should have a type after migration", d.Code)
}
}
if out[1].Pattern != `(?i)\b(D\d{1,2}[AB]?)\b` {
t.Errorf("DDFM pattern not fixed: %q", out[1].Pattern)
}
if out[2].Type != TypeQSOFields {
t.Errorf("custom legacy award type = %q, want QSOFIELDS", out[2].Type)
}
// Idempotent: a second pass changes nothing.
if _, changed2 := Migrate(out); changed2 {
t.Error("migration should be idempotent")
}
}
// Regression: a predefined reference whose own DXCC differs from the QSO's
// entity must still count when the field code matches and the award-level
// DXCC filter allows it (WAS Alaska: state AK, but DXCC entity 6, not 291).
func TestComputePredefinedCrossDXCC(t *testing.T) {
def := Def{Code: "WAS", Type: TypeQSOFields, Field: "state", ExactMatch: true,
DXCCFilter: []int{291, 110, 6}, Confirm: []string{"lotw", "qsl"}, Valid: true}
qsos := []qso.QSO{
{Callsign: "KL5DX", Band: "20m", DXCC: ip(6), State: "AK", LOTWRcvd: "Y"}, // Alaska
{Callsign: "K1ABC", Band: "20m", DXCC: ip(291), State: "MA"}, // continental
}
refMetas := map[string][]RefMeta{"WAS": {
{Code: "AK", Name: "Alaska", DXCCList: []int{291}, Valid: true}, // wrong DXCC on purpose
{Code: "MA", Name: "Massachusetts", DXCCList: []int{291}, Valid: true},
}}
r := Compute([]Def{def}, qsos, refMetas, nil)[0]
if r.Worked != 2 {
t.Errorf("WAS worked = %d, want 2 (Alaska must count despite DXCC 6) %v", r.Worked, refCodes(r))
}
}
// A predefined award only counts references present in its list, lists the
// unworked ones too, and uses the list size as the denominator.
func TestComputePredefinedList(t *testing.T) {