package link import ( "fmt" "os" "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 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 { Module string `json:"module"` Node string `json:"node"` Key string `json:"key"` Body map[string]any `json:"body"` Headers map[string]string `json:"headers"` } `json:"given"` Wire struct { Subject string `json:"subject"` RequiredHeaders []string `json:"requiredHeaders"` HeaderFormats map[string]string `json:"headerFormats"` } `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") ) // 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. // 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. // 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. // And each value is in the shape the fixture pins, because a header present but differently // formatted is the disagreement that does not announce itself. // The subject a module's event lands on is derived, not carried — so this implementation must // derive the same one the fixture names. // goEventHeaders is the header set EmitEvent produces, factored so conformance can see it // without a broker. Kept beside the emitter so the two cannot drift apart silently. func goEventHeaders(eventType, source, node string) map[string]string { id, err := eventID() if err != nil { panic(err) } return map[string]string{ "x-event-id": id, "x-source": source, "x-node": node, "x-time": time.Now().UTC().Format(time.RFC3339), "content-type": "application/json", } }