Test the Go module's steps and guard against the installed go (issue 1572, ADR 0477)
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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=<RecipeGo> 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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user