Tests of the build module follow the composition's move: mesh-build-image tests its own files, and sdkjudge lists what mesh-sdk still holds (ADR 0593 §7)

mesh-build-image's two tests that composed with catalogue.Resolve (gone with the composition) now hold the module's own
facts: its registry file is the placement and no file binds the store; it requires rootless-image-build, which buildah
provides and docker does not. What only proved composition is listed in .work/sdk1/proofs-for-node-engine.md. sdkjudge's
composition list drops the six refusals that left mesh-sdk.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
2026-11-02 19:23:26 +01:00
parent 308d052ff6
commit 0697b2994e
3 changed files with 39 additions and 88 deletions
@@ -5,69 +5,37 @@ import (
"path/filepath"
"strings"
"testing"
"git.novox.be/novox/mesh-sdk/go/catalogue"
)
// Issue 1703 (the live failure of the first assignment): the module is moved, so the provisioner's client on the node
// fills its templates, and it fills only config, secrets, collections, placement and what a moved provider told it.
// The registry's address therefore cannot be a ${bound:artifact-store:...} (the artifact store has not moved): it is the
// placement ${seat:mesh-artifact-store:reach}, the form the container runtime's daemon.json uses. This composes the
// module on a machine with buildah and the artifact store reachable, handing its files to the client as a node does.
func TestTheModuleComposesWhereItsFilesAreHandedToTheProvisionersClient(t *testing.T) {
built := func(m catalogue.Manifest) []catalogue.Built {
texts := map[string]string{}
entries, err := os.ReadDir(filepath.Join(moduleDir, "files"))
// fills its templates, and it fills only config, secrets, collections, placement and what a moved provider told it. The
// registry's address therefore cannot be a ${bound:artifact-store:...} (the artifact store has not moved): it is the
// placement ${seat:mesh-artifact-store:reach}, the form the container runtime's daemon.json uses. The module's own part
// of that is what this holds: its registry file is that placement and none of its files or its manifest binds the store.
// That a node composes it is the node-engine's composition test (.work/sdk1/proofs-for-node-engine.md).
func TestTheRegistryIsThePlacementAndNoFileBindsTheStore(t *testing.T) {
raw, err := os.ReadFile(filepath.Join(moduleDir, "files", "registry"))
if err != nil {
t.Fatal(err)
}
if got := strings.TrimSpace(string(raw)); got != "${seat:mesh-artifact-store:reach}" {
t.Errorf("the registry file is %q, want the placement ${seat:mesh-artifact-store:reach}", got)
}
files := []string{filepath.Join(moduleDir, "module.json")}
entries, err := os.ReadDir(filepath.Join(moduleDir, "files"))
if err != nil {
t.Fatal(err)
}
for _, e := range entries {
files = append(files, filepath.Join(moduleDir, "files", e.Name()))
}
for _, f := range files {
body, err := os.ReadFile(f)
if err != nil {
t.Fatal(err)
}
for _, e := range entries {
raw, err := os.ReadFile(filepath.Join(moduleDir, "files", e.Name()))
if err != nil {
t.Fatal(err)
}
texts[e.Name()] = string(raw)
if strings.Contains(string(body), "${bound:artifact-store") {
t.Errorf("%s binds the artifact store, which has not moved: a moved module's client cannot fill it", f)
}
return []catalogue.Built{
{Name: "tools", Kind: "bundle", Reference: catalogue.ArtifactStoreScheme + "mesh-build-image/tools/blobs/sha256:" + strings.Repeat("a", 64), Digest: "sha256:" + strings.Repeat("a", 64)},
{Name: "files", Kind: "archive", Reference: catalogue.ArtifactStoreScheme + "mesh-build-image/files/blobs/sha256:" + strings.Repeat("b", 64), Digest: "sha256:" + strings.Repeat("b", 64), Texts: texts},
}
}
read := func(name string) catalogue.Manifest {
return decodeTree(t, filepath.Join(catalogueRoot, "modules", name))
}
image := read("mesh-build-image")
resolved, err := image.Resolve(built(image))
if err != nil {
t.Fatal(err)
}
shelf := map[string]catalogue.Manifest{
"mesh-build-image": resolved,
"buildah": read("buildah"),
"forge": {Module: "forge", Version: "1", Provides: []catalogue.Offer{{Name: "forge-account"}}},
"store": {Module: "store", Version: "1", Provides: []catalogue.Offer{{Name: "artifact-store"}}},
"runner": {Module: "runner", Version: "1", Provides: []catalogue.Offer{{Name: "node-tools"}}}, // node-tools, which the module pulls its asks through (1911)
}
node := catalogue.Node{Name: "builder", Site: "house",
Capabilities: map[string]bool{"package-manager": true, "user-namespaces": true}}
r, err := catalogue.Resolve(shelf, []string{"forge", "store", "runner", "buildah", "mesh-build-image"}, node, catalogue.World{})
if err != nil {
t.Fatal(err)
}
r.Account = "op"
// What the resolution states for the node: the forge's provider has moved, the artifact store's has not.
r.Needs = []catalogue.Needed{
{Name: "forge-account", For: "mesh-build-image", Module: "forge", From: "novox", At: "10.7.0.1", ProviderMoved: true,
Serves: map[string]any{"known_host": "[novox.internal]:222 ssh-ed25519 AAAA", "host_key": "ssh-ed25519 AAAA", "ssh_host": "novox.internal", "ssh_alias": "mesh-forge", "ssh_port": "222", "ssh_base": "ssh://git@novox.internal:222/", "https_base": "https://git.novox.be/"}},
{Name: "artifact-store", For: "mesh-build-image", Module: "store", From: "novox", At: "10.7.0.1", Serves: map[string]any{"port": "5000"}},
}
with := catalogue.Rendering{Hands: true, Provisioner: "mesh-provisioner-client", ArtifactStore: "store:5000",
Needed: map[string]map[string]string{"mesh-build-image": {"broker": "sealed-to-the-node"}}}
c, err := r.Compose(with)
if err != nil {
t.Fatalf("the module does not compose on a machine with buildah and the artifact store: %v", err)
}
if _, asked := c.Plans["mesh-build-image"].Placement["seat:mesh-artifact-store:reach"]; !asked {
t.Errorf("the plan's placement %v does not ask where the registry is reached", c.Plans["mesh-build-image"].Placement)
}
}
@@ -183,33 +183,22 @@ func TestGiteaReadsTheImageBuildModulesKeys(t *testing.T) {
}
}
// 1704b: over the real manifests, mesh-build-image where no module provides rootless-image-build is refused, the error
// naming the provision, the module that wants it and the module to assign (buildah); with buildah it composes. The
// forge and the registry, which other modules provide for the whole mesh, are stood in for.
func TestACompositionWithoutBuildahIsRefusedNamingItAndWithBuildahComposes(t *testing.T) {
shelf := map[string]catalogue.Manifest{
"forge": {Module: "forge", Version: "1", Provides: []catalogue.Offer{{Name: "forge-account"}}},
"store": {Module: "store", Version: "1", Provides: []catalogue.Offer{{Name: "artifact-store"}}},
// node-tools, the tool runner the module pulls its asks through (1911), stood in for as the others are.
"runner": {Module: "runner", Version: "1", Provides: []catalogue.Offer{{Name: "node-tools"}}},
// 1704b: the module requires rootless-image-build, which buildah provides and docker does not. That a composition without
// buildah is refused naming both is the node-engine's composition test (.work/sdk1/proofs-for-node-engine.md).
func TestTheModuleRequiresRootlessImageBuildAndBuildahProvidesIt(t *testing.T) {
if m := readManifest(t); !slices.Contains(m.Requires, "rootless-image-build") {
t.Errorf("requires %v lack rootless-image-build", m.Requires)
}
for _, name := range []string{"mesh-build-image", "buildah", "docker"} {
m := decodeTree(t, filepath.Join(catalogueRoot, "modules", name))
shelf[m.Module] = m
}
node := catalogue.Node{Name: "builder", Site: "house",
Capabilities: map[string]bool{"package-manager": true, "user-namespaces": true}}
_, err := catalogue.Resolve(shelf, []string{"forge", "store", "runner", "docker", "mesh-build-image"}, node, catalogue.World{})
if err == nil {
t.Fatal("mesh-build-image composed on a machine without buildah (docker does not stand in for it)")
}
for _, want := range []string{`"rootless-image-build" is wanted by mesh-build-image`, "buildah"} {
if !strings.Contains(err.Error(), want) {
t.Errorf("the refusal does not say %q:\n%v", want, err)
provides := func(name string) bool {
for _, o := range decodeTree(t, filepath.Join(catalogueRoot, "modules", name)).Provides {
if o.Name == "rootless-image-build" {
return true
}
}
return false
}
if _, err := catalogue.Resolve(shelf, []string{"forge", "store", "runner", "buildah", "mesh-build-image"}, node, catalogue.World{}); err != nil {
t.Errorf("with buildah assigned the composition is refused: %v", err)
if !provides("buildah") || provides("docker") {
t.Errorf("buildah provides it: %v, docker provides it: %v; want true and false", provides("buildah"), provides("docker"))
}
}