A found ended or pending key is skipped and a pulled-in key is created only where no key exists. RecordSentWith and RecordSentUnder set sent_builds = null in their own update of the node row, so a machine delivery 2 has sent is never copied afterwards. These two updates and the copy's own null are the only writes to the column.
112 lines
4.8 KiB
Go
112 lines
4.8 KiB
Go
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 := c.Builds.Create(ctx, "b-wg", chain.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)
|
|
}
|
|
}
|