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
69 lines
2.8 KiB
Go
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)
|
|
}
|
|
}
|