From 4632b6a50857ddc1b5b04850bc1661f4e4e99f4d Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 11 Oct 2026 03:20:15 +0200 Subject: [PATCH 1/3] Judge the SDK conformance fixtures against the SDK the controller pins, never a desktop's checkout (issue 449) A check clones mesh-sdk beside the controller at the commit go.mod pins; elsewhere the test reads a copy captured at that commit, held to go.mod. A missing clone fails instead of skipping. --- cmd/mesh-controller/checks.go | 49 +++++++++++++++++-- cmd/mesh-controller/merge_gate_test.go | 28 +++++++++-- internal/beside/beside.go | 5 +- internal/link/conformance_test.go | 44 +++++++++++++++-- testdata/beside/CAPTURED | 7 ++- .../conformance/events/module-event.json | 44 +++++++++++++++++ 6 files changed, 161 insertions(+), 16 deletions(-) create mode 100644 testdata/beside/mesh-sdk/conformance/events/module-event.json diff --git a/cmd/mesh-controller/checks.go b/cmd/mesh-controller/checks.go index 2fdfbddd..4cbb75b3 100644 --- a/cmd/mesh-controller/checks.go +++ b/cmd/mesh-controller/checks.go @@ -5,6 +5,8 @@ import ( "encoding/json" "fmt" "path" + "regexp" + "runtime/debug" "slices" "sort" "strings" @@ -246,9 +248,11 @@ func checkRequestFor(ctx context.Context, open *stores, p link.PullUpdated, scop if dir == "mesh-controller" { beside["mesh-controller-main"] = link.CheckedOut{Repository: url, Ref: refs["mesh-controller-main"]} if e.Source.Seat != "" { - if lab, err := clone(inventory.Source{Seat: e.Source.Seat, Repository: siblingOf(e.Source.Repository, - "mesh-lab")}); err == nil { - beside["mesh-lab"] = link.CheckedOut{Repository: lab, Ref: refs["mesh-lab"]} + for _, sibling := range []string{"mesh-lab", "mesh-sdk"} { + if url, err := clone(inventory.Source{Seat: e.Source.Seat, Repository: siblingOf(e.Source.Repository, + sibling)}); err == nil { + beside[sibling] = link.CheckedOut{Repository: url, Ref: refs[sibling]} + } } } } @@ -271,17 +275,52 @@ func checkRequestFor(ctx context.Context, open *stores, p link.PullUpdated, scop // catalogue, whose checkout beside is what tests read its files from, at its main, what the next merge // builds from; and beside the controller its main, for a judge the running controller predates, and the // lab's main, whose replays every check runs. **One rule, read by the check the controller asks for and by -// the facts snapshot** (Facts.Beside), so a check run by hand clones what the build seat clones. +// the facts snapshot** (Facts.Beside), so a check run by hand clones what the build seat clones. Beside the +// controller also the SDK, at the commit the controller's go.mod pins (sdkPinned): its conformance fixtures +// are judged against the SDK the controller is built with, not one a desktop holds (novox/hq issue 449). func besideRefs(dir, running string) map[string]string { switch dir { case "mesh-catalog": return map[string]string{dir: "main"} case "mesh-controller": - return map[string]string{dir: running, "mesh-controller-main": "main", "mesh-lab": "main"} + return map[string]string{dir: running, "mesh-controller-main": "main", "mesh-lab": "main", + "mesh-sdk": sdkPinned()} } return map[string]string{dir: running} } +// sdkModule is the Go module of the SDK the controller is built against. +const sdkModule = "git.novox.be/novox/mesh-sdk/go" + +// pseudoCommit is the commit a Go pseudo-version names: v0.1.11-0.20261009143344-f047d0a4a970 → f047d0a4a970. +var pseudoCommit = regexp.MustCompile(`-([0-9a-f]{12})$`) + +// sdkPinned is the ref of the SDK repository this controller was built from, as its go.mod pins it and its +// build records it: a pseudo-version's commit, or a release's tag (the SDK tags its Go module under go/). +// "main" only for a binary that records no SDK version — a local replace — which a running controller is +// not (novox/hq issue 449). +func sdkPinned() string { + info, ok := debug.ReadBuildInfo() + if !ok { + return "main" + } + for _, dep := range info.Deps { + if dep.Path != sdkModule { + continue + } + if dep.Replace != nil { + dep = dep.Replace + } + if m := pseudoCommit.FindStringSubmatch(dep.Version); m != nil { + return m[1] + } + if strings.HasPrefix(dep.Version, "v") { + return "go/" + dep.Version + } + } + return "main" +} + // siblingOf is another repository of the same owner: novox/mesh-controller → novox/mesh-lab. func siblingOf(repository, name string) string { if cut := strings.LastIndex(repository, "/"); cut >= 0 { diff --git a/cmd/mesh-controller/merge_gate_test.go b/cmd/mesh-controller/merge_gate_test.go index 49792733..00289ff2 100644 --- a/cmd/mesh-controller/merge_gate_test.go +++ b/cmd/mesh-controller/merge_gate_test.go @@ -390,9 +390,10 @@ func TestAWithheldPathIsStoodInForByAPath(t *testing.T) { // controller's ask and by the facts — and finds the repository it checks from its origin. func TestACheckByHandClonesWhatTheSeatClones(t *testing.T) { for dir, refs := range map[string]map[string]string{ - "mesh-catalog": {"mesh-catalog": "main"}, - "mesh-host": {"mesh-host": "c0ffee"}, - "mesh-controller": {"mesh-controller": "c0ffee", "mesh-controller-main": "main", "mesh-lab": "main"}, + "mesh-catalog": {"mesh-catalog": "main"}, + "mesh-host": {"mesh-host": "c0ffee"}, + "mesh-controller": {"mesh-controller": "c0ffee", "mesh-controller-main": "main", "mesh-lab": "main", + "mesh-sdk": sdkPinnedByGoMod(t)}, } { got := besideRefs(dir, "c0ffee") for d, ref := range refs { @@ -412,3 +413,24 @@ func TestACheckByHandClonesWhatTheSeatClones(t *testing.T) { } } } + +// sdkPinnedByGoMod is the SDK commit this tree's go.mod pins, read from the file rather than the build, so +// sdkPinned is held to what the repository says (novox/hq issue 449). +func sdkPinnedByGoMod(t *testing.T) string { + t.Helper() + raw, err := os.ReadFile(filepath.Join("..", "..", "go.mod")) + if err != nil { + t.Fatal(err) + } + for _, line := range strings.Split(string(raw), "\n") { + fields := strings.Fields(line) + if len(fields) >= 2 && fields[0] == sdkModule { + if m := pseudoCommit.FindStringSubmatch(fields[1]); m != nil { + return m[1] + } + return "go/" + fields[1] + } + } + t.Fatalf("go.mod requires no %s", sdkModule) + return "" +} diff --git a/internal/beside/beside.go b/internal/beside/beside.go index 3b281765..b64f67cc 100644 --- a/internal/beside/beside.go +++ b/internal/beside/beside.go @@ -1,5 +1,5 @@ // 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 node-engine's genesis template (novox/hq issue 432), the SDK's conformance fixtures (issue 449). // // 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 @@ -10,7 +10,8 @@ // test judges against those clones, so agreement with the other repository is checked where // `mesh/repo-check` runs; a repository missing there fails the test, never skips it. Which clones a // check gets is chosen from the inventory: mesh-catalog by the source of the `nats` module, mesh-host -// by the source of `mesh-host`. In a mesh where either module has no source repository nothing is +// by the source of `mesh-host`, and mesh-sdk as the controller's sibling at the commit the controller's +// go.mod pins. In a mesh where either module has no source repository nothing is // cloned, and these tests fail loudly with "not beside this check": a cause in the setup, not in the // change. And in a delivery group that holds a mesh-catalog pull request, these tests read the // catalogue's main, not the group's head. diff --git a/internal/link/conformance_test.go b/internal/link/conformance_test.go index 50f60dac..edc3f148 100644 --- a/internal/link/conformance_test.go +++ b/internal/link/conformance_test.go @@ -5,15 +5,21 @@ import ( "os" "path/filepath" "regexp" + "strings" "testing" "time" + + "github.com/novox/mesh-controller/internal/beside" ) // The Go implementation, held to the shared fixtures (novox/hq ADR 0074, design 19). // -// **Read from the sdk's conformance directory by sibling path**, the way the lab finds its -// siblings — deliberately not copied here. A fixture copied into each implementation is two -// fixtures, and two fixtures drift, which is the exact failure the suite exists to prevent. +// **Read from the SDK's own conformance directory, never copied by hand**: a fixture written into each +// implementation is two fixtures, and two fixtures drift, which is the failure the suite exists to prevent. +// Which SDK is read is named, never the one a desktop happens to hold beside this checkout (novox/hq issue +// 449): in a merge check the SDK the build seat clones beside it, at the commit this controller's go.mod +// pins, and a missing clone fails the test; elsewhere the copy captured in testdata/beside at that same +// commit (internal/beside, testdata/beside/CAPTURED), held to go.mod by TestTheCapturedSDKIsTheOneGoModPins. type fixture struct { Name string `json:"name"` Given struct { @@ -32,10 +38,11 @@ type fixture struct { func loadFixture(t *testing.T, name string) fixture { t.Helper() - path := filepath.Join("..", "..", "..", "mesh-sdk", "conformance", name) + path := filepath.Join(beside.Dir(t, "mesh-sdk"), "conformance", name) raw, err := os.ReadFile(path) if err != nil { - t.Skipf("the sdk's conformance fixtures are not beside this checkout: %v", err) + // Failed, never skipped: a skip here passed the suite with nothing judged. + t.Fatalf("the SDK's conformance fixture %s: %v", name, err) } var f fixture if err := json.Unmarshal(raw, &f); err != nil { @@ -44,6 +51,33 @@ func loadFixture(t *testing.T, name string) fixture { return f } +// The captured SDK is the one this controller is built against: when go.mod moves the SDK, the copy moves +// with it, or the tests away from a merge check judge an SDK the controller no longer uses (novox/hq issue +// 449). +func TestTheCapturedSDKIsTheOneGoModPins(t *testing.T) { + mod, err := os.ReadFile(filepath.Join("..", "..", "go.mod")) + if err != nil { + t.Fatal(err) + } + pinned := regexp.MustCompile(`(?m)^\s*git\.novox\.be/novox/mesh-sdk/go v\S+-([0-9a-f]{12})$`).FindSubmatch(mod) + if pinned == nil { + t.Fatal("go.mod pins no commit of git.novox.be/novox/mesh-sdk/go as a pseudo-version; say here how a " + + "release's tag is matched against testdata/beside/CAPTURED") + } + captured, err := os.ReadFile(filepath.Join(beside.Captured(), "CAPTURED")) + if err != nil { + t.Fatal(err) + } + at := regexp.MustCompile(`(?m)^mesh-sdk\s+([0-9a-f]{40})\s`).FindSubmatch(captured) + if at == nil { + t.Fatal("testdata/beside/CAPTURED names no commit for mesh-sdk") + } + if !strings.HasPrefix(string(at[1]), string(pinned[1])) { + t.Errorf("the SDK is captured at %s but go.mod pins %s: capture it again at the pinned commit, as "+ + "testdata/beside/CAPTURED says", at[1], pinned[1]) + } +} + // Every header the fixture requires is one this implementation actually sets. func TestTheGoEmitterSetsEveryRequiredHeader(t *testing.T) { f := loadFixture(t, "events/module-event.json") diff --git a/testdata/beside/CAPTURED b/testdata/beside/CAPTURED index 6b819622..17d63986 100644 --- a/testdata/beside/CAPTURED +++ b/testdata/beside/CAPTURED @@ -9,6 +9,7 @@ a second place to clean besides the catalogue itself. mesh-catalog b9de001833b1b61b297182b8e3bcdae1cbdeace9 modules/*/module.json, modules/nats/Dockerfile mesh-host bd5cc6980419c1bd4824be6d58dfebd2381a9de3 examples/foundation-first-node-nats.lock +mesh-sdk f047d0a4a9702f5d7f3d7eadd3e62496311acc00 conformance/events/module-event.json To move them, from this repository's root, with the two repositories checked out beside it: @@ -19,4 +20,8 @@ To move them, from this repository's root, with the two repositories checked out find testdata/beside -name module.json -exec mv {} {}.captured \; git -C ../mesh-host archive examples/foundation-first-node-nats.lock | tar -x -C testdata/beside/mesh-host -and write the commits here. +and write the commits here. The SDK is captured at the commit go.mod pins for git.novox.be/novox/mesh-sdk/go +(the last part of its pseudo-version; novox/hq issue 449), which internal/link's conformance test holds it to: + + rm -rf testdata/beside/mesh-sdk && mkdir -p testdata/beside/mesh-sdk + git -C ../mesh-sdk archive conformance/events | tar -x -C testdata/beside/mesh-sdk diff --git a/testdata/beside/mesh-sdk/conformance/events/module-event.json b/testdata/beside/mesh-sdk/conformance/events/module-event.json new file mode 100644 index 00000000..8de1169b --- /dev/null +++ b/testdata/beside/mesh-sdk/conformance/events/module-event.json @@ -0,0 +1,44 @@ +{ + "capability": "events", + "name": "a module emits an event", + "why": "The envelope is what two implementations can disagree about without either failing: a missing header, a header spelled differently, or a body nested where metadata belongs. None of those stop a mesh running; they stop it reacting.", + "given": { + "module": "shop", + "node": "one", + "key": "order.placed", + "body": { "id": "a1", "total": 12 }, + "headers": { + "x-event-id": "0123456789abcdef0123456789abcdef", + "x-source": "shop", + "x-node": "one", + "x-time": "2026-09-26T12:00:00Z", + "content-type": "application/json" + } + }, + "wire": { + "subject": "mesh.mod.shop.event.order.placed", + "requiredHeaders": ["x-event-id", "x-source", "x-node", "x-time", "content-type"], + "optionalHeaders": ["x-causation-id", "x-schema"], + "headerFormats": { + "x-time": "RFC3339", + "content-type": "application/json", + "x-event-id": "^[0-9a-f]{32}$" + }, + "payloadIs": "the body alone, not the envelope", + "keyRecoveredFrom": "the subject, after the .event. token" + }, + "refuses": [ + { + "what": "an envelope nested in the payload", + "why": "an implementation that publishes the whole envelope as the body passes all of its own tests and is unreadable to every other" + }, + { + "what": "a missing x-event-id", + "why": "delivery is at-least-once and only the emitter can say which of two messages is a redelivery" + }, + { + "what": "an x-source that differs from the subject's module", + "why": "the bus enforces the namespace, so a disagreement means the envelope is lying about its origin" + } + ] +} From 4f5fab81fe9e0b34005f7ab7f2c06c94ba6e9b34 Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 11 Oct 2026 03:25:27 +0200 Subject: [PATCH 2/3] Read the SDK fixtures at the pin of the tree under check, not the running controller's (issue 449 review) A pull request moving the SDK passed its check against the old SDK and then failed everywhere once it rolled out. In a merge check the test now reads the clone's history at the tree's own go.mod pin, and holds the captured copy to the clone byte for byte. --- cmd/mesh-controller/checks.go | 6 +- internal/link/conformance_sources_test.go | 128 ++++++++++++++++++ internal/link/conformance_test.go | 152 +++++++++++++++++++--- 3 files changed, 264 insertions(+), 22 deletions(-) create mode 100644 internal/link/conformance_sources_test.go diff --git a/cmd/mesh-controller/checks.go b/cmd/mesh-controller/checks.go index 4cbb75b3..bb78c718 100644 --- a/cmd/mesh-controller/checks.go +++ b/cmd/mesh-controller/checks.go @@ -276,8 +276,10 @@ func checkRequestFor(ctx context.Context, open *stores, p link.PullUpdated, scop // builds from; and beside the controller its main, for a judge the running controller predates, and the // lab's main, whose replays every check runs. **One rule, read by the check the controller asks for and by // the facts snapshot** (Facts.Beside), so a check run by hand clones what the build seat clones. Beside the -// controller also the SDK, at the commit the controller's go.mod pins (sdkPinned): its conformance fixtures -// are judged against the SDK the controller is built with, not one a desktop holds (novox/hq issue 449). +// controller also the SDK, checked out at the commit the running controller's go.mod pins (sdkPinned), not +// one a desktop holds (novox/hq issue 449). The checkout only places the clone: the conformance test reads the +// fixtures at the pin of the tree under check, from the clone's history, so a pull request moving the SDK is +// judged against the SDK it moves to (internal/link/conformance_test.go). func besideRefs(dir, running string) map[string]string { switch dir { case "mesh-catalog": diff --git a/internal/link/conformance_sources_test.go b/internal/link/conformance_sources_test.go new file mode 100644 index 00000000..53e40484 --- /dev/null +++ b/internal/link/conformance_sources_test.go @@ -0,0 +1,128 @@ +package link + +import ( + "os" + "os/exec" + "path/filepath" + "strings" + "testing" +) + +// Where the conformance fixtures are read from, held on a small SDK repository of the test's own: two +// commits, the clone checked out at the older as the build seat leaves it at the running controller's pin +// (novox/hq issue 449). The contents are the test's, because what is judged is which commit is read. +type sdkBed struct { + root, goMod, captured, capturedLog string + older, newer string +} + +const fixtureName = "events/module-event.json" + +func newSDKBed(t *testing.T) sdkBed { + t.Helper() + if _, err := exec.LookPath("git"); err != nil { + t.Fatalf("git is needed to read the SDK at a pin, as a merge check does: %v", err) + } + b := sdkBed{root: t.TempDir()} + clone := filepath.Join(b.root, "mesh-sdk") + git := func(args ...string) string { + t.Helper() + cmd := exec.Command("git", append([]string{"-C", clone, "-c", "user.name=t", "-c", "user.email=t@t", + "-c", "commit.gpgsign=false"}, args...)...) + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("git %v: %v: %s", args, err, out) + } + return strings.TrimSpace(string(out)) + } + write := func(dir, body string) { + t.Helper() + file := filepath.Join(dir, "conformance", filepath.FromSlash(fixtureName)) + if err := os.MkdirAll(filepath.Dir(file), 0o755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(file, []byte(body), 0o644); err != nil { + t.Fatal(err) + } + } + if err := os.MkdirAll(clone, 0o755); err != nil { + t.Fatal(err) + } + git("init", "--quiet") + write(clone, "older\n") + git("add", "-A") + git("commit", "--quiet", "-m", "older") + b.older = git("rev-parse", "HEAD") + write(clone, "newer\n") + git("commit", "--quiet", "-am", "newer") + b.newer = git("rev-parse", "HEAD") + git("checkout", "--quiet", b.older) + + other := t.TempDir() + b.captured = filepath.Join(other, "mesh-sdk") + write(b.captured, "older\n") + b.capturedLog = filepath.Join(other, "CAPTURED") + if err := os.WriteFile(b.capturedLog, []byte("mesh-sdk "+b.older+" conformance/events\n"), 0o644); err != nil { + t.Fatal(err) + } + b.goMod = filepath.Join(other, "go.mod") + b.pin(t, b.newer) + return b +} + +func (b sdkBed) pin(t *testing.T, commit string) { + t.Helper() + mod := "module x\n\nrequire (\n\t" + sdkModule + " v0.1.11-0.20261009143344-" + commit[:12] + "\n)\n" + if err := os.WriteFile(b.goMod, []byte(mod), 0o644); err != nil { + t.Fatal(err) + } +} + +// A pull request moving the SDK is judged against the SDK it moves to: the clone is read at the tree's own +// pin, not at what it is checked out at, the running controller's. +func TestAMergeCheckReadsTheSDKAtTheTreesOwnPin(t *testing.T) { + b := newSDKBed(t) + raw, from, err := sdkFixture(b.root, b.goMod, b.captured, b.capturedLog, fixtureName) + if err != nil { + t.Fatal(err) + } + if string(raw) != "newer\n" { + t.Errorf("read %q from %s; the tree pins the newer commit", raw, from) + } +} + +// A captured copy that is not the SDK's at the commit CAPTURED names fails a merge check. +func TestAMergeCheckFailsACapturedCopyEditedByHand(t *testing.T) { + b := newSDKBed(t) + if err := os.WriteFile(filepath.Join(b.captured, "conformance", filepath.FromSlash(fixtureName)), + []byte("edited\n"), 0o644); err != nil { + t.Fatal(err) + } + if _, _, err := sdkFixture(b.root, b.goMod, b.captured, b.capturedLog, fixtureName); err == nil || + !strings.Contains(err.Error(), "is not mesh-sdk's at") { + t.Errorf("a hand-edited captured copy was accepted: %v", err) + } +} + +// A pin the clone does not hold, or no clone at all, fails loudly and names what is missing. +func TestAMergeCheckFailsWithoutTheSDKAtItsPin(t *testing.T) { + b := newSDKBed(t) + b.pin(t, "0123456789ab0123456789ab0123456789ab0123") + if _, _, err := sdkFixture(b.root, b.goMod, b.captured, b.capturedLog, fixtureName); err == nil || + !strings.Contains(err.Error(), "0123456789ab") { + t.Errorf("a pin the clone lacks was not named: %v", err) + } + if _, _, err := sdkFixture(t.TempDir(), b.goMod, b.captured, b.capturedLog, fixtureName); err == nil || + !strings.Contains(err.Error(), "not beside this check") { + t.Errorf("a missing clone was not refused: %v", err) + } +} + +// Away from a merge check the captured copy is read, and nothing else. +func TestAwayFromACheckTheCapturedCopyIsRead(t *testing.T) { + b := newSDKBed(t) + raw, _, err := sdkFixture("", b.goMod, b.captured, b.capturedLog, fixtureName) + if err != nil || string(raw) != "older\n" { + t.Errorf("read %q, %v; the captured copy holds the older", raw, err) + } +} diff --git a/internal/link/conformance_test.go b/internal/link/conformance_test.go index edc3f148..66c7471e 100644 --- a/internal/link/conformance_test.go +++ b/internal/link/conformance_test.go @@ -1,8 +1,12 @@ package link import ( + "bytes" "encoding/json" + "fmt" "os" + "os/exec" + "path" "path/filepath" "regexp" "strings" @@ -16,10 +20,19 @@ import ( // // **Read from the SDK's own conformance directory, never copied by hand**: a fixture written into each // implementation is two fixtures, and two fixtures drift, which is the failure the suite exists to prevent. -// Which SDK is read is named, never the one a desktop happens to hold beside this checkout (novox/hq issue -// 449): in a merge check the SDK the build seat clones beside it, at the commit this controller's go.mod -// pins, and a missing clone fails the test; elsewhere the copy captured in testdata/beside at that same -// commit (internal/beside, testdata/beside/CAPTURED), held to go.mod by TestTheCapturedSDKIsTheOneGoModPins. +// Which SDK is read is the one this tree's go.mod pins, never the one a desktop happens to hold beside this +// checkout (novox/hq issue 449): +// +// - in a merge check, the fixture as it stands at that pin in the mesh-sdk clone the build seat places +// beside the check — read with `git show`, because the clone is checked out at the *running* +// controller's pin, and a pull request moving the SDK must be judged against the SDK it moves to, or it +// passes the check and fails everywhere after it rolls out. A missing clone or a pin the clone lacks +// fails the test; +// - elsewhere, the copy captured in testdata/beside at the commit testdata/beside/CAPTURED names, held to +// go.mod by TestTheCapturedSDKIsTheOneGoModPins. +// +// In a merge check the captured copy is also compared with the clone at the captured commit, byte for byte, +// so a hand-edited copy cannot pass for the SDK's. type fixture struct { Name string `json:"name"` Given struct { @@ -36,14 +49,24 @@ type fixture struct { } `json:"wire"` } +// sdkModule is the Go module of the SDK the controller is built against. +const sdkModule = "git.novox.be/novox/mesh-sdk/go" + +// The tree's go.mod, and the captured copy, as this package finds them. +var ( + goModFile = filepath.Join("..", "..", "go.mod") + capturedSDK = filepath.Join(beside.Captured(), "mesh-sdk") + capturedLog = filepath.Join(beside.Captured(), "CAPTURED") +) + func loadFixture(t *testing.T, name string) fixture { t.Helper() - path := filepath.Join(beside.Dir(t, "mesh-sdk"), "conformance", name) - raw, err := os.ReadFile(path) + raw, from, err := sdkFixture(os.Getenv(beside.Env), goModFile, capturedSDK, capturedLog, name) if err != nil { // Failed, never skipped: a skip here passed the suite with nothing judged. - t.Fatalf("the SDK's conformance fixture %s: %v", name, err) + t.Fatal(err) } + t.Logf("%s: %s", name, from) var f fixture if err := json.Unmarshal(raw, &f); err != nil { t.Fatalf("%s: %v", name, err) @@ -51,30 +74,119 @@ func loadFixture(t *testing.T, name string) fixture { return f } +// sdkFixture is one conformance fixture and where it was read: from the mesh-sdk clone in root (a merge +// check's MESH_CHECK_BESIDE) at the pin of goMod, or, with no root, from the captured copy. +func sdkFixture(root, goMod, captured, capturedLog, name string) ([]byte, string, error) { + file := path.Join("conformance", name) + if root == "" { + raw, err := os.ReadFile(filepath.Join(captured, filepath.FromSlash(file))) + if err != nil { + return nil, "", fmt.Errorf("the captured SDK's %s: %w", file, err) + } + return raw, "the copy captured in testdata/beside (see CAPTURED); a merge check reads the SDK's clone " + + "at this tree's pin", nil + } + clone := filepath.Join(root, "mesh-sdk") + if _, err := os.Stat(clone); err != nil { + return nil, "", fmt.Errorf("mesh-sdk is not beside this check in %s=%s, so the SDK this controller "+ + "pins cannot be judged here: %w", beside.Env, root, err) + } + ref, err := sdkRef(goMod) + if err != nil { + return nil, "", err + } + raw, err := gitShow(clone, ref, file) + if err != nil { + return nil, "", fmt.Errorf("the mesh-sdk clone beside this check has no %s at %s, the SDK this tree's "+ + "go.mod pins: %w", file, ref, err) + } + // The captured copy is the SDK's own, never edited by hand: the clone at the captured commit says so. + at, err := capturedCommit(capturedLog) + if err != nil { + return nil, "", err + } + theirs, err := gitShow(clone, at, file) + if err != nil { + return nil, "", fmt.Errorf("the mesh-sdk clone has no %s at %s, the commit CAPTURED names: %w", file, at, err) + } + ours, err := os.ReadFile(filepath.Join(captured, filepath.FromSlash(file))) + if err != nil { + return nil, "", fmt.Errorf("the captured SDK's %s: %w", file, err) + } + if !bytes.Equal(ours, theirs) { + return nil, "", fmt.Errorf("the captured %s is not mesh-sdk's at %s, the commit CAPTURED names: it was "+ + "edited or captured wrong; capture it again as CAPTURED says", file, at) + } + return raw, "mesh-sdk cloned beside this check, at " + ref + " (this tree's go.mod)", nil +} + +// gitShow is one file of a repository at a ref. safe.directory, because the clone is the build seat's and +// the test may run as another user. +func gitShow(repository, ref, file string) ([]byte, error) { + cmd := exec.Command("git", "-c", "safe.directory=*", "-C", repository, "show", ref+":"+file) + var stderr bytes.Buffer + cmd.Stderr = &stderr + out, err := cmd.Output() + if err != nil { + return nil, fmt.Errorf("git show %s:%s: %v: %s", ref, file, err, strings.TrimSpace(stderr.String())) + } + return out, nil +} + +// pseudoCommit is the commit a Go pseudo-version names: v0.1.11-0.20261009143344-f047d0a4a970 → f047d0a4a970. +var pseudoCommit = regexp.MustCompile(`-([0-9a-f]{12})$`) + +// sdkRef is the SDK repository's ref a go.mod pins: a pseudo-version's commit, or a release's tag (the SDK +// tags its Go module under go/). +func sdkRef(goMod string) (string, error) { + raw, err := os.ReadFile(goMod) + if err != nil { + return "", err + } + version := "" + for _, line := range strings.Split(string(raw), "\n") { + fields := strings.Fields(strings.TrimPrefix(strings.TrimSpace(line), "require ")) + if len(fields) >= 2 && fields[0] == sdkModule { + version = fields[1] + } + } + if m := pseudoCommit.FindStringSubmatch(version); m != nil { + return m[1], nil + } + if strings.HasPrefix(version, "v") { + return "go/" + version, nil + } + return "", fmt.Errorf("%s pins no version of %s that names a commit or a tag", goMod, sdkModule) +} + +// capturedCommit is the commit testdata/beside/CAPTURED names for mesh-sdk. +func capturedCommit(capturedLog string) (string, error) { + raw, err := os.ReadFile(capturedLog) + if err != nil { + return "", err + } + at := regexp.MustCompile(`(?m)^mesh-sdk\s+([0-9a-f]{40})\s`).FindSubmatch(raw) + if at == nil { + return "", fmt.Errorf("%s names no commit for mesh-sdk", capturedLog) + } + return string(at[1]), nil +} + // The captured SDK is the one this controller is built against: when go.mod moves the SDK, the copy moves // with it, or the tests away from a merge check judge an SDK the controller no longer uses (novox/hq issue // 449). func TestTheCapturedSDKIsTheOneGoModPins(t *testing.T) { - mod, err := os.ReadFile(filepath.Join("..", "..", "go.mod")) + pinned, err := sdkRef(goModFile) if err != nil { t.Fatal(err) } - pinned := regexp.MustCompile(`(?m)^\s*git\.novox\.be/novox/mesh-sdk/go v\S+-([0-9a-f]{12})$`).FindSubmatch(mod) - if pinned == nil { - t.Fatal("go.mod pins no commit of git.novox.be/novox/mesh-sdk/go as a pseudo-version; say here how a " + - "release's tag is matched against testdata/beside/CAPTURED") - } - captured, err := os.ReadFile(filepath.Join(beside.Captured(), "CAPTURED")) + at, err := capturedCommit(capturedLog) if err != nil { t.Fatal(err) } - at := regexp.MustCompile(`(?m)^mesh-sdk\s+([0-9a-f]{40})\s`).FindSubmatch(captured) - if at == nil { - t.Fatal("testdata/beside/CAPTURED names no commit for mesh-sdk") - } - if !strings.HasPrefix(string(at[1]), string(pinned[1])) { + if strings.HasPrefix(pinned, "go/") || !strings.HasPrefix(at, pinned) { t.Errorf("the SDK is captured at %s but go.mod pins %s: capture it again at the pinned commit, as "+ - "testdata/beside/CAPTURED says", at[1], pinned[1]) + "testdata/beside/CAPTURED says", at, pinned) } } From 9b6acf0ef53c797d15dc22ecff2628679e3d9b0c Mon Sep 17 00:00:00 2001 From: jochen Date: Sun, 11 Oct 2026 04:20:54 +0200 Subject: [PATCH 3/3] Read the captured SDK, saying so, when the seat placed no clone beside the check (issue 449) The seat runs the running controller's besideRefs, which clones no mesh-sdk until this change has rolled out, so a missing clone failing the test kept this change from ever passing its own check. A follow-up makes it a failure again after the rollout. --- internal/link/conformance_sources_test.go | 17 +++++++--- internal/link/conformance_test.go | 39 +++++++++++++++++++---- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/internal/link/conformance_sources_test.go b/internal/link/conformance_sources_test.go index 53e40484..06a0fcd7 100644 --- a/internal/link/conformance_sources_test.go +++ b/internal/link/conformance_sources_test.go @@ -104,7 +104,7 @@ func TestAMergeCheckFailsACapturedCopyEditedByHand(t *testing.T) { } } -// A pin the clone does not hold, or no clone at all, fails loudly and names what is missing. +// A pin the clone does not hold fails loudly and names it. func TestAMergeCheckFailsWithoutTheSDKAtItsPin(t *testing.T) { b := newSDKBed(t) b.pin(t, "0123456789ab0123456789ab0123456789ab0123") @@ -112,9 +112,18 @@ func TestAMergeCheckFailsWithoutTheSDKAtItsPin(t *testing.T) { !strings.Contains(err.Error(), "0123456789ab") { t.Errorf("a pin the clone lacks was not named: %v", err) } - if _, _, err := sdkFixture(t.TempDir(), b.goMod, b.captured, b.capturedLog, fixtureName); err == nil || - !strings.Contains(err.Error(), "not beside this check") { - t.Errorf("a missing clone was not refused: %v", err) +} + +// Until mesh-controller #220 has rolled out the seat places no mesh-sdk beside a check: the captured copy is +// read, and where it was read from says plainly that the clone did not judge it (novox/hq issue 449). +func TestAMergeCheckWithoutTheCloneSaysItReadTheCapturedCopy(t *testing.T) { + b := newSDKBed(t) + raw, from, err := sdkFixture(t.TempDir(), b.goMod, b.captured, b.capturedLog, fixtureName) + if err != nil || string(raw) != "older\n" { + t.Fatalf("read %q, %v; the captured copy holds the older", raw, err) + } + if !strings.HasPrefix(from, "NOT JUDGED AGAINST THE CLONE") || !strings.Contains(from, b.older) { + t.Errorf("the log does not say the clone was missing and which copy was read: %q", from) } } diff --git a/internal/link/conformance_test.go b/internal/link/conformance_test.go index 66c7471e..048ba4f3 100644 --- a/internal/link/conformance_test.go +++ b/internal/link/conformance_test.go @@ -10,6 +10,7 @@ import ( "path/filepath" "regexp" "strings" + "sync" "testing" "time" @@ -26,8 +27,9 @@ import ( // - in a merge check, the fixture as it stands at that pin in the mesh-sdk clone the build seat places // beside the check — read with `git show`, because the clone is checked out at the *running* // controller's pin, and a pull request moving the SDK must be judged against the SDK it moves to, or it -// passes the check and fails everywhere after it rolls out. A missing clone or a pin the clone lacks -// fails the test; +// passes the check and fails everywhere after it rolls out. A pin the clone lacks fails the test; a +// missing clone reads the captured copy and says so loudly, until mesh-controller #220 has rolled out +// and the build seat clones mesh-sdk (then its follow-up makes a missing clone fail again); // - elsewhere, the copy captured in testdata/beside at the commit testdata/beside/CAPTURED names, held to // go.mod by TestTheCapturedSDKIsTheOneGoModPins. // @@ -67,6 +69,10 @@ func loadFixture(t *testing.T, name string) fixture { t.Fatal(err) } t.Logf("%s: %s", name, from) + if strings.HasPrefix(from, notJudged) { + // Said on the check's own output too, where `go test` without -v prints no log of a passing test. + saidNotJudged.Do(func() { fmt.Fprintln(os.Stderr, "internal/link conformance: "+from) }) + } var f fixture if err := json.Unmarshal(raw, &f); err != nil { t.Fatalf("%s: %v", name, err) @@ -74,22 +80,41 @@ func loadFixture(t *testing.T, name string) fixture { return f } +// notJudged opens what a merge check without the SDK's clone says; saidNotJudged says it once on stderr. +const notJudged = "NOT JUDGED AGAINST THE CLONE" + +var saidNotJudged sync.Once + // sdkFixture is one conformance fixture and where it was read: from the mesh-sdk clone in root (a merge // check's MESH_CHECK_BESIDE) at the pin of goMod, or, with no root, from the captured copy. func sdkFixture(root, goMod, captured, capturedLog, name string) ([]byte, string, error) { file := path.Join("conformance", name) - if root == "" { + readCaptured := func() ([]byte, error) { raw, err := os.ReadFile(filepath.Join(captured, filepath.FromSlash(file))) if err != nil { - return nil, "", fmt.Errorf("the captured SDK's %s: %w", file, err) + return nil, fmt.Errorf("the captured SDK's %s: %w", file, err) } + return raw, nil + } + if root == "" { + raw, err := readCaptured() return raw, "the copy captured in testdata/beside (see CAPTURED); a merge check reads the SDK's clone " + - "at this tree's pin", nil + "at this tree's pin", err } clone := filepath.Join(root, "mesh-sdk") if _, err := os.Stat(clone); err != nil { - return nil, "", fmt.Errorf("mesh-sdk is not beside this check in %s=%s, so the SDK this controller "+ - "pins cannot be judged here: %w", beside.Env, root, err) + // A build seat asked by a controller from before mesh-controller #220 places no mesh-sdk beside the + // check, so #220 could never pass its own check if this failed. Until #220 has rolled out, the + // captured copy is read and the log says so loudly; the follow-up of novox/hq issue 449 makes this + // a failure again. + at, cerr := capturedCommit(capturedLog) + if cerr != nil { + return nil, "", cerr + } + raw, rerr := readCaptured() + return raw, fmt.Sprintf(notJudged+": the seat placed no mesh-sdk beside this check in "+ + "%s=%s (it does once mesh-controller #220 has rolled out); read the captured copy at %s", beside.Env, + root, at), rerr } ref, err := sdkRef(goMod) if err != nil {