package main import ( "testing" "hamlog/internal/award" ) // The catalog is the channel through which a shipped award is both DELIVERED and // CORRECTED. These tests pin the three rules that make that safe: // - a new catalog award reaches an operator who already has awards stored; // - a fixed one (higher Version) replaces the stored copy; // - unless the operator has edited it, in which case their work wins. func catalogDef(t *testing.T, code string) award.Def { t.Helper() for _, d := range award.Defaults() { if d.Code == code { return d } } t.Fatalf("%s is not in the catalog", code) return award.Def{} } func findDef(defs []award.Def, code string) (award.Def, bool) { for _, d := range defs { if d.Code == code { return d, true } } return award.Def{}, false } func TestMergeCatalogAddsMissingAward(t *testing.T) { stored := []award.Def{{Code: "MYOWN", Name: "Mine", Valid: true}} got, updated, changed := mergeCatalog(stored) if !changed { t.Fatal("merge reported no change, but every catalog award was missing") } if len(updated) != 0 { t.Errorf("updated = %v, want none: an ADDED award is not an UPDATED one", updated) } if _, ok := findDef(got, "FFMA"); !ok { t.Error("FFMA was not added — a newly shipped award never reaches an existing install") } if _, ok := findDef(got, "MYOWN"); !ok { t.Error("the operator's own award was dropped by the merge") } } func TestMergeCatalogUpdatesOlderVersion(t *testing.T) { // The stored copy is an older revision of a shipped award, with a broken rule. old := catalogDef(t, "FFMA") old.Version = catalogDef(t, "FFMA").Version - 1 old.Field = "wrong" old.Valid = false // the operator disabled it — a preference, not a definition got, updated, changed := mergeCatalog([]award.Def{old}) if !changed || len(updated) == 0 { t.Fatal("a higher catalog version did not replace the stored definition — a shipped award could never be FIXED") } d, _ := findDef(got, "FFMA") if d.Field != "grid4" { t.Errorf("FFMA field = %q, want the catalog's %q", d.Field, "grid4") } if d.Valid { t.Error("the update re-enabled an award the operator had switched off; that is their choice to make, not ours") } } func TestMergeCatalogSkipsUserEdited(t *testing.T) { old := catalogDef(t, "FFMA") old.Version = catalogDef(t, "FFMA").Version - 1 old.Field = "mine" old.UserEdited = true got, updated, _ := mergeCatalog([]award.Def{old}) if len(updated) != 0 { t.Fatalf("updated = %v: an award the operator has edited must never be overwritten", updated) } if d, _ := findDef(got, "FFMA"); d.Field != "mine" { t.Errorf("field = %q, want the operator's %q", d.Field, "mine") } } func TestMarkUserEditedOnlyOnRealChange(t *testing.T) { prev := []award.Def{ {Code: "A", Name: "A", Field: "state", Valid: true, Version: 2}, {Code: "B", Name: "B", Field: "cqz", Valid: true, Version: 2}, } next := []award.Def{ {Code: "A", Name: "A", Field: "state", Valid: true}, // untouched {Code: "B", Name: "B", Field: "county", Valid: true}, // changed {Code: "C", Name: "C", Field: "note", Valid: true}, // brand new } markUserEdited(next, prev) if next[0].UserEdited { t.Error("A was flagged as edited although nothing about it changed — every save would freeze every award out of future updates") } if !next[1].UserEdited { t.Error("B changed field and was not flagged; a catalog update would overwrite the operator's work") } if next[2].UserEdited { t.Error("C is a brand-new award; there is no shipped version to protect it from") } // A save must not pretend to be a new shipped revision. if next[0].Version != 2 || next[1].Version != 2 { t.Errorf("versions = %d/%d, want both 2: saving is not shipping", next[0].Version, next[1].Version) } }