Files
mesh-sdk/go/catalogue/toolchain_manifests_test.go
T
jschoubben a2c813a6b4
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 feat/r4g merged: every member is merged
mesh/check checking a2c813a6b468 on 07462c4caa12 | novox/mesh-sdk#73 in group feat/r4g
Stand in node-tools beside the toolchain modules in the composition test, as the catalogue's build modules require it since issue 1911, so the test judges the toolchain alone (issue 1887, group check of #73)
2026-10-23 17:03:21 +02:00

91 lines
3.3 KiB
Go

package catalogue
import (
"strings"
"testing"
)
// Defends novox/hq ADR 0477 §4 (issue 1573): the go and nodejs modules own the toolchain packages and provide
// them with the machine's reach, so a node without one refuses the module that needs it at resolution, naming
// the provision, and does not find out at its first build. The manifests are the catalogue's, as they are.
// builderNode is a machine that can run a build module: the package manager and user namespaces it asks of
// the machine, and the forge account (mesh-scope, provided by the forge) and the artifact store the build
// modules publish to (ADR 0513, provided by distribution) stood in for, and node-tools, the tool runner they pull their
// asks through (mesh-catalog issue 1911; mesh-tools' module, not in the catalogue).
func builderNode() Node {
return Node{Name: "builder", Site: "house",
Capabilities: map[string]bool{"package-manager": true, "user-namespaces": true}}
}
func toolchainShelf(t *testing.T) map[string]Manifest {
t.Helper()
m := map[string]Manifest{
"forge": {Module: "forge", Version: "1", Provides: []Offer{{Name: "forge-account"}, {Name: "npm-package-registry"}, {Name: "artifact-store"}, {Name: "node-tools"}}},
}
for _, name := range []string{"go", "nodejs", "mesh-build-go", "mesh-build-node", "dev-tools"} {
m[name] = catalogueManifest(t, name)
}
return m
}
func TestAModuleNeedingAToolchainIsRefusedOnANodeWithoutIt(t *testing.T) {
for _, c := range []struct{ module, provision string }{
{"mesh-build-go", "go-toolchain"},
{"mesh-build-node", "node-toolchain"},
{"dev-tools", "go-toolchain"},
} {
_, err := Resolve(toolchainShelf(t), []string{"forge", c.module}, builderNode(), World{})
if err == nil {
t.Fatalf("%s composed on a node with no toolchain module", c.module)
}
for _, want := range []string{`"` + c.provision + `" is wanted by ` + c.module,
"usable only on the machine that provides it"} {
if !strings.Contains(err.Error(), want) {
t.Errorf("%s: the refusal does not say %q:\n%v", c.module, want, err)
}
}
}
}
func TestAModuleNeedingAToolchainComposesOnANodeWithIt(t *testing.T) {
for _, c := range []struct{ module, owner string }{
{"mesh-build-go", "go"},
{"mesh-build-node", "nodejs"},
{"dev-tools", "go"},
} {
assigned := []string{"forge", c.owner, c.module}
if c.module == "dev-tools" {
assigned = append(assigned, "nodejs")
}
got, err := Resolve(toolchainShelf(t), assigned, builderNode(), World{})
if err != nil {
t.Fatalf("%s with %s did not compose: %v", c.module, c.owner, err)
}
in := map[string]bool{}
for _, n := range names(got) {
in[n] = true
}
if !in[c.module] || !in[c.owner] {
t.Errorf("%s with %s composed %v", c.module, c.owner, names(got))
}
}
}
// Only the owners declare the packages: a build module that declared go again would compose without the
// owner's provision being met by anything, which is the gap the provision closes.
func TestOnlyTheToolchainModulesDeclareTheToolchainPackages(t *testing.T) {
owner := map[string]string{"go": "go", "nodejs": "nodejs", "npm": "nodejs"}
for name, m := range toolchainShelf(t) {
for _, r := range m.Resources {
if r["type"] != "package" {
continue
}
pkg, _ := r["package"].(string)
if o, ok := owner[pkg]; ok && o != name {
t.Errorf("%s declares the package %s, which only %s may", name, pkg, o)
}
}
}
}