mesh/delivery covered after its failure: a later walk that contains it was delivered: plan-1792794944815912745 (novox/mesh-sdk at e1f393bb into main, don…
mesh/delivery-group group fix/1930-digest-shape merged: every member is merged
mesh/check pass 403a5d72ab93 on 1ff6d339780c | unit 4545 run, 2097 kept, 13 skipped | integration 4595 run, 2188 kept, 21 skipped | over bound: unit, …
A check ask's answer kept the digest's packages as a map keyed by Go import path;
the node runtime refuses a state value with a field ending in a credential word,
so the end of any check of mesh-controller (internal/token) was never written and
the ask was acked anyway. The level's digest is now delivery.LevelDigest, packages
a sorted list of {package, digest}, reading the old map form too. finish logs an
end refused every time, writes a small failed end naming the write's error, and
the puller gives the ask back instead of acking it when no end could be written.
181 lines
7.0 KiB
Go
181 lines
7.0 KiB
Go
package delivery
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
const askCommit = "0123456789abcdef0123456789abcdef01234567"
|
|
|
|
func buildAsk() StepAsk {
|
|
return StepAsk{ID: "mh3k2x9qa1b2c3", Kind: AskDelivery, Language: AskGo, Asker: "mesh-delivery-builder",
|
|
At: time.Date(2026, 10, 23, 13, 0, 0, 0, time.UTC),
|
|
Build: &BuildJob{Module: "mesh-delivery-view", Path: "modules/mesh-delivery-view", FP: "ab12",
|
|
Fetch: map[string]any{"repository": "novox/mesh-delivery", "commit": askCommit, "dir": "modules/mesh-delivery-view", "paths": []any{"cmd"}},
|
|
Artifacts: []AskArtifact{{Artifact: "bundle"}, {Artifact: "base", From: "registry/x@sha256:00"}},
|
|
Publish: true, Toolchain: &ToolchainUsed{Version: "go1.26.0", Recipe: "3"}}}
|
|
}
|
|
|
|
func checkAsk() StepAsk {
|
|
return StepAsk{ID: "mh3k2x9qa1b2c4", Kind: AskCheck, Language: AskNode, Asker: "mesh-delivery-checker",
|
|
At: time.Date(2026, 10, 23, 13, 0, 0, 0, time.UTC),
|
|
Check: &CheckJob{Fetch: map[string]any{"repository": "novox/mesh-catalog", "commit": askCommit, "ref": "refs/pull/3/head", "base": askCommit},
|
|
Levels: []string{LevelUnit, LevelIntegration}, Kept: true, Runner: "workspace", RunnerWhen: "modules/mesh-build-go/", Run: ""}}
|
|
}
|
|
|
|
// 1887/A1a: an ask encodes and decodes with every field.
|
|
func TestAskRoundTrips(t *testing.T) {
|
|
for _, a := range []StepAsk{buildAsk(), checkAsk()} {
|
|
raw, err := EncodeStepAsk(a)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := DecodeStepAsk(raw)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
a.V = StepAskVersion
|
|
if !reflect.DeepEqual(got, a) {
|
|
t.Fatalf("round trip:\n got %+v\nwant %+v", got, a)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 1887/A1a: an ask with an unknown kind or language is refused.
|
|
func TestAskRefusesUnknownKindAndLanguage(t *testing.T) {
|
|
cases := map[string]func(*StepAsk){
|
|
"kind": func(a *StepAsk) { a.Kind = "build" },
|
|
"language": func(a *StepAsk) { a.Language = "typescript" },
|
|
"id": func(a *StepAsk) { a.ID = "postgres" },
|
|
"neither": func(a *StepAsk) { a.Build = nil },
|
|
"both": func(a *StepAsk) { a.Check = &CheckJob{Levels: []string{LevelUnit}, Fetch: a.Build.Fetch} },
|
|
"recheck": func(a *StepAsk) { a.Kind = AskRecheck },
|
|
"commit": func(a *StepAsk) { a.Build.Fetch = map[string]any{"repository": "novox/x", "commit": "main"} },
|
|
"path": func(a *StepAsk) { a.Build.Path = "" },
|
|
}
|
|
for name, change := range cases {
|
|
a := buildAsk()
|
|
change(&a)
|
|
if _, err := EncodeStepAsk(a); err == nil {
|
|
t.Errorf("%s: encoded", name)
|
|
}
|
|
}
|
|
// A copy alone fetches nothing and needs no path.
|
|
a := buildAsk()
|
|
a.Build.Fetch, a.Build.Path, a.Build.Artifacts = nil, "", []AskArtifact{{Artifact: "base", From: "registry/x@sha256:00"}}
|
|
if _, err := EncodeStepAsk(a); err != nil {
|
|
t.Errorf("a copy alone: %v", err)
|
|
}
|
|
c := checkAsk()
|
|
c.Kind = AskDelivery
|
|
if _, err := EncodeStepAsk(c); err == nil {
|
|
t.Error("a check on the delivery stream encoded")
|
|
}
|
|
c = checkAsk()
|
|
c.Language = AskImage
|
|
if _, err := EncodeStepAsk(c); err == nil {
|
|
t.Error("a check of an image encoded")
|
|
}
|
|
raw, _ := EncodeStepAsk(buildAsk())
|
|
for _, bad := range []string{
|
|
strings.Replace(string(raw), `"kind":"delivery"`, `"kind":"queue"`, 1),
|
|
strings.Replace(string(raw), `"language":"go"`, `"language":"rust"`, 1),
|
|
} {
|
|
if _, err := DecodeStepAsk([]byte(bad)); err == nil {
|
|
t.Errorf("decoded %s", bad)
|
|
}
|
|
}
|
|
if _, err := DecodeStepAsk([]byte(strings.Replace(string(raw), `"v":1`, `"v":2`, 1))); !errors.Is(err, ErrUnknownVersion) {
|
|
t.Errorf("version 2: %v", err)
|
|
}
|
|
}
|
|
|
|
// 1887/A1a: the subjects, stream and workers are the seat's, as the broker derives them.
|
|
func TestAskSubjects(t *testing.T) {
|
|
s, err := AskSubject(AskGo, AskCheck)
|
|
if err != nil || s != "mesh.seat.node-build-go.accept.check" {
|
|
t.Fatalf("AskSubject = %q, %v", s, err)
|
|
}
|
|
language, kind, err := ParseAskSubject("mesh.seat.node-build-image.accept.delivery")
|
|
if err != nil || language != AskImage || kind != AskDelivery {
|
|
t.Fatalf("ParseAskSubject = %q %q %v", language, kind, err)
|
|
}
|
|
for _, bad := range []string{"mesh.seat.node-build-agent.accept.build", "mesh.seat.node-build-go.accept.queue", "mesh.seat.node-build-go.event.x"} {
|
|
if _, _, err := ParseAskSubject(bad); err == nil {
|
|
t.Errorf("parsed %s", bad)
|
|
}
|
|
}
|
|
if st, _ := AskStream(AskNode); st != "SEAT_NODE_BUILD_NODE" {
|
|
t.Errorf("AskStream = %q", st)
|
|
}
|
|
if w, _ := AskWorker(AskGo, AskRecheck); w != "SEAT_NODE_BUILD_GO_RECHECK_worker" {
|
|
t.Errorf("AskWorker = %q", w)
|
|
}
|
|
if seat, _ := BuildSeat(AskImage); seat != "node-build-image" {
|
|
t.Errorf("BuildSeat = %q", seat)
|
|
}
|
|
if m, _ := BuildModuleOf(AskNode); m != "mesh-build-node" {
|
|
t.Errorf("BuildModuleOf = %q", m)
|
|
}
|
|
if _, err := BuildSeat("agent"); err == nil {
|
|
t.Error("BuildSeat(agent)")
|
|
}
|
|
}
|
|
|
|
// 1887/A4b: a building key names the asks put for it; a done key or a name that is no ask id is refused.
|
|
func TestABuildingKeyNamesItsAsks(t *testing.T) {
|
|
b := BuilderBuild{State: BuildBuilding, By: "novox.builder", Asks: map[string]string{AskGo: "mh3k2x9qa1b2c3"}}
|
|
raw, err := EncodeBuilderBuild(b)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := DecodeBuilderBuild(raw)
|
|
if err != nil || len(got.Asks) != 1 || got.Asks[AskGo] != "mh3k2x9qa1b2c3" {
|
|
t.Fatalf("decoded %+v, %v", got, err)
|
|
}
|
|
for _, bad := range []BuilderBuild{
|
|
{State: BuildBuilding, Asks: map[string]string{AskGo: "postgres"}},
|
|
{State: BuildBuilding, Asks: map[string]string{"typescript": "mh3k2x9qa1b2c3"}},
|
|
{State: BuildFailed, Error: "x", Asks: map[string]string{AskGo: "mh3k2x9qa1b2c3"}},
|
|
} {
|
|
if _, err := EncodeBuilderBuild(bad); err == nil {
|
|
t.Errorf("encoded %+v", bad)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 1930/AC1: a level answer stored before issue 1930, its digest's packages a map keyed by package path, and the digest
|
|
// tool's own answer, read as the list sorted by package; the list form reads back the same and keys no field by a path.
|
|
func TestALevelDigestReadsTheMapAndTheList(t *testing.T) {
|
|
var old LevelAnswer
|
|
if err := json.Unmarshal([]byte(`{"level":"unit","digest":{"level":"unit","packages":{"x/pkg/a":"d-a","x/internal/token":"d-t"},"beside":["novox/y abc"],"judged_by":"installed"}}`), &old); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
want := []PackageDigest{{Package: "x/internal/token", Digest: "d-t"}, {Package: "x/pkg/a", Digest: "d-a"}}
|
|
if old.Digest == nil || !reflect.DeepEqual(old.Digest.Packages, want) || old.Digest.JudgedBy != "installed" || len(old.Digest.Beside) != 1 {
|
|
t.Fatalf("the old form read %+v", old.Digest)
|
|
}
|
|
raw, err := json.Marshal(old)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(string(raw), `"x/internal/token":`) {
|
|
t.Errorf("the list form keys a field by a package path: %s", raw)
|
|
}
|
|
var back LevelAnswer
|
|
if err := json.Unmarshal(raw, &back); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !reflect.DeepEqual(back.Digest, old.Digest) || back.Digest.Map()["x/internal/token"] != "d-t" {
|
|
t.Errorf("the list form read back %+v, want %+v", back.Digest, old.Digest)
|
|
}
|
|
var none LevelAnswer
|
|
if err := json.Unmarshal([]byte(`{"level":"unit"}`), &none); err != nil || none.Digest != nil || none.Digest.Map() != nil {
|
|
t.Errorf("a level without a digest read %+v, %v", none.Digest, err)
|
|
}
|
|
}
|