Files
mesh-sdk/go/delivery/held_test.go
T
jschoubben 8295207077 Remove the controller's seat, the lease verbs and placement's adopt and hand-back; refuse any manifest naming the controller
The controller ended by its unassignment (ADR 0590), so its seat, the grants seat's lease write and
hand-back and placement's take-over from it serve nothing. The dependence check becomes strict
(clause 5): a read of the lease, a claim, a require or the module itself is refused like any other
declaration. held and artifacts-gone are the artifact sweep seat's (ADR 0571 §2). Issue 1126 B1, B2.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-02 11:20:28 +01:00

69 lines
2.8 KiB
Go

package delivery
import (
"slices"
"strings"
"testing"
"time"
"git.novox.be/novox/mesh-sdk/go/catalogue"
)
const (
refA = "artifact-store://node-tools/runtime/blobs/sha256:a46b5d51a30e8fe71107d75ceb46e3187022fe51f551d0498b8be817f22435cb"
refB = "artifact-store://mesh-host/host-arch/blobs/sha256:8a752f914f3523904a5442cae37ce3cb1ad297d6789b0f871585bce4d0c2ea00"
)
// 1937/AC1 (the contract): the controller's answer reads into held and gone, and an answer leaving a reference out is refused.
func TestDecodeHeldAnswer(t *testing.T) {
raw := []byte(`{"output":"…","ok":true,"answer":{"store":"s:5100","held":["` + refA + `"],"gone":["` + refB + `"]}}`)
a, err := DecodeHeldAnswer(raw, []string{refA, refB})
if err != nil {
t.Fatal(err)
}
if len(a.Held) != 1 || a.Held[0] != refA || len(a.Gone) != 1 || a.Gone[0] != refB {
t.Fatalf("read %+v", a)
}
if _, err := DecodeHeldAnswer(raw, []string{refA, refB, "artifact-store://x/y/blobs/sha256:00"}); err == nil ||
!strings.Contains(err.Error(), "names nothing of") {
t.Fatalf("an answer leaving a reference out was taken: %v", err)
}
if _, err := DecodeHeldAnswer([]byte(`{"output":"the store is down","ok":false}`), nil); err == nil ||
!strings.Contains(err.Error(), "the store is down") {
t.Fatalf("a failed verb was taken: %v", err)
}
}
// 1937/AC2 (the contract): the event's body names the module, the fingerprint and the gone references, sorted.
func TestArtifactsGoneRoundTrip(t *testing.T) {
raw, err := EncodeArtifactsGone(ArtifactsGone{Module: "node-tools", Key: "node-tools.75", Fingerprint: "src2:75",
Gone: []string{refB, refA}, At: time.Unix(0, 0).UTC()})
if err != nil {
t.Fatal(err)
}
e, err := DecodeArtifactsGone(raw)
if err != nil {
t.Fatal(err)
}
if e.Event != ArtifactsGoneEvent || e.Module != "node-tools" || len(e.Gone) != 2 || e.Gone[0] != refB {
t.Fatalf("read %+v", e)
}
if _, err := EncodeArtifactsGone(ArtifactsGone{Module: "m", Fingerprint: "src2:1"}); err == nil {
t.Fatal("an event naming no gone reference was made")
}
if _, err := DecodeArtifactsGone([]byte(`{"event":"artifacts-gone","module":"m"}`)); err == nil {
t.Fatal("an event naming no fingerprint was read")
}
}
// The verb and the event are on the artifact sweep seat's contract under the names the builder uses (ADR 0571 §2).
func TestHeldOnTheArtifactSweepSeat(t *testing.T) {
seat, ok := catalogue.SeatNamed(catalogue.ArtifactSweepSeat)
if !ok || HeldVerb != "seat:"+seat.Name+".held" || ArtifactsGoneSubject != "mesh.seat."+seat.Name+".event."+ArtifactsGoneEvent {
t.Fatalf("HeldVerb %q, ArtifactsGoneSubject %q", HeldVerb, ArtifactsGoneSubject)
}
if !slices.ContainsFunc(seat.Serves, func(v catalogue.Verb) bool { return v.Name == "held" }) || !slices.Contains(seat.Emits, ArtifactsGoneEvent) {
t.Fatalf("the sweep seat serves %v and emits %v", seat.Serves, seat.Emits)
}
}