Files
mesh-delivery/modules/mesh-delivery-builder/internal/build/imports_test.go
T
jschoubben 71a35a6376 MV1: move contracts and sdk under modules/, importers follow
The delivery repository's contracts and SDK live under modules/ as
mesh-delivery-contracts and mesh-delivery-sdk, their Go module paths
renamed to match the folders (the TypeScript names already did), and
every importer in mesh-delivery, mesh-catalog, mesh-host, mesh-tools
and mesh-sdk follows in the same group: a design as it should be, one
path per module, checked by the group's check building every importer.

Builds: design 47 G §427, §88

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 05:09:56 +01:00

113 lines
4.5 KiB
Go

package build
import (
"encoding/json"
"strings"
"sync"
"testing"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-sdk/buildstep"
)
// ADR 0593 §2 (F3): the imports a Go build module answers at a build's end reach the done key and the log's end entry,
// where the indexer keeps them on the module.
func importsABuildAnswersReachTheDoneKeyAndTheLog(t *testing.T) {
r := newRig(t, goBundle)
imports := []string{"git.novox.be/novox/mesh-sdk/go", "git.novox.be/novox/mesh-sdk/go/announce"}
r.asks.Answer = func(a buildstep.StepAsk) buildstep.Step {
step := r.answer(a)
var answer map[string]any
if err := json.Unmarshal(step.Result, &answer); err == nil && a.Language == "go" {
answer["imports"] = imports
step.Result, _ = json.Marshal(answer)
}
return step
}
var logged []builds.BuildLogEntry
var mu sync.Mutex
r.b.Node, r.b.Log = "novox", func(e builds.BuildLogEntry) { mu.Lock(); logged = append(logged, e); mu.Unlock() }
v := r.consider(t, "modules/m")
if v.State != builds.BuildDone || strings.Join(v.Imports, ",") != strings.Join(imports, ",") {
t.Fatalf("done key %+v, want the imports", v)
}
if len(logged) != 2 || strings.Join(logged[1].Imports, ",") != strings.Join(imports, ",") || len(logged[0].Imports) != 0 {
t.Fatalf("log %+v, want the imports on the end alone", logged)
}
}
// Design 26 (the lead's ruling on A2i's finding 2): the out-of-step files a contracts module's build module answers, in a
// failed step's result too, reach the log's end entry, sorted and each once, where the indexer raises its finding; a
// build that answers none names none.
func outOfStepFilesABuildAnswersReachTheLog(t *testing.T) {
for _, row := range []struct {
name string
fail bool
files []string
want string
}{
{"a passed build naming files", false, []string{"go/b.go", "go/a.go", "go/b.go"}, "go/a.go,go/b.go"},
{"a failed build naming files", true, []string{"ts/x.ts"}, "ts/x.ts"},
{"a build naming none", false, nil, ""},
} {
t.Run(row.name, func(t *testing.T) {
r := newRig(t, goBundle)
r.asks.Answer = func(a buildstep.StepAsk) buildstep.Step {
step := r.answer(a)
answer := map[string]any{}
_ = json.Unmarshal(step.Result, &answer)
if row.files != nil {
answer["out-of-step"] = row.files
}
step.Result, _ = json.Marshal(answer)
if row.fail {
step.State, step.Reason = buildstep.StepFailed, "the generated files are out of step"
}
return step
}
var logged []builds.BuildLogEntry
var mu sync.Mutex
r.b.Node, r.b.Log = "novox", func(e builds.BuildLogEntry) { mu.Lock(); logged = append(logged, e); mu.Unlock() }
r.consider(t, "modules/m")
mu.Lock()
defer mu.Unlock()
if len(logged) != 2 || strings.Join(logged[1].OutOfStep, ",") != row.want || len(logged[0].OutOfStep) != 0 {
t.Fatalf("log %+v, want the end naming %q", logged, row.want)
}
// A failed build's end carries its reason, so the log never hides why it failed; a passed one no error.
if end := logged[1]; row.fail != strings.Contains(end.Error, "out of step") || !row.fail && end.Error != "" ||
logged[0].Error != "" || logged[0].Step != "" {
t.Errorf("log %+v, want a failed build's end alone to carry its error", logged)
}
})
}
}
// Design 47 G §671: a build the build node kept instead of storing it, which answers its digest and no reference, is done,
// and its end in the log names the node that keeps it once; a build not kept names none.
func aKeptBuildNamesItsNodeInItsEnd(t *testing.T) {
r := newRig(t, goBundle)
r.asks.Answer = func(a buildstep.StepAsk) buildstep.Step {
step := r.answer(a)
var answer buildstep.BuildAnswer
if err := json.Unmarshal(step.Result, &answer); err == nil && a.Language == "go" && len(answer.Artifacts) > 0 {
for i := range answer.Artifacts {
answer.Artifacts[i].Reference = ""
}
answer.Kept = strings.TrimPrefix(answer.Artifacts[0].Digest, "sha256:")
step.Result, _ = json.Marshal(answer)
}
return step
}
var logged []builds.BuildLogEntry
var mu sync.Mutex
r.b.Node, r.b.Log = "novox", func(e builds.BuildLogEntry) { mu.Lock(); logged = append(logged, e); mu.Unlock() }
v := r.consider(t, "modules/m")
if v.State != builds.BuildDone || v.KeptOn == "" {
t.Fatalf("done key %+v, want it done and kept on its node", v)
}
if len(logged) != 2 || logged[1].KeptOn != v.KeptOn || logged[0].KeptOn != "" {
t.Fatalf("log %+v, want kept-on %q on the end alone", logged, v.KeptOn)
}
}