package inventory import ( "testing" "time" "github.com/novox/mesh-controller/internal/chain" ) // **What delivery 1 recorded in node.sent_builds is copied onto the keys of the assignments, once** (issue 1109, review // round 1): a module pulled in by an assignment had no commit on its key, so it would fall out of SentBuilds. The copy // creates a standing pulled-in key where none stands, fills a standing key that names no commit (commit, and the build // when one is known, no rollout), and overwrites nothing: a key with a commit, a hand send's unknown, a wait for a build. func TestWhatTheColumnRecordedIsCopiedOntoTheKeysAndOverwritesNothing(t *testing.T) { inv, c := anInventoryOnTheChain(t) ctx := t.Context() at := time.Date(2026, 10, 18, 4, 0, 0, 0, time.UTC) if err := inv.chainRecordBuild(ctx, c, Build{ID: "b-wg", Module: "mesh-wireguard", Commit: "c-wg"}); err != nil { t.Fatal(err) } put := func(module string, a chain.Assignment) { a.Node, a.Module = "laptop", module if _, err := c.Assignments.Create(ctx, assignmentKey("laptop", module), a); err != nil { t.Fatal(err) } } put("notes", chain.Assignment{Assigned: at}) // assigned, no commit yet put("web", chain.Assignment{Assigned: at, Commit: "c-web-new", Build: "b1"}) // already has one put("hand", chain.Assignment{Assigned: at, SentUnknown: at}) // a hand send since put("waits", chain.Assignment{Assigned: at, State: chain.StatePending}) // a wait for a build put("ended", chain.Assignment{Assigned: at, Ended: at}) // unassigned sent := map[string]string{"notes": "c-notes", "web": "c-web-old", "mesh-wireguard": "c-wg", "hand": "c-hand", "waits": "c-waits", "ended": "c-ended"} for pass := 0; pass < 2; pass++ { // and the second pass changes nothing if err := inv.copySentBuildsOf(ctx, c, "laptop", sent, at); err != nil { t.Fatal(err) } get := func(module string) chain.Assignment { t.Helper() k, found, err := c.Assignments.Get(ctx, assignmentKey("laptop", module)) if err != nil || !found { t.Fatalf("%s: %v %v", module, found, err) } return k.Value } if a := get("notes"); a.Commit != "c-notes" || a.Rollout != "" || !a.Stands() || a.PulledIn { t.Errorf("notes: %+v", a) } if a := get("web"); a.Commit != "c-web-new" || a.Build != "b1" { t.Errorf("a key with a commit was overwritten: %+v", a) } if a := get("hand"); a.Commit != "" || a.SentUnknown.IsZero() { t.Errorf("a hand send's unknown was overwritten: %+v", a) } if a := get("waits"); a.Commit != "" || a.State != chain.StatePending { t.Errorf("a wait was overwritten: %+v", a) } if a := get("mesh-wireguard"); a.Commit != "c-wg" || a.Build != "b-wg" || !a.PulledIn || !a.Stands() || a.Rollout != "" { t.Errorf("the pulled-in module has no standing key of its own: %+v", a) } if a := get("ended"); a.Commit != "" || a.Stands() { t.Errorf("an ended key was revived or given a commit: %+v", a) } } } // The copy reads the column of the store, writes the keys, and empties the column it has copied: SentBuilds then answers the // pulled-in module, and a second copy finds nothing to do. func TestTheColumnOfASentMachineIsCopiedOntoItsKeysAndEmptied(t *testing.T) { inv := ForTest(t) ctx := t.Context() if _, err := inv.AddNode(ctx, "laptop"); err != nil { t.Fatal(err) } inv.ExecForTest(t, `update node set sent_builds = '{"mesh-wireguard": "c-wg"}'::jsonb, sent_at = now() where name = 'laptop'`) if n, err := inv.CopySentBuilds(ctx); err != nil || n != 1 { t.Fatalf("the copy did %d nodes (%v)", n, err) } got, known, err := inv.SentBuilds(ctx, "laptop") if err != nil || !known || got["mesh-wireguard"] != "c-wg" { t.Fatalf("SentBuilds answers %v, known %v (%v)", got, known, err) } if rows := inv.QueryForTest(t, `select count(*) from node where sent_builds is not null`); rows[0][0] != "0" { t.Fatalf("the column was not emptied: %v", rows) } if n, err := inv.CopySentBuilds(ctx); err != nil || n != 0 { t.Fatalf("a second copy did %d nodes (%v)", n, err) } } // A machine delivery 2 has sent has its column emptied by the send itself, so a copy after it leaves a key the send cleared // without a commit. func TestASendEmptiesTheColumnSoALaterCopyLeavesAClearedKeyBare(t *testing.T) { inv := ForTest(t) ctx := t.Context() record, err := inv.AddNode(ctx, "laptop") if err != nil { t.Fatal(err) } inv.ExecForTest(t, `update node set sent_builds = '{"notes": "c-old"}'::jsonb where name = 'laptop'`) if err := inv.RecordSentUnder(ctx, record.ID, "d1", map[string]string{}, 1); err != nil { t.Fatal(err) } if rows := inv.QueryForTest(t, `select count(*) from node where sent_builds is not null`); rows[0][0] != "0" { t.Fatalf("a send left the column: %v", rows) } if n, err := inv.CopySentBuilds(ctx); err != nil || n != 0 { t.Fatalf("the copy did %d nodes (%v)", n, err) } }