Move the catalogue tests that read mesh-catalog into go/catalogue, and lay the captured copy out once per process (issue 1379)

Imports rewritten to mesh-sdk's (beside, catalogue, overlay). Left in mesh-controller, which holds their modules: TestTheBuilderRequiresTheRegistryTheNpmSeatDelivers (foundation_manifests_test.go), TestGenesisAssignsTheBuildAgentWithNothingProvidingTheRegistry (genesis_registry_test.go, whole file), TestRouteProxyKeepsItsPublicAccountDirectory (public_issuer_test.go), TestEveryManifestInUseClaimsASeatTheMeshDefines (seats_test.go); with controller_is_a_process_test.go and genesis_image_test.go. moduleTree reads mesh-catalog only: mesh-sdk has no modules directory of its own. beside.Dir lays a repository's captured copy out once per process (a mutex and a map, os.MkdirTemp), new test TestTheCapturedCopyIsLaidOutOnce failed first (two directories). The captured copy is refreshed from the mains: mesh-catalog bb0fe93b, mesh-host cdcba9ca, only what these tests read; CAPTURED says how to move it. Unit level 1.16 s (bound 2 s).
This commit is contained in:
2026-10-20 08:22:45 +00:00
parent 8a4381d0c6
commit d1886bbd7b
16 changed files with 121 additions and 178 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ import (
"strings"
"testing"
"github.com/novox/mesh-controller/internal/beside"
"git.novox.be/novox/mesh-sdk/go/internal/beside"
)
// **The word does not come back through a manifest** (novox/hq ADR 0131). A module that wants
+1 -1
View File
@@ -6,7 +6,7 @@ import (
"strings"
"testing"
"github.com/novox/mesh-controller/internal/beside"
"git.novox.be/novox/mesh-sdk/go/internal/beside"
)
// The module holding mesh-broker is the bus, and its account is granted the bus's snapshot API and
+1 -1
View File
@@ -5,7 +5,7 @@ import (
"strings"
"testing"
"github.com/novox/mesh-controller/internal/beside"
"git.novox.be/novox/mesh-sdk/go/internal/beside"
)
// TestEveryCatalogueManifestParses runs the real catalogue through the real gate.
+1 -1
View File
@@ -5,7 +5,7 @@ import (
"strings"
"testing"
"github.com/novox/mesh-controller/internal/beside"
"git.novox.be/novox/mesh-sdk/go/internal/beside"
)
// **A machine trusts the mesh's authority because a module put its root there** (novox/hq ADR
+1 -1
View File
@@ -9,7 +9,7 @@ import (
"strconv"
"testing"
"github.com/novox/mesh-controller/internal/beside"
"git.novox.be/novox/mesh-sdk/go/internal/beside"
)
// The controller refuses a definition's resolved path by the node-engine's own rules, no more (novox/hq issue
+5 -39
View File
@@ -3,13 +3,12 @@ package catalogue
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
"github.com/novox/mesh-controller/internal/beside"
"git.novox.be/novox/mesh-sdk/go/internal/beside"
)
// The catalogue's foundation modules as they are — in a merge check the catalogue cloned beside it,
@@ -17,15 +16,12 @@ import (
// real parser (novox/hq ADR 0100):
// the store and the broker say which of their ports the mesh guards on an adopted node, and the
// filter module loads its table through a unit of its own whose stop deletes only that table.
// moduleTree is where a module is read from: this repository's own modules/<module> when it lives whole here
// (novox/hq ADR 0420 §1, issue 1019: build-agent and route-proxy), with this repository as the tree root;
// otherwise the catalogue beside, with the catalogue as the root.
// moduleTree is where a module is read from: the catalogue beside, with the catalogue as the root. In
// mesh-controller it first looked in the repository's own modules/<module>, where build-agent and
// route-proxy live whole (novox/hq ADR 0420 §1, issue 1019); mesh-sdk has no such directory, and the
// tests that read those two modules stay in mesh-controller (issue 1379).
func moduleTree(t *testing.T, module string) (root, dir string) {
t.Helper()
own := filepath.Join("..", "..", "modules", module)
if _, err := os.Stat(filepath.Join(own, "module.json")); err == nil {
return filepath.Join("..", ".."), own
}
return beside.Dir(t, "mesh-catalog"), filepath.Join(beside.Catalogue(t), module)
}
@@ -132,36 +128,6 @@ func TestTheForgesPortIsGivenLikeAnyOtherProvidersPort(t *testing.T) {
}
}
// **The builder requires the registry the npm seat delivers, and carries no binding of its own.**
//
// It used to carry a hand-written binding because nothing provided a package registry to resolve
// one from at genesis. The catalogue now requires it like any consumer, and ADR 0110 makes the
// seat's holder the answer when more than one module provides it — so a carried copy would be a
// second answer to the same question, free to drift from the first. Asserted gone, not merely
// unused.
//
// The builder is the build-agent on every machine since novox/hq ADR 0190; this read the retired
// `builder` and, finding no manifest, skipped unseen from then until novox/hq issue 432.
func TestTheBuilderRequiresTheRegistryTheNpmSeatDelivers(t *testing.T) {
builder := catalogueManifest(t, "build-agent")
seat, _ := SeatNamed("npm-package-registry")
var requires bool
for _, r := range builder.Requires {
requires = requires || r == seat.Delivers
}
if !requires {
t.Fatalf("the builder does not require %q: %v", seat.Delivers, builder.Requires)
}
if builder.Binds[seat.Delivers] == "" {
t.Errorf("the builder is not told where the registry is: binds %v", builder.Binds)
}
for _, r := range builder.Resources {
if fmt.Sprint(r["id"]) == "package-binding" {
t.Fatal("the builder carries its own package binding beside the one the mesh resolves")
}
}
}
// The forge holds the seats it answers for (novox/hq ADR 0110, 0111), parsed by the real parser —
// which refuses a delivering seat claimed by a module that does not provide what it delivers.
func TestTheForgeHoldsTheNpmAndGitSeats(t *testing.T) {
+1 -1
View File
@@ -5,7 +5,7 @@ import (
"strings"
"testing"
"github.com/novox/mesh-controller/internal/beside"
"git.novox.be/novox/mesh-sdk/go/internal/beside"
)
// A bind mount the module never declared is refused where it is written (novox/hq 04-ISSUES/026,
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"strings"
"testing"
"github.com/novox/mesh-controller/internal/beside"
"git.novox.be/novox/mesh-sdk/go/internal/beside"
)
// **A manifest holds no subject** (novox/hq design 29 §1).
+2 -2
View File
@@ -5,8 +5,8 @@ import (
"strings"
"testing"
"github.com/novox/mesh-controller/internal/catalogue"
"github.com/novox/mesh-controller/internal/overlay"
"git.novox.be/novox/mesh-sdk/go/catalogue"
"git.novox.be/novox/mesh-sdk/go/overlay"
)
// The manifests the control plane actually ships, resolved.
+1 -82
View File
@@ -5,90 +5,9 @@ import (
"path/filepath"
"testing"
"github.com/novox/mesh-controller/internal/beside"
"git.novox.be/novox/mesh-sdk/go/internal/beside"
)
// The public issuer is the proxy's own fact (novox/hq ADR 0226), and the folding of it must not
// move the proxy's account.
//
// route-proxy keeps each ACME authority's account and certificates in a directory named after a
// digest of the directory URL and of the root bundle its `trust` container copies in
// (examples/route-proxy, forThisAuthority). Until ADR 0226 the URL was rendered from a binding to
// `public-acme`'s `acme-ca`, spelled with the port. A URL spelled any other way — even the same
// authority without `:443` — or a root bundle from another image is a new authority to the proxy:
// a new account, and every routed name ordered again, on two machines at once, against Let's
// Encrypt's rate limits. So what the module now states is held to exactly what the binding rendered.
// renderedBeforeTheFold is route-proxy's acme.env as the binding to public-acme rendered it on every
// machine running the proxy, read from the controller's plan on 2026-10-06.
const renderedBeforeTheFold = "ACME_DIRECTORY=https://acme-v02.api.letsencrypt.org:443/directory\n" +
"ACME_ROOTS=https://acme-v02.api.letsencrypt.org:443\n" +
"ACME_ROOTS_PATH=\n"
// trustImage is the image whose system bundle becomes the public root the account directory is
// named after. Moving it renames that directory.
const trustImage = "alpine@sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b"
func TestRouteProxyKeepsItsPublicAccountDirectory(t *testing.T) {
m := catalogueManifest(t, "route-proxy")
for _, r := range m.Requires {
if r == "acme-ca" {
t.Fatal("route-proxy still asks for acme-ca; the public issuer is its own fact (ADR 0226)")
}
}
// As a build would leave it: each artifact a container names resolved to an image. Which image
// does not matter to the file checked here.
for _, res := range m.Resources {
if artifact, ok := res["artifact"].(string); ok {
delete(res, "artifact")
res["image"] = "registry.invalid/route-proxy/" + artifact +
"@sha256:1111111111111111111111111111111111111111111111111111111111111111"
}
}
r := Resolution{
Node: "anchor",
Modules: []Manifest{m},
Needs: []Needed{{
Name: "internal-acme-ca", From: "home", At: "home.internal", For: "route-proxy",
Serves: map[string]any{
"port": float64(9000), "path": "/acme/acme/directory", "roots": "/roots.pem",
},
}},
}
// Its own broker credential, sealed as the mesh would; what it is does not matter here.
out, err := r.Declaration(Rendering{Needed: map[string]map[string]string{
"route-proxy": {"broker": "sealed"}}})
if err != nil {
t.Fatalf("route-proxy could not be composed for a machine: %v", err)
}
env := fileNamed(out, "route-proxy.acme-env")
if env == nil {
t.Fatalf("nothing writes the public issuer's environment: %v", out)
}
if got, _ := env["content"].(string); got != renderedBeforeTheFold {
t.Fatalf("acme.env changed, which moves the proxy's account and reorders every certificate:\n"+
"got %q\nwant %q", got, renderedBeforeTheFold)
}
if fileNamed(out, "route-proxy.bound-acme-ca") != nil {
t.Error("a binding to acme-ca is still written")
}
var trust string
if m.Build != nil {
for _, a := range m.Build.Artifacts {
if a.Name == "trust" {
trust = a.From
}
}
}
if trust != trustImage {
t.Errorf("the trust image is %q, not %q: its system bundle is the public root the account "+
"directory is named after, so moving it reorders every certificate. Move it only with a "+
"plan for the account (ADR 0226)", trust, trustImage)
}
}
// The modules ADR 0226 retired stay retired, and nothing asks for what they provided. A module is
// in the catalogue when its manifest is: a directory left behind with no manifest in it (a build's
// leftovers, untracked by git) is not a module, and failed this on a desktop (novox/hq issue 432).
-38
View File
@@ -1,14 +1,9 @@
package catalogue
import (
"encoding/json"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
"github.com/novox/mesh-controller/internal/beside"
)
// Defends novox/hq ADR 0110: a seat is a module assignment from a closed set.
@@ -194,39 +189,6 @@ func TestAClaimThatIsMalformedIsRefusedOnceForThat(t *testing.T) {
}
}
// Every module in use claims a seat in the set, so closing it refuses nothing that runs.
//
// Read from the catalogue (internal/beside) and from this repository's own manifest, the two places a
// manifest lives (ADR 0069). The private-network module's manifest is composed in code, and its claim
// is checked where it is composed.
func TestEveryManifestInUseClaimsASeatTheMeshDefines(t *testing.T) {
paths, _ := filepath.Glob(filepath.Join(beside.Catalogue(t), "*", "module.json"))
if len(paths) == 0 {
t.Fatal("no manifests in the catalogue, so this proved nothing")
}
paths = append(paths, "../../module.json")
var checked int
for _, path := range paths {
raw, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
// Leniently, so a manifest refused for something unrelated is not reported as a seat
// problem, and the seat check below is the only thing this test holds a module to.
var m Manifest
if err := json.Unmarshal(raw, &m); err != nil {
t.Fatalf("%s: %v", path, err)
}
for _, problem := range claimProblems(m) {
t.Errorf("%s: %s", path, problem)
}
checked += len(m.Claims)
}
if checked == 0 {
t.Fatal("no claims were checked, so this proved nothing")
}
}
// The holder of a seat answers among several providers.
func registryShelf() map[string]Manifest {
+1 -1
View File
@@ -5,7 +5,7 @@ import (
"path/filepath"
"testing"
"github.com/novox/mesh-controller/internal/beside"
"git.novox.be/novox/mesh-sdk/go/internal/beside"
)
// **The showcase module is parsed by the real parser, in the real test suite.**
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"strings"
"testing"
"github.com/novox/mesh-controller/internal/beside"
"git.novox.be/novox/mesh-sdk/go/internal/beside"
)
// boundKeys are every key the files of a module ask of ${bound:forge-account:<key>}, read from the module's own
+40 -6
View File
@@ -1,6 +1,10 @@
// Package beside is where a test finds another repository of the mesh it reads: the catalogue's manifests,
// the node-engine's genesis template (novox/hq issue 432), the SDK's conformance fixtures (issue 449).
//
// Copied from mesh-controller internal/beside at commit 6c2a8d62 (R4b, issue 1379); the one change is that Dir
// lays out the captured copy of a repository once per process, not once per test (see laidOut). In mesh-sdk the
// catalogue's tests read mesh-catalog and mesh-host only; the captured copy holds just what they read.
//
// A test used to read the checkout beside this one, `../../../mesh-catalog`, so its verdict depended on
// whatever sat on the machine running it: a stale or dirty checkout failed it on a desktop, and where none
// was beside it skipped and said nothing. Now there are two inputs, both named, and no third:
@@ -30,6 +34,7 @@ import (
"path/filepath"
"runtime"
"strings"
"sync"
"testing"
)
@@ -57,8 +62,37 @@ func Dir(t testing.TB, repository string) string {
if _, err := os.Stat(from); err != nil {
t.Fatalf("no captured copy of %s at %s: %v", repository, from, err)
}
dir := filepath.Join(t.TempDir(), repository)
err := filepath.WalkDir(from, func(path string, d fs.DirEntry, err error) error {
dir, err := layOut(repository, from)
if err != nil {
t.Fatalf("the captured copy of %s could not be laid out: %v", repository, err)
}
t.Logf("judged against the copy of %s captured in testdata/beside (see CAPTURED); a merge check "+
"judges it against the repository's own clone", repository)
return dir
}
// laidOut is the captured copies already put back in place in this process, by repository. A copy is
// read-only to every test, so it is laid out once per process and not once per test: with a copy per test
// the catalogue's tests alone took 2.14 s, over mesh-sdk's unit bound of 2 s, and with one 0.81 s (measured
// 2026-10-20). The directories are in the temp directory, which the system clears; a process cannot remove
// them while another test may still read them.
var (
laidOutMu sync.Mutex
laidOut = map[string]string{}
)
func layOut(repository, from string) (string, error) {
laidOutMu.Lock()
defer laidOutMu.Unlock()
if dir, ok := laidOut[repository]; ok {
return dir, nil
}
root, err := os.MkdirTemp("", "beside-")
if err != nil {
return "", err
}
dir := filepath.Join(root, repository)
err = filepath.WalkDir(from, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
@@ -74,11 +108,11 @@ func Dir(t testing.TB, repository string) string {
return os.WriteFile(into, raw, 0o644)
})
if err != nil {
t.Fatalf("the captured copy of %s could not be laid out: %v", repository, err)
os.RemoveAll(root)
return "", err
}
t.Logf("judged against the copy of %s captured in testdata/beside (see CAPTURED); a merge check "+
"judges it against the repository's own clone", repository)
return dir
laidOut[repository] = dir
return dir, nil
}
// Catalogue is the catalogue's modules directory, as Dir finds the catalogue.
+62
View File
@@ -0,0 +1,62 @@
package beside
import (
"fmt"
"os"
"path/filepath"
"testing"
)
// The captured copy of a repository is laid out once per test process: a second call, in another test,
// gets the same directory (issue 1379, AC3). Laying it out per test made go/catalogue's tests take 2.14 s
// against the unit level's 2 s bound; once, 0.81 s.
func TestTheCapturedCopyIsLaidOutOnce(t *testing.T) {
t.Setenv(Env, "")
var first, second string
t.Run("one", func(t *testing.T) { first = Dir(t, "mesh-catalog") })
t.Run("two", func(t *testing.T) { second = Dir(t, "mesh-catalog") })
if first == "" || first != second {
t.Fatalf("the captured copy was laid out twice: %q and %q", first, second)
}
if _, err := os.Stat(filepath.Join(first, "modules")); err != nil {
t.Fatalf("the copy holds no modules directory: %v", err)
}
// And it outlives the test that first asked, which a t.TempDir would not.
if _, err := os.Stat(first); err != nil {
t.Fatalf("the copy is gone after the first test ended: %v", err)
}
}
// With MESH_CHECK_BESIDE naming a directory that lacks the repository, the test fails naming it (AC4; the
// rule is beside's own, unchanged). Dir fails the test it is given, so it is given a recorder.
func TestABesideDirectoryWithoutTheRepositoryFailsNamingIt(t *testing.T) {
empty := t.TempDir()
t.Setenv(Env, empty)
r := &recorder{TB: t}
func() {
defer func() { _ = recover() }()
Dir(r, "mesh-catalog")
}()
if !r.failed {
t.Fatal("a repository missing beside the check did not fail the test")
}
if want := "mesh-catalog is not beside this check in " + Env + "=" + empty; len(r.msg) < len(want) || r.msg[:len(want)] != want {
t.Fatalf("the failure does not name the repository and the directory: %q", r.msg)
}
}
type recorder struct {
testing.TB
failed bool
msg string
}
func (r *recorder) Fatalf(format string, args ...any) {
r.failed = true
r.msg = sprintf(format, args...)
panic(stop{})
}
type stop struct{}
func sprintf(format string, args ...any) string { return fmt.Sprintf(format, args...) }
+2 -2
View File
@@ -17,8 +17,8 @@ To move them, from this repository's go/ directory, with the two repositories ch
rm -rf testdata/beside/mesh-catalog testdata/beside/mesh-host
mkdir -p testdata/beside/mesh-catalog testdata/beside/mesh-host/internal/apply
git -C ../../mesh-catalog archive <commit> modules predefined.json | tar -x -C testdata/beside/mesh-catalog --wildcards \
predefined.json "modules/*/module.json" "modules/*/files/*" "modules/*/templates/*"
git -C ../../mesh-catalog archive <commit> -- predefined.json ':(glob)modules/*/module.json' \
':(glob)modules/*/files/**' ':(glob)modules/*/templates/**' | tar -x -C testdata/beside/mesh-catalog
find testdata/beside -name module.json -exec mv {} {}.captured \;
git -C ../../mesh-host show <commit>:internal/apply/placement_guard.go \
> testdata/beside/mesh-host/internal/apply/placement_guard.go.captured