diff --git a/modules/mesh-build-go/cmd/mesh-build-go/build_test.go b/modules/mesh-build-go/cmd/mesh-build-go/build_test.go index d5598bd5..181f7d87 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/build_test.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/build_test.go @@ -120,7 +120,7 @@ func TestTheBuildCommandCarriesTodaysFlagsAndEnvironment(t *testing.T) { } } env := strings.Join(c.env, "\n") - for _, want := range []string{compile[1], "GOFLAGS=" + strings.TrimPrefix(compile[2], "GOFLAGS=") + " -mod=mod", "GOTOOLCHAIN=go1.27.1", "GOPROXY=off", + for _, want := range []string{compile[1], "GOFLAGS=" + strings.TrimPrefix(compile[2], "GOFLAGS=") + " -mod=mod", "GOTOOLCHAIN=local", "GOPROXY=off", "GOMODCACHE=" + filepath.Join(m.Cache, "gomod"), "GOCACHE=" + filepath.Join(m.Cache, "gobuild")} { if !strings.Contains(env, want) { t.Errorf("the environment lacks %q:\n%s", want, env) diff --git a/modules/mesh-build-go/cmd/mesh-build-go/digest_test.go b/modules/mesh-build-go/cmd/mesh-build-go/digest_test.go index da9d83bf..7bccd406 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/digest_test.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/digest_test.go @@ -37,15 +37,7 @@ func digestFixture(t *testing.T) (*Module, string) { case "postgres": return forgekey.Ran{Stdout: "postgres (PostgreSQL) " + strconv.Itoa(m.Recipe.PostgresMajor) + ".1\n"} } - // The unit suite has no network: the release (ADR 0476) is not fetched here, the installed Go runs the fixture. - local := []string{} - for _, e := range env { - if e == "GOTOOLCHAIN="+RecipeGo { - e = "GOTOOLCHAIN=local" - } - local = append(local, e) - } - return real(ctx, local, name, args...) + return real(ctx, env, name, args...) } return m, tree } @@ -131,10 +123,6 @@ func TestTheLevelTheStoreAndTheBusAreInTheDigest(t *testing.T) { t.Errorf("a new nats-server version left the integration digest as it was") } m.Recipe.NatsServer = "2.11.17" - m.Recipe.Go = "go1.27.2" - if got := digests(t, m, "unit"); got.Packages[a] == unit.Packages[a] { - t.Errorf("a new Go version left the unit digest as it was") - } } // 1291/AC4: a change in a package no test of a reaches leaves a's digest as it was; d, with no tests, has none. diff --git a/modules/mesh-build-go/cmd/mesh-build-go/fetch_test.go b/modules/mesh-build-go/cmd/mesh-build-go/fetch_test.go index 03de3b9c..d223cdaa 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/fetch_test.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/fetch_test.go @@ -93,7 +93,7 @@ func fetchTool(t *testing.T, m *Module) func(map[string]any) (any, error) { var fetchArgs = map[string]any{"repository": "novox/mesh-sdk", "commit": commitSHA} // 1485/AC1: git is asked through FetchCommit with the ssh remote, and go mod download and verify run in each go.mod -// directory (not in one that vendors) with GOTOOLCHAIN=go1.27.1, GOFLAGS=-mod=mod and the module's cache paths. +// directory (not in one that vendors) with GOTOOLCHAIN=local, GOFLAGS=-mod=mod and the module's cache paths. func TestFetchAsksGitForTheSshRemoteAndDownloadsEachModule(t *testing.T) { f := &fakeStep{gomods: "go.mod\nsub/dir/go.mod\nvend/go.mod\n", vendors: []string{"vend"}} m, states := fetchModule(t, f) @@ -128,7 +128,7 @@ func TestFetchAsksGitForTheSshRemoteAndDownloadsEachModule(t *testing.T) { t.Errorf("a module that vendors was downloaded: %s", c.line) } env := strings.Join(c.env, " ") - for _, want := range []string{"GOTOOLCHAIN=go1.27.1", "GOFLAGS=-mod=mod", "GOMODCACHE=" + filepath.Join(m.Cache, "gomod"), "GOCACHE=" + filepath.Join(m.Cache, "gobuild"), "GOPRIVATE=git.novox.be"} { + for _, want := range []string{"GOTOOLCHAIN=local", "GOFLAGS=-mod=mod", "GOMODCACHE=" + filepath.Join(m.Cache, "gomod"), "GOCACHE=" + filepath.Join(m.Cache, "gobuild"), "GOPRIVATE=git.novox.be"} { if !strings.Contains(env, want) { t.Errorf("%s: environment lacks %s: %s", c.line, want, env) } diff --git a/modules/mesh-build-go/cmd/mesh-build-go/module_test.go b/modules/mesh-build-go/cmd/mesh-build-go/module_test.go index 6e1035fe..99bdcf5a 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/module_test.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/module_test.go @@ -3,6 +3,7 @@ package main import ( "context" "encoding/json" + "errors" "strings" "sync" "testing" @@ -71,7 +72,7 @@ func (s *keptStates) waitEnd(t *testing.T) delivery.Step { } func moduleWith(states *keptStates, installed string) *Module { - return &Module{Node: "ace", Recipe: Recipe{Steps: "1", Go: "go1.27.1", PostgresMajor: 18, NatsServer: "2.11.17"}, + return &Module{Node: "ace", Recipe: Recipe{Steps: "1", PostgresMajor: 18, NatsServer: "2.11.17"}, Probe: func(context.Context) (string, error) { return installed, nil }, Toolchains: states.putToolchain, Steps: states.putStep, Ctx: context.Background()} } @@ -99,11 +100,14 @@ func TestStartWritesTheToolchainAndHealthIsHealthy(t *testing.T) { } } -// 1483/AC3: a probe answering go1.27.2: every step tool's step ends refused naming both versions, ace.go holds -// go1.27.2, and the health tool answers unhealthy naming both. -func TestAnotherGoRefusesEveryStepAndHealthIsUnhealthy(t *testing.T) { +// 1572/AC2 (ADR 0477 §2): a module whose go cannot be run refuses every step tool, naming the cause, and its health is +// unhealthy; no step runs. +func TestAGoThatCannotBeRunRefusesEveryStepAndHealthIsUnhealthy(t *testing.T) { states := newStates() - m := moduleWith(states, "go1.27.2") + m := moduleWith(states, "") + m.Probe = func(context.Context) (string, error) { + return "", errors.New("exec: go: executable file not found in $PATH") + } ran := false run := func(context.Context, map[string]any) (any, error) { ran = true; return "done", nil } steps := append([]StepTool{ @@ -123,30 +127,26 @@ func TestAnotherGoRefusesEveryStepAndHealthIsUnhealthy(t *testing.T) { if out.(map[string]any)["step"] == "" { t.Fatalf("%s answered no step id: %v", tool.Name, out) } - st := states.waitEnd(t) - if st.State != delivery.StepRefused { + if st := states.waitEnd(t); st.State != delivery.StepRefused { t.Errorf("%s: the step ended %s, want refused", tool.Name, st.State) + } else { + reason = st.Reason } - reason = st.Reason } else { if err == nil { t.Fatalf("%s: answered %v, want a refusal", tool.Name, out) } reason = err.Error() } - if !strings.Contains(reason, "go1.27.2") || !strings.Contains(reason, "go1.27.1") { - t.Errorf("%s: the refusal %q does not name both versions", tool.Name, reason) + if !strings.Contains(reason, "executable file not found") { + t.Errorf("%s: the refusal %q does not name the cause", tool.Name, reason) } } if ran { - t.Errorf("a step ran on a node that refuses") + t.Errorf("a step ran on a node whose go cannot be run") } - if tc, _ := states.toolchain("ace.go"); tc.Version != "go1.27.2" { - t.Errorf("ace.go holds %q, want the installed go1.27.2", tc.Version) - } - a := m.Health(context.Background()) - if a.State != health.StateUnhealthy || !strings.Contains(a.Why, "go1.27.2") || !strings.Contains(a.Why, "go1.27.1") { - t.Errorf("health = %+v, want unhealthy naming both versions", a) + if a := m.Health(context.Background()); a.State != health.StateUnhealthy || !strings.Contains(a.Why, "executable file not found") { + t.Errorf("health = %+v, want unhealthy naming the cause", a) } } @@ -159,19 +159,18 @@ func isLong(steps []StepTool, name string) bool { return false } -// 1483/AC4: recipes differing only in the postgres major or the nats-server version have different recipe versions; -// equal recipes have the same one. +// 1483/AC4, 1572/AC3: recipes differing in the steps' version, the postgres major or the nats-server version have +// different recipe versions; equal recipes have the same one. func TestRecipeVersionMovesWithEachOfItsValues(t *testing.T) { - base := Recipe{Steps: "1", Go: "go1.27.1", PostgresMajor: 18, NatsServer: "2.11.17"} - if base.Version() != (Recipe{Steps: "1", Go: "go1.27.1", PostgresMajor: 18, NatsServer: "2.11.17"}).Version() { + base := Recipe{Steps: "1", PostgresMajor: 18, NatsServer: "2.11.17"} + if base.Version() != (Recipe{Steps: "1", PostgresMajor: 18, NatsServer: "2.11.17"}).Version() { t.Errorf("one recipe has two versions") } seen := map[string]string{base.Version(): "base"} for name, r := range map[string]Recipe{ - "postgres major": {Steps: "1", Go: "go1.27.1", PostgresMajor: 19, NatsServer: "2.11.17"}, - "nats-server": {Steps: "1", Go: "go1.27.1", PostgresMajor: 18, NatsServer: "2.11.18"}, - "go": {Steps: "1", Go: "go1.27.2", PostgresMajor: 18, NatsServer: "2.11.17"}, - "steps": {Steps: "2", Go: "go1.27.1", PostgresMajor: 18, NatsServer: "2.11.17"}, + "postgres major": {Steps: "1", PostgresMajor: 19, NatsServer: "2.11.17"}, + "nats-server": {Steps: "1", PostgresMajor: 18, NatsServer: "2.11.18"}, + "steps": {Steps: "2", PostgresMajor: 18, NatsServer: "2.11.17"}, } { v := r.Version() if was, dup := seen[v]; dup { @@ -181,9 +180,9 @@ func TestRecipeVersionMovesWithEachOfItsValues(t *testing.T) { } } -func TestTheRecipeNamesGo1271AndPostgres18AndTheLibrarysNatsVersion(t *testing.T) { +func TestTheRecipeNamesPostgres18AndTheLibrarysNatsVersionAndNoGo(t *testing.T) { r := Current() - if r.Go != "go1.27.1" || r.PostgresMajor != 18 { + if r.PostgresMajor != 18 { t.Errorf("recipe = %+v", r) } if r.NatsServer == "" || strings.HasPrefix(r.NatsServer, "v") { diff --git a/modules/mesh-build-go/cmd/mesh-build-go/test_real_integration_test.go b/modules/mesh-build-go/cmd/mesh-build-go/test_real_integration_test.go index 9a91ef8f..60c2aac7 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/test_real_integration_test.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/test_real_integration_test.go @@ -7,14 +7,11 @@ import ( "bytes" "compress/gzip" "context" - "fmt" "io" - "io/fs" "os" "os/exec" "path/filepath" "strings" - "sync" "testing" "time" ) @@ -24,52 +21,7 @@ func TestMain(m *testing.M) { if len(os.Args) > 1 && os.Args[1] == busArg { os.Exit(runBus(os.Args[2:])) } - code := m.Run() - releaseCleanup() - os.Exit(code) -} - -// The module cache the real-go tests share, filled once by the probe as the module's start does (ADR 0476 §3): the -// steps carry GOTOOLCHAIN= and fetch no toolchain, so a test that runs the real go needs the release in its -// cache first. -var ( - releaseOnce sync.Once - releaseCache string - releaseErr error -) - -// needRelease answers the cache with the recipe's release in it, filled by ProbeGo, and skips loudly when the probe -// cannot have it (no network to Go's download service where the check runs); the build node has it (issues 1486, 1560). -func needRelease(t *testing.T, home string) string { - t.Helper() - releaseOnce.Do(func() { - releaseCache, releaseErr = os.MkdirTemp("", "release-cache") - if releaseErr != nil { - return - } - var v string - if v, releaseErr = ProbeGo(home, releaseCache)(context.Background()); releaseErr == nil && v != RecipeGo { - releaseErr = fmt.Errorf("the probe read %s", v) - } - }) - if releaseErr != nil { - t.Skipf("NOT RUN: ADR 0476: the probe could not put %s into the module cache where this check runs (%v); proven live on a build node", RecipeGo, releaseErr) - } - return releaseCache -} - -// releaseCleanup removes the shared cache; Go writes its module cache read-only. -func releaseCleanup() { - if releaseCache == "" { - return - } - filepath.WalkDir(releaseCache, func(p string, d fs.DirEntry, err error) error { - if err == nil && d.IsDir() { - os.Chmod(p, 0o755) - } - return nil - }) - os.RemoveAll(releaseCache) + os.Exit(m.Run()) } // needTools skips, loudly, a test whose tools are not installed where the check runs (the mesh's Go toolchain image @@ -116,7 +68,7 @@ func TestUnitLevelRunsTheRealGoOnTheFixtureRepositories(t *testing.T) { for name, w := range want { tree := repository(t, name) home := t.TempDir() - m := &Module{Home: home, Work: t.TempDir(), Cache: needRelease(t, home)} + m := &Module{Home: home, Work: t.TempDir(), Cache: t.TempDir()} m.Run = StepRunner(home) res, err := m.runLevel(context.Background(), tree, levelOpts{level: LevelUnit, bounds: Bounds{Run: UnitBound}}) if err != nil { @@ -159,7 +111,7 @@ func TestIntegrationLevelRunsAgainstARealStoreAndBusAndLeavesNothing(t *testing. t.Fatal(err) } home := t.TempDir() - m := &Module{Recipe: Current(), Home: home, Work: work, Cache: needRelease(t, home)} + m := &Module{Recipe: Current(), Home: home, Work: work, Cache: t.TempDir()} m.Run = StepRunner(home) res, err := m.integration(context.Background(), "ws1", filepath.Join(work, "ws1", "tree"), nil) if err != nil { @@ -197,7 +149,7 @@ func TestBuildTwiceGivesOneDigestAndOneExecutable(t *testing.T) { t.Fatal(err) } home := t.TempDir() - m := &Module{Home: home, Work: work, Cache: needRelease(t, home)} + m := &Module{Home: home, Work: work, Cache: t.TempDir()} m.Run = StepRunner(home) args := map[string]any{"workspace": "ws1", "module": ".", "artifact": "tools"} first, err := m.build(context.Background(), args) diff --git a/modules/mesh-build-go/cmd/mesh-build-go/test_test.go b/modules/mesh-build-go/cmd/mesh-build-go/test_test.go index 4fdbda2c..c13e8085 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/test_test.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/test_test.go @@ -175,7 +175,7 @@ func TestUnitLevelRunsGoTestJsonInTheModulesDirectoryWithTheStepsEnvironment(t * if line != want { t.Errorf("command = %q, want %q", line, want) } - for _, w := range []string{"GOTOOLCHAIN=go1.27.1", "GOFLAGS=-mod=mod", "GOMODCACHE=" + filepath.Join(m.Cache, "gomod")} { + for _, w := range []string{"GOTOOLCHAIN=local", "GOFLAGS=-mod=mod", "GOMODCACHE=" + filepath.Join(m.Cache, "gomod")} { if !strings.Contains(env, w) { t.Errorf("environment %s lacks %s", env, w) }