Merge pull request 'The SDK conformance test reads the SDK the controller pins, never a desktop's checkout (issue 449)' (#220) from fix/449-the-conformance-test-reads-what-it-carries into main
This commit was merged in pull request #220.
This commit is contained in:
@@ -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,54 @@ 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, 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":
|
||||
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 {
|
||||
|
||||
@@ -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 ""
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
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 fails loudly and names it.
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,40 @@
|
||||
package link
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"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 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 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.
|
||||
//
|
||||
// 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 {
|
||||
@@ -30,12 +51,27 @@ 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("..", "..", "..", "mesh-sdk", "conformance", name)
|
||||
raw, err := os.ReadFile(path)
|
||||
raw, from, err := sdkFixture(os.Getenv(beside.Env), goModFile, capturedSDK, capturedLog, name)
|
||||
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.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 {
|
||||
@@ -44,6 +80,141 @@ 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)
|
||||
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 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", err
|
||||
}
|
||||
clone := filepath.Join(root, "mesh-sdk")
|
||||
if _, err := os.Stat(clone); err != nil {
|
||||
// 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 {
|
||||
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) {
|
||||
pinned, err := sdkRef(goModFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
at, err := capturedCommit(capturedLog)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
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, pinned)
|
||||
}
|
||||
}
|
||||
|
||||
// Every header the fixture requires is one this implementation actually sets.
|
||||
func TestTheGoEmitterSetsEveryRequiredHeader(t *testing.T) {
|
||||
f := loadFixture(t, "events/module-event.json")
|
||||
|
||||
Vendored
+6
-1
@@ -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 <commit> 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 <commit> conformance/events | tar -x -C testdata/beside/mesh-sdk
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user