package chain import ( "encoding/json" "strings" "testing" "time" ) // **A pending value is not a standing assignment** (ADR 0261, ADR 0408, issue 1094): it names its node and module, holds no // time assigned, says `state: pending`, and every reader that skips a value with a state skips it. func TestAPendingValueIsNotAStandingAssignment(t *testing.T) { since := time.Date(2026, 10, 18, 3, 0, 0, 0, time.UTC) a := Assignment{Node: "anchor", Module: "notes", State: StatePending, Pending: &Pending{ID: "p-1", State: PendingWaiting, Build: "build-1", Since: since}} if a.Stands() { t.Fatal("a pending value stands") } raw, _ := json.Marshal(a) for _, want := range []string{`"node":"anchor"`, `"module":"notes"`, `"state":"pending"`, `"pending":{"id":"p-1","state":"waiting"`} { if !strings.Contains(string(raw), want) { t.Errorf("the value lacks %s: %s", want, raw) } } if strings.Contains(string(raw), `"assigned"`) { t.Errorf("a pending value names a time assigned: %s", raw) } // The wait carried into the value that stands: no state, so it stands, and the record stays. made := Assignment{Node: "anchor", Module: "notes", Assigned: since, Pending: &Pending{ID: "p-1", State: PendingApplied}} if !made.Stands() || !a.Pending.Open() || made.Pending.Open() { t.Fatalf("stands %v, open before %v, open after %v", made.Stands(), a.Pending.Open(), made.Pending.Open()) } var back Assignment if err := json.Unmarshal(raw, &back); err != nil || back.Pending == nil || back.Pending.Build != "build-1" || !back.Pending.Since.Equal(since) { t.Fatalf("read back %+v: %v", back, err) } now := since raised := Pending{ID: "p-2", State: PendingExpired, Raised: &now, Acknowledged: &now, Cleared: &now} body, _ := json.Marshal(raised) if !strings.Contains(string(body), `"raised"`) || !strings.Contains(string(body), `"acknowledged"`) || !strings.Contains(string(body), `"cleared"`) { t.Errorf("the marks of a wait: %s", body) } } func TestTheTransitionsOfAPendingAssignment(t *testing.T) { ok := [][2]string{{PendingWaiting, PendingExpired}, {PendingWaiting, PendingWithdrawn}, {PendingApplying, PendingApplied}, {PendingApplying, PendingRefused}} for _, p := range ok { if !CanSettle(p[0], p[1]) { t.Errorf("%s may end as %s", p[0], p[1]) } } for _, p := range [][2]string{{PendingWaiting, PendingApplied}, {PendingWaiting, PendingRefused}, {PendingApplying, PendingExpired}, {PendingApplying, PendingWithdrawn}, {PendingApplied, PendingExpired}, {PendingExpired, PendingWithdrawn}, {PendingWaiting, PendingWaiting}} { if CanSettle(p[0], p[1]) { t.Errorf("%s may not end as %s", p[0], p[1]) } } if !CanMove(PendingWaiting, PendingApplying) || !CanMove(PendingApplying, PendingWaiting) || CanMove(PendingApplied, PendingWaiting) { t.Error("a claim moves a wait to applying and back") } } // **A module only pulled in is a standing value marked pulled-in, never a state, and a hand send makes a key say its commit // unknown with a time** (issue 1109): every reader treats a non-empty state as no assignment, so a pulled-in key must have // none; and unknown is never written in `commit`. func TestAPulledInValueStandsAndAHandSendIsATimeNotACommit(t *testing.T) { at := time.Date(2026, 10, 18, 3, 0, 0, 0, time.UTC) pulled := Assignment{Node: "anchor", Module: "mesh-wireguard", PulledIn: true, Commit: "c1", Build: "b1", Since: at} if !pulled.Stands() || pulled.State != "" { t.Fatalf("a pulled-in value does not stand: %+v", pulled) } raw, _ := json.Marshal(pulled) if !strings.Contains(string(raw), `"pulled-in":true`) || strings.Contains(string(raw), `"state"`) || strings.Contains(string(raw), `"assigned"`) { t.Fatalf("the pulled-in value is %s", raw) } unknown := Assignment{Node: "anchor", Module: "notes", Assigned: at, SentUnknown: at} raw, _ = json.Marshal(unknown) if !strings.Contains(string(raw), `"sent-unknown":"2026-10-18T03:00:00Z"`) || strings.Contains(string(raw), `"commit"`) || !unknown.Stands() { t.Fatalf("the hand-sent value is %s", raw) } plain, _ := json.Marshal(Assignment{Node: "anchor", Module: "notes", Assigned: at}) if strings.Contains(string(plain), "pulled-in") || strings.Contains(string(plain), "sent-unknown") { t.Fatalf("a value that is neither says so: %s", plain) } }