mesh/merge-gate pass: the change touches no module of the mesh's graph
mesh/repo-check pass: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh, merge-check-typescript.sh): main's version judged it; the change's judges the pull r…
mesh/delivery delivered
mesh/delivery-group group fix/385-an-untestable-module-fails-the-check delivered: every member is delivered
A module whose dependencies could not be fetched was said NOT TESTED and the repository check passed, so a merge on it would have landed untested code. The build seat clones the SDK beside every check already: a forge requirement the module cache lacks is now taken from that clone through a go.mod of its own, and a module that still cannot be built fails the check by name and why. A TypeScript module whose dependencies the toolchain lacks fails the same way.
178 lines
6.2 KiB
Go
178 lines
6.2 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// offline is a Go that fetches nothing: what the build seat is once the forge credential is gone, for a pin
|
|
// its module cache does not hold (novox/hq issue 385).
|
|
func offline(t *testing.T) {
|
|
t.Helper()
|
|
t.Setenv("GOPROXY", "off")
|
|
t.Setenv("GOFLAGS", "")
|
|
t.Setenv("GOWORK", "off")
|
|
// Not GOPRIVATE: a private module is fetched directly by git, around GOPROXY, with whatever credential
|
|
// the machine running the test holds.
|
|
t.Setenv("GOPRIVATE", "")
|
|
t.Setenv("GONOPROXY", "")
|
|
t.Setenv("GOSUMDB", "off")
|
|
t.Setenv("GOMODCACHE", t.TempDir())
|
|
}
|
|
|
|
func git(t *testing.T, dir string, args ...string) string {
|
|
t.Helper()
|
|
cmd := exec.Command("git", args...)
|
|
cmd.Dir = dir
|
|
cmd.Env = append(os.Environ(), "GIT_AUTHOR_NAME=t", "GIT_AUTHOR_EMAIL=t@t", "GIT_COMMITTER_NAME=t",
|
|
"GIT_COMMITTER_EMAIL=t@t", "GIT_AUTHOR_DATE=2026-10-12T08:21:13Z", "GIT_COMMITTER_DATE=2026-10-12T08:21:13Z")
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
t.Fatalf("git %s: %v\n%s", strings.Join(args, " "), err, out)
|
|
}
|
|
return strings.TrimSpace(string(out))
|
|
}
|
|
|
|
func write(t *testing.T, path, content string) {
|
|
t.Helper()
|
|
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// sdkBeside is the SDK's repository cloned beside a check, its Go module under go/, one commit tagged
|
|
// go/v0.1.7. It answers the beside root and the commit.
|
|
func sdkBeside(t *testing.T) (string, string) {
|
|
t.Helper()
|
|
beside := t.TempDir()
|
|
repo := filepath.Join(beside, "mesh-sdk")
|
|
write(t, filepath.Join(repo, "go", "go.mod"), "module git.novox.be/novox/mesh-sdk/go\n\ngo 1.22\n")
|
|
write(t, filepath.Join(repo, "go", "sdk", "sdk.go"), "package sdk\n\nfunc Hello() string { return \"hello\" }\n")
|
|
write(t, filepath.Join(repo, "README.md"), "the SDK\n")
|
|
git(t, repo, "init", "--quiet")
|
|
git(t, repo, "add", ".")
|
|
git(t, repo, "commit", "--quiet", "-m", "sdk")
|
|
git(t, repo, "tag", "go/v0.1.7")
|
|
return beside, git(t, repo, "rev-parse", "HEAD")
|
|
}
|
|
|
|
// moduleRequiring is a catalogue module whose code and test use the SDK at the version given.
|
|
func moduleRequiring(t *testing.T, version string) string {
|
|
t.Helper()
|
|
dir := filepath.Join(t.TempDir(), "messenger")
|
|
write(t, filepath.Join(dir, "go.mod"), "module git.novox.be/novox/mesh-catalog/modules/messenger\n\ngo 1.22\n\n"+
|
|
"require git.novox.be/novox/mesh-sdk/go "+version+"\n")
|
|
write(t, filepath.Join(dir, "main.go"), "package main\n\nimport \"git.novox.be/novox/mesh-sdk/go/sdk\"\n\n"+
|
|
"func main() { println(sdk.Hello()) }\n")
|
|
return dir
|
|
}
|
|
|
|
func builds(t *testing.T, dir, modfile string) {
|
|
t.Helper()
|
|
args := []string{"vet"}
|
|
if modfile != "" {
|
|
args = append(args, "-modfile="+modfile)
|
|
}
|
|
cmd := exec.Command("go", append(args, "./...")...)
|
|
cmd.Dir = dir
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
t.Fatalf("the module does not build with %q: %v\n%s", modfile, err, out)
|
|
}
|
|
}
|
|
|
|
// A module that needs nothing it cannot fetch is left as it is: no other go.mod.
|
|
func TestAModuleThatFetchesItsDependenciesNeedsNothingBeside(t *testing.T) {
|
|
offline(t)
|
|
dir := filepath.Join(t.TempDir(), "plain")
|
|
write(t, filepath.Join(dir, "go.mod"), "module example.com/plain\n\ngo 1.22\n")
|
|
write(t, filepath.Join(dir, "main.go"), "package main\n\nfunc main() {}\n")
|
|
modfile, err := resolve(dir, "", t.TempDir())
|
|
if err != nil || modfile != "" {
|
|
t.Fatalf("resolve = %q, %v; want nothing", modfile, err)
|
|
}
|
|
}
|
|
|
|
// The case of issue 385: the module pins an SDK commit the build seat cannot fetch. It is taken from the
|
|
// SDK's clone beside the check, and the module builds against it.
|
|
func TestAPinnedSDKCommitIsTakenFromTheCloneBeside(t *testing.T) {
|
|
offline(t)
|
|
beside, commit := sdkBeside(t)
|
|
dir := moduleRequiring(t, "v0.1.14-0.20261012082113-"+commit[:12])
|
|
modfile, err := resolve(dir, beside, t.TempDir())
|
|
if err != nil {
|
|
t.Fatalf("resolve: %v", err)
|
|
}
|
|
if modfile == "" {
|
|
t.Fatal("resolve said the module fetches its dependencies, with nothing to fetch them from")
|
|
}
|
|
builds(t, dir, modfile)
|
|
if raw, _ := os.ReadFile(filepath.Join(dir, "go.mod")); strings.Contains(string(raw), "replace") {
|
|
t.Fatalf("the module's own go.mod was changed:\n%s", raw)
|
|
}
|
|
}
|
|
|
|
// A released SDK is found by the tag it is released under: go/<version>.
|
|
func TestATaggedSDKIsTakenFromTheCloneBeside(t *testing.T) {
|
|
offline(t)
|
|
beside, _ := sdkBeside(t)
|
|
dir := moduleRequiring(t, "v0.1.7")
|
|
modfile, err := resolve(dir, beside, t.TempDir())
|
|
if err != nil {
|
|
t.Fatalf("resolve: %v", err)
|
|
}
|
|
builds(t, dir, modfile)
|
|
}
|
|
|
|
// A commit the clone beside does not hold (a branch never pushed) cannot be tested, and says which.
|
|
func TestACommitTheCloneBesideDoesNotHoldIsSaid(t *testing.T) {
|
|
offline(t)
|
|
beside, _ := sdkBeside(t)
|
|
dir := moduleRequiring(t, "v0.1.14-0.20261010132341-4f3efc98e0c0")
|
|
_, err := resolve(dir, beside, t.TempDir())
|
|
if err == nil {
|
|
t.Fatal("resolve found a commit nothing holds")
|
|
}
|
|
for _, want := range []string{"git.novox.be/novox/mesh-sdk/go@v0.1.14-0.20261010132341-4f3efc98e0c0", "4f3efc98e0c0"} {
|
|
if !strings.Contains(err.Error(), want) {
|
|
t.Errorf("the reason %q does not name %s", err, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// With nothing cloned beside (a check by hand without the forge), the reason says that.
|
|
func TestNoCloneBesideIsSaid(t *testing.T) {
|
|
offline(t)
|
|
_, commit := sdkBeside(t)
|
|
dir := moduleRequiring(t, "v0.1.14-0.20261012082113-"+commit[:12])
|
|
for _, beside := range []string{"", t.TempDir()} {
|
|
_, err := resolve(dir, beside, t.TempDir())
|
|
if err == nil {
|
|
t.Fatalf("resolve with %q beside found what is not there", beside)
|
|
}
|
|
if !strings.Contains(err.Error(), "mesh-sdk") {
|
|
t.Errorf("the reason %q does not name the repository it needed", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTheRefOfAVersion(t *testing.T) {
|
|
cases := []struct{ sub, version, want string }{
|
|
{"go", "v0.1.14-0.20261012082113-9747f3e1b1ac", "9747f3e1b1ac"},
|
|
{"go", "v0.0.0-20261012082113-9747f3e1b1ac", "9747f3e1b1ac"},
|
|
{"go", "v0.1.7", "go/v0.1.7"},
|
|
{"", "v1.2.0", "v1.2.0"},
|
|
{"", "v2.0.0+incompatible", "v2.0.0"},
|
|
}
|
|
for _, c := range cases {
|
|
if got := refOf(c.sub, c.version); got != c.want {
|
|
t.Errorf("refOf(%q, %q) = %q, want %q", c.sub, c.version, got, c.want)
|
|
}
|
|
}
|
|
}
|