mesh/delivery covered after its stop: a later walk that contains it was delivered: plan-1792875397682979170 (novox/mesh-sdk at 312878be into main, done, …
mesh/delivery-group group fix/1937-done-key-blobs closed: a member was closed before the group merged
mesh/check pass 34f210e5eba1 on e1f393bbb12e | unit 202 run, 3523 kept | integration 2223 run, 2682 kept, 7 skipped | over bound: unit, integration (s…
The store's nightly collector deletes every bare blob no manifest names, and a done key the controller never took in holds nothing: node-tools, mesh-host and the forge bridge kept done keys naming blobs that answer 404. held lets the builder ask before it reuses a key; artifacts-gone is said when a take-in finds a gone blob. delivery/held.go is the shared contract.
85 lines
2.9 KiB
Go
85 lines
2.9 KiB
Go
package delivery
|
|
|
|
import (
|
|
"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 controller seat's contract under the names the builder uses.
|
|
func TestHeldOnTheControllerSeat(t *testing.T) {
|
|
if HeldVerb != "seat:"+catalogue.ControllerSeatName+".held" {
|
|
t.Fatalf("HeldVerb %q", HeldVerb)
|
|
}
|
|
found := false
|
|
for _, v := range catalogue.ControllerVerbs {
|
|
if v.Name == "held" {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatal("the controller seat declares no verb held")
|
|
}
|
|
for _, s := range catalogue.DefaultSeats() {
|
|
if s.Name != catalogue.ControllerSeatName {
|
|
continue
|
|
}
|
|
for _, e := range s.Emits {
|
|
if e == ArtifactsGoneEvent {
|
|
return
|
|
}
|
|
}
|
|
t.Fatalf("the controller seat emits %v, not %s", s.Emits, ArtifactsGoneEvent)
|
|
}
|
|
t.Fatal("no controller seat")
|
|
}
|