package inventory import ( "context" "encoding/json" "errors" "testing" "time" "github.com/novox/mesh-controller/internal/chain" ) // **An assignment's key is `.`, as the bucket has always had it** (ADR 0408 §3): its readers parse nothing // but it. func TestAnAssignmentsKeyIsTheNodeAndTheModule(t *testing.T) { if got := assignmentKey("anchor", "notes"); got != "anchor.notes" { t.Fatalf("the key is %q", got) } if got := assignmentKey("g14.example", "dunst"); got != "g14.example.dunst" { t.Fatalf("the key of a machine whose name holds a dot is %q", got) } } // **A plan that is absent and a null one are one** (the provisioner's plan in an assignment's value): the pass that keeps // the plans writes only where they differ. func TestAnAbsentPlanAndANullOneAreTheSame(t *testing.T) { for name, tc := range map[string]struct { a, b json.RawMessage same bool }{ "both absent": {nil, nil, true}, "null and absent": {json.RawMessage(`null`), nil, true}, "the same plan": {json.RawMessage(`{"files":[]}`), json.RawMessage(` {"files":[]} `), true}, "another plan": {json.RawMessage(`{"files":[]}`), json.RawMessage(`{"files":[1]}`), false}, "a plan and none": {json.RawMessage(`{"files":[]}`), nil, false}, "none and a plan": {nil, json.RawMessage(`{"files":[]}`), false}, } { if got := sameJSON(tc.a, tc.b); got != tc.same { t.Errorf("%s: %v", name, got) } } } // **A pass that keeps the plans never brings back an assignment that ended** (issue 1093): it reads what it wants and what // the bucket holds in two reads, and an unassignment between them leaves a key that ended while the pass still wants it // assigned. func TestAPassThatKeepsThePlansNeverRevivesAnEndedAssignment(t *testing.T) { at := time.Date(2026, 10, 18, 3, 0, 0, 0, time.UTC) plan := json.RawMessage(`{"files":[]}`) assigned := chain.Assignment{Node: "a", Module: "m", Assigned: at, Provisioning: plan} ended := chain.Assignment{Node: "a", Module: "m", Assigned: at, Ended: at.Add(time.Minute)} pulledIn := chain.Assignment{Node: "a", Module: "p", RequiredBy: "m", Provisioning: plan} pulledInEnded := chain.Assignment{Node: "a", Module: "p", Ended: at} for name, tc := range map[string]struct { has bool cur, next chain.Assignment want string }{ "the interleaving: wanted assigned, ended since": {true, ended, assigned, keepSkip}, "wanted assigned, not in the bucket yet": {false, chain.Assignment{}, assigned, keepSkip}, "a pulled-in module, not in the bucket": {false, chain.Assignment{}, pulledIn, keepCreate}, "a pulled-in module that ended, pulled again": {true, pulledInEnded, pulledIn, keepFresh}, "an assignment ended, a plan for it": {true, ended, chain.Assignment{Node: "a", Module: "m", RequiredBy: "x", Provisioning: plan}, keepSkip}, "the plan is the same": {true, assigned, assigned, keepSkip}, "the plan changed": {true, chain.Assignment{Node: "a", Module: "m", Assigned: at}, assigned, keepField}, } { if got := keepStep(tc.has, tc.cur, tc.next); got != tc.want { t.Errorf("%s: %s, want %s", name, got, tc.want) } } } // **The store's mirror of the assignments is refused to its writer once the assignments are moved** (ADR 0408 §4, issue // 1093): asked at the moment of the write, so a tick that decided before the switch lays nothing after it. func TestTheStoresMirrorOfTheAssignmentsIsRefusedToItsWriterOnceTheyAreMoved(t *testing.T) { inv := &Inventory{moved: &movedKinds{kind: map[string]bool{KindAssignments: true}}} inv.UseChain(chain.InMemory(nil)) if _, err := inv.AssignmentsMirrorToWrite(context.Background()); !errors.Is(err, ErrAssignmentsMoved) { t.Fatalf("the writer was given the store's rows: %v", err) } }