Replace the downloaded release's tests with tests of the installed toolchain (issue 1572, ADR 0477)
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/forgekey"
|
||||
"git.novox.be/novox/mesh-sdk/go/health"
|
||||
)
|
||||
|
||||
// toolchainValues are the GOTOOLCHAIN settings of an environment.
|
||||
func toolchainValues(env []string) []string {
|
||||
var out []string
|
||||
for _, e := range env {
|
||||
if strings.HasPrefix(e, "GOTOOLCHAIN=") {
|
||||
out = append(out, e)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// 1572/AC1 (ADR 0477 §1): every environment a step gives its go commands says GOTOOLCHAIN=local, once, and names no
|
||||
// release.
|
||||
func TestEveryStepEnvironmentSaysGotoolchainLocal(t *testing.T) {
|
||||
m := &Module{Cache: t.TempDir()}
|
||||
vend := t.TempDir()
|
||||
os.MkdirAll(filepath.Join(vend, "vendor"), 0o755)
|
||||
os.WriteFile(filepath.Join(vend, "vendor", "modules.txt"), nil, 0o644)
|
||||
plain := t.TempDir()
|
||||
envs := map[string][]string{
|
||||
"goEnv": m.goEnv(), "goEnvFor(plain)": m.goEnvFor(plain), "goEnvFor(vendor)": m.goEnvFor(vend),
|
||||
"buildEnv(plain)": m.buildEnv(plain), "buildEnv(vendor)": m.buildEnv(vend), "probeEnv": probeEnv(t.TempDir()),
|
||||
}
|
||||
for name, env := range envs {
|
||||
if got := toolchainValues(env); len(got) != 1 || got[0] != "GOTOOLCHAIN=local" {
|
||||
t.Errorf("%s: GOTOOLCHAIN settings %v, want exactly GOTOOLCHAIN=local", name, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1572/AC1: the fetch step runs no `go version` and downloads no toolchain, and each go command says GOTOOLCHAIN=local.
|
||||
func TestTheFetchStepsGoCommandsSayGotoolchainLocalAndFetchNoToolchain(t *testing.T) {
|
||||
f := &fakeStep{gomods: "go.mod\n"}
|
||||
m, states := fetchModule(t, f)
|
||||
if _, err := fetchTool(t, m)(fetchArgs); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if st := states.waitEnd(t); st.State != delivery.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
goCalls := 0
|
||||
for _, c := range f.calls {
|
||||
if strings.HasSuffix(c.line, " version") || strings.Contains(c.line, " get toolchain") || strings.Contains(c.line, " toolchain") {
|
||||
t.Errorf("the fetch step ran %q", c.line)
|
||||
}
|
||||
if strings.HasPrefix(c.line, "go ") {
|
||||
goCalls++
|
||||
if got := toolchainValues(c.env); len(got) != 1 || got[0] != "GOTOOLCHAIN=local" {
|
||||
t.Errorf("%s: GOTOOLCHAIN settings %v", c.line, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
if goCalls == 0 {
|
||||
t.Error("no go command ran")
|
||||
}
|
||||
}
|
||||
|
||||
type probeSpy struct {
|
||||
env []string
|
||||
line string
|
||||
ran forgekey.Ran
|
||||
left int64
|
||||
}
|
||||
|
||||
func (p *probeSpy) run(ctx context.Context, env []string, name string, args ...string) forgekey.Ran {
|
||||
p.env, p.line = env, name+" "+strings.Join(args, " ")
|
||||
if d, ok := ctx.Deadline(); ok {
|
||||
p.left = int64(d.Sub(time.Now()))
|
||||
}
|
||||
return p.ran
|
||||
}
|
||||
|
||||
// 1572/AC1 (ADR 0477 §1): the probe runs `go version` with GOTOOLCHAIN=local and a short bound, with an empty cache:
|
||||
// it fetches nothing, so there is no long bound for a download.
|
||||
func TestTheProbeRunsGoVersionLocalWithOneShortBound(t *testing.T) {
|
||||
p := &probeSpy{ran: forgekey.Ran{Stdout: "go version go1.27.1-X:nodwarf5 linux/amd64\n"}}
|
||||
v, err := probeGo(context.Background(), p.run, "/home/x", t.TempDir())
|
||||
if err != nil || v != "go1.27.1-X:nodwarf5" {
|
||||
t.Fatalf("= %q, %v", v, err)
|
||||
}
|
||||
if got := toolchainValues(p.env); len(got) != 1 || got[0] != "GOTOOLCHAIN=local" || p.line != "go version" {
|
||||
t.Errorf("command %q, GOTOOLCHAIN settings %v", p.line, got)
|
||||
}
|
||||
if p.left > int64(ProbeTimeout) || p.left <= 0 {
|
||||
t.Errorf("the probe's bound is %v, want at most %v", p.left, int64(ProbeTimeout))
|
||||
}
|
||||
}
|
||||
|
||||
// 1572/AC2 (ADR 0477 §2): neither the recipe nor the module names a Go version: the recipe has no field for one.
|
||||
func TestTheRecipeHasNoFieldForAToolchainVersion(t *testing.T) {
|
||||
typ := reflect.TypeOf(Recipe{})
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
if n := typ.Field(i).Name; n == "Go" || strings.Contains(n, "Toolchain") {
|
||||
t.Errorf("Recipe has the field %s", n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1572/AC2 and AC4: whatever version the node has installed, the module stays healthy, names that version in its health,
|
||||
// and writes it to <node>.go of the toolchains state.
|
||||
func TestAnyInstalledGoIsHealthyNamedAndReported(t *testing.T) {
|
||||
for _, installed := range []string{"go1.27.1", "go1.27.2", "go1.28.0", "go1.27.1-X:nodwarf5"} {
|
||||
t.Run(installed, func(t *testing.T) {
|
||||
states := newStates()
|
||||
m := moduleWith(states, installed)
|
||||
if err := m.Check(context.Background()); err != nil {
|
||||
t.Fatalf("Check refused: %v", err)
|
||||
}
|
||||
a := m.Health(context.Background())
|
||||
if !a.Healthy || a.State != health.StateHealthy || !strings.Contains(a.Why, installed) {
|
||||
t.Errorf("health = %+v, want healthy naming %s", a, installed)
|
||||
}
|
||||
tc, ok := states.toolchain("ace.go")
|
||||
if !ok || tc.Version != installed || tc.Recipe != m.Recipe.Version() {
|
||||
t.Errorf("ace.go = %+v, %v; want version %s and recipe %s", tc, ok, installed, m.Recipe.Version())
|
||||
}
|
||||
ran := false
|
||||
run := func(context.Context, map[string]any) (any, error) { ran = true; return "done", nil }
|
||||
if _, err := m.Short(run)(nil); err != nil || !ran {
|
||||
t.Errorf("a step on go %s: ran %v, err %v", installed, ran, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 1572/AC3 (ADR 0477 §3): the recipe version is made without a toolchain version: the module's recipe is the same
|
||||
// whatever go is installed, and a toolchain version set on the recipe by any name moves nothing; it moves with the
|
||||
// steps' version.
|
||||
func TestTheRecipeVersionIgnoresTheToolchainVersion(t *testing.T) {
|
||||
base := Current()
|
||||
v := base.Version()
|
||||
for _, installed := range []string{"go1.27.1", "go1.28.0"} {
|
||||
m := moduleWith(newStates(), installed)
|
||||
m.Recipe = Current()
|
||||
if err := m.Check(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := m.Recipe.Version(); got != v {
|
||||
t.Errorf("with go %s installed the recipe version is %s, want %s", installed, got, v)
|
||||
}
|
||||
}
|
||||
r := base
|
||||
if f := reflect.ValueOf(&r).Elem().FieldByName("Go"); f.IsValid() {
|
||||
f.SetString("go9.9.9")
|
||||
if r.Version() != v {
|
||||
t.Errorf("a toolchain version on the recipe moved its version")
|
||||
}
|
||||
}
|
||||
r = base
|
||||
r.Steps += "x"
|
||||
if r.Version() == v {
|
||||
t.Errorf("the steps' version did not move the recipe version")
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// 1560 (ADR 0476 §1): every environment a step gives its go commands names the recipe's release as the toolchain
|
||||
// and never GOTOOLCHAIN=local, which would run the distribution's build.
|
||||
func TestEveryStepEnvironmentCarriesTheRecipesRelease(t *testing.T) {
|
||||
m := &Module{Cache: t.TempDir()}
|
||||
vend := t.TempDir()
|
||||
os.MkdirAll(filepath.Join(vend, "vendor"), 0o755)
|
||||
os.WriteFile(filepath.Join(vend, "vendor", "modules.txt"), nil, 0o644)
|
||||
plain := t.TempDir()
|
||||
envs := map[string][]string{
|
||||
"goEnv": m.goEnv(), "goEnvFor(plain)": m.goEnvFor(plain), "goEnvFor(vendor)": m.goEnvFor(vend),
|
||||
"buildEnv(plain)": m.buildEnv(plain), "buildEnv(vendor)": m.buildEnv(vend),
|
||||
}
|
||||
for name, env := range envs {
|
||||
joined := "\n" + strings.Join(env, "\n") + "\n"
|
||||
if !strings.Contains(joined, "\nGOTOOLCHAIN="+RecipeGo+"\n") {
|
||||
t.Errorf("%s lacks GOTOOLCHAIN=%s:\n%s", name, RecipeGo, joined)
|
||||
}
|
||||
if strings.Contains(joined, "GOTOOLCHAIN=local") {
|
||||
t.Errorf("%s still says GOTOOLCHAIN=local:\n%s", name, joined)
|
||||
}
|
||||
if !strings.Contains(joined, "\nGOMODCACHE="+filepath.Join(m.Cache, "gomod")+"\n") {
|
||||
t.Errorf("%s lacks the module cache:\n%s", name, joined)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1560 (ADR 0476 §3): the fetch step downloads the change's locked dependencies and nothing else: no `go version`
|
||||
// and no toolchain download among its commands, and each go command carries the recipe's release.
|
||||
func TestTheFetchStepsCommandsFetchNoToolchain(t *testing.T) {
|
||||
f := &fakeStep{gomods: "go.mod\n"}
|
||||
m, states := fetchModule(t, f)
|
||||
if _, err := fetchTool(t, m)(fetchArgs); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if st := states.waitEnd(t); st.State != delivery.StepPassed {
|
||||
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
|
||||
}
|
||||
goCalls := 0
|
||||
for _, c := range f.calls {
|
||||
if strings.Contains(c.line, "version") || strings.Contains(c.line, "toolchain") {
|
||||
t.Errorf("the fetch step ran %q", c.line)
|
||||
}
|
||||
if strings.HasPrefix(c.line, "go ") {
|
||||
goCalls++
|
||||
if env := strings.Join(c.env, " "); !strings.Contains(env, "GOTOOLCHAIN="+RecipeGo) || strings.Contains(env, "GOTOOLCHAIN=local") {
|
||||
t.Errorf("%s: environment %s", c.line, env)
|
||||
}
|
||||
}
|
||||
}
|
||||
if goCalls == 0 {
|
||||
t.Error("no go command ran")
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
//go:build integration
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// 1560 (ADR 0476 §3): the probe, with a fresh module cache and network, lets the installed Go put
|
||||
// golang.org/toolchain@v0.0.1-<RecipeGo>.<os>-<arch> into the cache and reads the release's version; then a step
|
||||
// with the cache read-only and no network (GOPROXY=off) reports the recipe's version from it.
|
||||
func TestTheProbePutsTheReleaseIntoTheCacheAndAStepReadsItOffline(t *testing.T) {
|
||||
home, cache := t.TempDir(), t.TempDir()
|
||||
t.Cleanup(func() { // the Go module cache is read-only by Go's own doing
|
||||
filepath.WalkDir(cache, func(p string, d fs.DirEntry, err error) error {
|
||||
if err == nil && d.IsDir() {
|
||||
os.Chmod(p, 0o755)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
})
|
||||
v, err := ProbeGo(home, cache)(context.Background())
|
||||
if err != 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, err)
|
||||
}
|
||||
if v != RecipeGo {
|
||||
t.Fatalf("probe = %q; want %s", v, RecipeGo)
|
||||
}
|
||||
dirs, _ := filepath.Glob(filepath.Join(cache, "gomod", "golang.org", "toolchain@v0.0.1-"+RecipeGo+".*"))
|
||||
if len(dirs) != 1 {
|
||||
t.Fatalf("the cache holds %v, want golang.org/toolchain@v0.0.1-%s.*", dirs, RecipeGo)
|
||||
}
|
||||
filepath.WalkDir(cache, func(p string, d fs.DirEntry, err error) error {
|
||||
if err == nil {
|
||||
if info, e := d.Info(); e == nil {
|
||||
os.Chmod(p, info.Mode()&^0o222)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
r := StepRunner(home)(context.Background(), []string{
|
||||
"GOTOOLCHAIN=" + RecipeGo, "GOMODCACHE=" + filepath.Join(cache, "gomod"), "GOCACHE=" + t.TempDir(), "GOPROXY=off", "GOFLAGS=-mod=mod",
|
||||
}, "go", "version")
|
||||
if r.Status != 0 || r.Err != "" {
|
||||
t.Fatalf("go version offline: %s %s", r.Err, r.Stderr)
|
||||
}
|
||||
if got, err := parseGoVersion(r.Stdout); err != nil || got != RecipeGo || !strings.Contains(r.Stdout, RecipeGo) {
|
||||
t.Errorf("the step reports %q, %v; want %s", r.Stdout, err, RecipeGo)
|
||||
}
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/forgekey"
|
||||
"git.novox.be/novox/mesh-sdk/go/health"
|
||||
)
|
||||
|
||||
// probeFake is a runner that records the command and answers by rule.
|
||||
type probeFake struct {
|
||||
env []string
|
||||
line string
|
||||
ran forgekey.Ran
|
||||
}
|
||||
|
||||
func (p *probeFake) run(_ context.Context, env []string, name string, args ...string) forgekey.Ran {
|
||||
p.env, p.line = env, name+" "+strings.Join(args, " ")
|
||||
return p.ran
|
||||
}
|
||||
|
||||
// 1560 (ADR 0476 §2, §3): the probe runs `go version` with the recipe's release as the toolchain and the module's
|
||||
// cache as GOMODCACHE, in an environment built from scratch (nothing of the process's, no GOTOOLCHAIN=local).
|
||||
func TestTheProbeRunsGoVersionWithTheRecipesReleaseAndTheModuleCache(t *testing.T) {
|
||||
p := &probeFake{ran: forgekey.Ran{Stdout: "go version go1.27.1 linux/amd64\n"}}
|
||||
cache := t.TempDir()
|
||||
v, err := probeGo(context.Background(), p.run, "/home/x", cache)
|
||||
if err != nil || v != "go1.27.1" {
|
||||
t.Fatalf("= %q, %v", v, err)
|
||||
}
|
||||
env := "\n" + strings.Join(p.env, "\n") + "\n"
|
||||
for _, want := range []string{"\nGOTOOLCHAIN=" + RecipeGo + "\n", "\nGOMODCACHE=" + filepath.Join(cache, "gomod") + "\n", "\nHOME=/home/x\n"} {
|
||||
if !strings.Contains(env, want) {
|
||||
t.Errorf("the probe's environment lacks %q:\n%s", want, env)
|
||||
}
|
||||
}
|
||||
if strings.Contains(env, "GOTOOLCHAIN=local") || p.line != "go version" {
|
||||
t.Errorf("command %q, environment:\n%s", p.line, env)
|
||||
}
|
||||
}
|
||||
|
||||
// 1560: the release's own output parses to its version; the distribution's build parses to a different one.
|
||||
func TestParseGoVersionOfTheReleaseAndOfTheDistributionsBuild(t *testing.T) {
|
||||
if v, err := parseGoVersion("go version go1.27.1 linux/amd64\n"); err != nil || v != RecipeGo {
|
||||
t.Errorf("release: %q, %v", v, err)
|
||||
}
|
||||
if v, _ := parseGoVersion("go version go1.27.1-X:nodwarf5 linux/amd64\n"); v == RecipeGo {
|
||||
t.Errorf("the distribution's build parsed as the release: %q", v)
|
||||
}
|
||||
}
|
||||
|
||||
// 1560 (ADR 0476 §2): the toolchains key carries the version the probe read from the release.
|
||||
func TestTheToolchainsKeyIsTheReleasesVersion(t *testing.T) {
|
||||
states := newStates()
|
||||
m := moduleWith(states, "")
|
||||
p := &probeFake{ran: forgekey.Ran{Stdout: "go version go1.27.1 linux/amd64\n"}}
|
||||
m.Probe = func(ctx context.Context) (string, error) { return probeGo(ctx, p.run, "/home/x", t.TempDir()) }
|
||||
if err := m.Check(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tc, ok := states.toolchain("ace.go")
|
||||
if !ok || tc.Version != RecipeGo {
|
||||
t.Fatalf("ace.go = %+v, %v; want version %s", tc, ok, RecipeGo)
|
||||
}
|
||||
}
|
||||
|
||||
// 1560 (ADR 0476 §2): a probe that fails (the release cannot be had) or reads another version makes the module
|
||||
// refuse, and its health is unhealthy naming the recipe's version and what the probe got.
|
||||
func TestAFailedFetchOrAnotherVersionMakesHealthUnhealthyNamingBoth(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
ran forgekey.Ran
|
||||
want []string
|
||||
}{
|
||||
{"fetch fails", forgekey.Ran{Status: 1, Stderr: "go: downloading go1.27.1 (linux/amd64)\ngo: download go1.27.1 for linux/amd64: dial tcp: no route to host\n"},
|
||||
[]string{"go1.27.1", "no route to host"}},
|
||||
{"other version", forgekey.Ran{Stdout: "go version go1.27.1-X:nodwarf5 linux/amd64\n"},
|
||||
[]string{"go1.27.1", "go1.27.1-X:nodwarf5"}},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
m := moduleWith(newStates(), "")
|
||||
p := &probeFake{ran: c.ran}
|
||||
m.Probe = func(ctx context.Context) (string, error) { return probeGo(ctx, p.run, "/home/x", t.TempDir()) }
|
||||
a := m.Health(context.Background())
|
||||
if a.Healthy || a.State != health.StateUnhealthy {
|
||||
t.Fatalf("health = %+v, want unhealthy", a)
|
||||
}
|
||||
for _, w := range c.want {
|
||||
if !strings.Contains(a.Why, w) {
|
||||
t.Errorf("health does not name %q: %q", w, a.Why)
|
||||
}
|
||||
}
|
||||
if !strings.Contains(a.Why, "recipe") && !strings.Contains(a.Why, RecipeGo) {
|
||||
t.Errorf("health does not name the recipe's version: %q", a.Why)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 1560: the probe is bound long enough to download while the release is missing, and short once it is cached.
|
||||
func TestTheProbeIsBoundLongOnlyWhileTheReleaseIsMissing(t *testing.T) {
|
||||
var got time.Duration
|
||||
run := func(ctx context.Context, _ []string, _ string, _ ...string) forgekey.Ran {
|
||||
d, _ := ctx.Deadline()
|
||||
got = time.Until(d)
|
||||
return forgekey.Ran{Stdout: "go version go1.27.1 linux/amd64\n"}
|
||||
}
|
||||
cache := t.TempDir()
|
||||
probeGo(context.Background(), run, "/h", cache)
|
||||
if got < time.Minute {
|
||||
t.Errorf("missing release: bound %v", got)
|
||||
}
|
||||
os.MkdirAll(filepath.Join(cache, "gomod", "golang.org", "toolchain@v0.0.1-"+RecipeGo+".linux-amd64"), 0o755)
|
||||
probeGo(context.Background(), run, "/h", cache)
|
||||
if got > ProbeTimeout {
|
||||
t.Errorf("cached release: bound %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
// 1560 (review): Go's progress line is not the cause: the refusal names the line after it.
|
||||
func TestCauseLineSkipsGosDownloadingLine(t *testing.T) {
|
||||
got := causeLine("go: downloading go1.27.1 (linux/amd64)\ngo: download go1.27.1: checksum mismatch\n")
|
||||
if got != "go: download go1.27.1: checksum mismatch" {
|
||||
t.Errorf("causeLine = %q", got)
|
||||
}
|
||||
if got := causeLine("go: downloading go1.27.1 (linux/amd64)\n"); got != "go: downloading go1.27.1 (linux/amd64)" {
|
||||
t.Errorf("only a progress line: %q", got)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/health"
|
||||
)
|
||||
|
||||
// 1572/AC2 (ADR 0477 §2): the recipe has no field for a Node version, and no constant names one.
|
||||
func TestTheRecipeHasNoFieldForAToolchainVersion(t *testing.T) {
|
||||
typ := reflect.TypeOf(Recipe{})
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
if n := typ.Field(i).Name; n == "Node" || strings.Contains(n, "Toolchain") {
|
||||
t.Errorf("Recipe has the field %s", n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1572/AC2 and AC4: whatever Node the machine has installed, the module stays healthy, names that version in its
|
||||
// health, runs its steps and writes the version to <node>.typescript of the toolchains state.
|
||||
func TestAnyInstalledNodeIsHealthyNamedAndReported(t *testing.T) {
|
||||
for _, installed := range []string{"v26.10.0", "v26.11.0", "v27.0.1", "v24.9.0"} {
|
||||
t.Run(installed, func(t *testing.T) {
|
||||
states := newStates()
|
||||
m := moduleWith(states, installed)
|
||||
if err := m.Check(context.Background()); err != nil {
|
||||
t.Fatalf("Check refused: %v", err)
|
||||
}
|
||||
a := m.Health(context.Background())
|
||||
if !a.Healthy || a.State != health.StateHealthy || !strings.Contains(a.Why, installed) {
|
||||
t.Errorf("health = %+v, want healthy naming %s", a, installed)
|
||||
}
|
||||
tc, ok := states.toolchain("ace.typescript")
|
||||
if !ok || tc.Version != installed || tc.Recipe != m.Recipe.Version() {
|
||||
t.Errorf("ace.typescript = %+v, %v; want version %s and recipe %s", tc, ok, installed, m.Recipe.Version())
|
||||
}
|
||||
if err := m.Prepare(context.Background()); err != nil {
|
||||
t.Errorf("no step can run on node %s: %v", installed, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 1572/AC3 (ADR 0477 §3): the recipe version is made without a Node version: the recipe read from a lock is the same
|
||||
// whatever node is installed, and a version set on the recipe by name moves nothing; it moves with the steps' version
|
||||
// and with one byte of the lock.
|
||||
func TestTheRecipeVersionIgnoresTheToolchainVersionAndMovesWithStepsAndLock(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "package-lock.json")
|
||||
os.WriteFile(path, []byte("lock-a"), 0o600)
|
||||
base, err := ReadRecipe(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
v := base.Version()
|
||||
for _, installed := range []string{"v26.10.0", "v27.0.1"} {
|
||||
m := moduleWith(newStates(), installed)
|
||||
m.Reload = func() (Recipe, error) { return ReadRecipe(path) }
|
||||
if err := m.Check(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := m.recipe().Version(); got != v {
|
||||
t.Errorf("with node %s installed the recipe version is %s, want %s", installed, got, v)
|
||||
}
|
||||
}
|
||||
r := base
|
||||
if f := reflect.ValueOf(&r).Elem().FieldByName("Node"); f.IsValid() {
|
||||
f.SetString("v99.0.0")
|
||||
if r.Version() != v {
|
||||
t.Errorf("a Node version on the recipe moved its version")
|
||||
}
|
||||
}
|
||||
r = base
|
||||
r.Steps += "x"
|
||||
if r.Version() == v {
|
||||
t.Errorf("the steps' version did not move the recipe version")
|
||||
}
|
||||
os.WriteFile(path, []byte("lock-b"), 0o600)
|
||||
if b, _ := ReadRecipe(path); b.Version() == v {
|
||||
t.Errorf("one byte of the lock did not move the recipe version")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user