Files
mesh-catalog/modules/mesh-build-go/cmd/mesh-build-go/pins_test.go
T

247 lines
9.9 KiB
Go

package main
import (
"context"
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
"git.novox.be/novox/mesh-sdk/go/delivery"
"git.novox.be/novox/mesh-sdk/go/forgekey"
)
// pinShort is the 12 digits of commitSHA a pseudo-version ends in.
const pinShort = "0123456789ab"
const pinnedGoMod = "module fixture/member\n\ngo 1.26\n\nrequire (\n\tgit.novox.be/novox/mesh-sdk/go v0.1.15-0.20261022120000-" + pinShort + "\n)\n"
// pinHarness runs a fetch of a tree whose only go.mod is gomod (a vendoring one when vendor is set) against a runner
// that stands for the forge and for go: a pull request's head is listed by ls-remote when pulls says so, and go mod
// download of a pseudo-version that no ref carries fails with "unknown revision" unless the command was handed a
// -modfile.
type pinHarness struct {
t *testing.T
m *Module
f *fakeStep
pulls string // what git ls-remote answers
onTrunk bool // the pin is fetchable as it is: go mod download never says unknown revision
cmds []cmdRecord
}
func newPinHarness(t *testing.T, gomod string, vendor bool, pulls string) (*pinHarness, *keptStates) {
f := &fakeStep{gomods: "go.mod\n"}
m, states := fetchModule(t, f)
h := &pinHarness{t: t, m: m, f: f, pulls: pulls}
m.Run = func(ctx context.Context, env []string, name string, args ...string) forgekey.Ran {
line := name + " " + strings.Join(args, " ")
h.cmds = append(h.cmds, cmdRecord{env: env, line: line})
switch {
case name == "git" && strings.Contains(line, " checkout ") && !strings.Contains(line, "pinned"):
for i, a := range args {
if a == "-C" {
os.MkdirAll(args[i+1], 0o755)
for _, g := range strings.Fields(f.gomods) {
d := filepath.Join(args[i+1], filepath.Dir(g))
os.MkdirAll(d, 0o755)
os.WriteFile(filepath.Join(d, "go.mod"), []byte(gomod), 0o644)
os.WriteFile(filepath.Join(d, "go.sum"), []byte("sum\n"), 0o644)
}
if vendor {
os.MkdirAll(filepath.Join(args[i+1], "vendor"), 0o755)
os.WriteFile(filepath.Join(args[i+1], "vendor", "modules.txt"), nil, 0o644)
}
}
}
case name == "git" && strings.Contains(line, "ls-remote"):
return forgekey.Ran{Stdout: h.pulls}
case name == "go" && strings.Contains(line, " mod download"):
if !h.onTrunk && !strings.Contains(strings.Join(env, " "), "-modfile=") {
return forgekey.Ran{Status: 1, Stderr: "go: git.novox.be/novox/mesh-sdk/go@v0.1.15-0.20261022120000-" + pinShort + ": invalid version: unknown revision " + pinShort + "\n"}
}
}
return f.run(ctx, env, name, args...)
}
return h, states
}
func (h *pinHarness) fetch(states *keptStates) (FetchResult, delivery.Step) {
h.t.Helper()
if _, err := fetchTool(h.t, h.m)(fetchArgs); err != nil {
h.t.Fatal(err)
}
st := states.waitEnd(h.t)
var res FetchResult
if st.State == delivery.StepPassed {
json.Unmarshal(st.Result, &res)
}
return res, st
}
func (h *pinHarness) ran(sub string) bool {
for _, c := range h.cmds {
if strings.Contains(c.line, sub) {
return true
}
}
return false
}
const pullList = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/pull/6/head\n" + commitSHA + "\trefs/pull/7/head\n"
// 1148/AC1: a go.mod requiring mesh-sdk at a pseudo-version only a pull request's head carries is fetched: the
// repository is placed beside the workspace at that commit, go mod download and verify succeed through a temporary
// -modfile replacing the module by that directory, and the build is handed the same -modfile with GOPROXY=off.
func TestAPinOnAPullHeadIsPlacedBesideAndHandedToGoByAModfile(t *testing.T) {
h, states := newPinHarness(t, pinnedGoMod, false, pullList)
res, st := h.fetch(states)
if st.State != delivery.StepPassed {
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
}
placed := filepath.Join(h.m.Work, res.Workspace, "pinned", "mesh-sdk@"+commitSHA, "beside", "mesh-sdk")
if !h.ran("-C " + placed + " checkout -q --detach " + commitSHA) {
t.Errorf("mesh-sdk was not placed at %s at the commit:\n%s", placed, strings.Join(h.f.lines(), "\n"))
}
if !h.ran("refs/pull/*/head") {
t.Errorf("the forge's pull request heads were not listed")
}
dir := filepath.Join(h.m.Work, res.Workspace, "tree")
flags := ""
for _, e := range h.m.goEnvFor(dir) {
if strings.HasPrefix(e, "GOFLAGS=") {
flags = e
}
}
_, modfile, ok := strings.Cut(flags, "-modfile=")
if !ok {
t.Fatalf("go is not handed a -modfile: %v", h.m.goEnvFor(dir))
}
raw, err := os.ReadFile(strings.Fields(modfile)[0])
if err != nil {
t.Fatal(err)
}
if want := "replace git.novox.be/novox/mesh-sdk/go => " + filepath.Join(placed, "go"); !strings.Contains(string(raw), want) || !strings.Contains(string(raw), "module fixture/member") {
t.Errorf("the modfile lacks %q:\n%s", want, raw)
}
build := strings.Join(h.m.buildEnv(dir), " ")
if !strings.Contains(build, "-modfile="+strings.Fields(modfile)[0]) || !strings.Contains(build, "GOPROXY=off") {
t.Errorf("the build is not handed the modfile with GOPROXY=off: %s", build)
}
if !h.ran(" mod verify") {
t.Errorf("go mod verify did not run")
}
}
// 1148/AC3: a pin go downloads as it is (on the trunk or a tag) behaves as today: no listing of pull requests, no
// repository placed, no -modfile in the fetch or the build.
func TestAPinGoCanDownloadIsLeftAsToday(t *testing.T) {
h, states := newPinHarness(t, pinnedGoMod, false, pullList)
h.onTrunk = true
res, st := h.fetch(states)
if st.State != delivery.StepPassed {
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
}
if h.ran("ls-remote") || h.ran("pinned") {
t.Errorf("a pin on the trunk made the fetch look for a pull request or place a repository:\n%s", strings.Join(h.f.lines(), "\n"))
}
dir := filepath.Join(h.m.Work, res.Workspace, "tree")
for _, e := range append(h.m.goEnvFor(dir), h.m.buildEnv(dir)...) {
if strings.Contains(e, "-modfile") {
t.Errorf("a -modfile is handed to go: %s", e)
}
}
if !h.ran(" mod download") || !h.ran(" mod verify") {
t.Errorf("go mod download and verify did not run as before")
}
}
// 1148/AC4: a pin that no ref of the forge carries ends the step failed, naming the module and the commit.
func TestAPinOnNoRefFailsNamingTheModuleAndTheCommit(t *testing.T) {
h, states := newPinHarness(t, pinnedGoMod, false, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\trefs/pull/6/head\n")
_, st := h.fetch(states)
if st.State != delivery.StepFailed {
t.Fatalf("the step ended %s, want failed", st.State)
}
if !strings.Contains(st.Reason, "no pull request head") || !strings.Contains(st.Reason, "unknown revision") ||
!strings.Contains(st.Reason, "git.novox.be/novox/mesh-sdk/go") || !strings.Contains(st.Reason, pinShort) {
t.Errorf("the reason does not name the module and the commit: %q", st.Reason)
}
if !h.ran("ls-remote") {
t.Errorf("the forge's pull request heads were never looked at")
}
}
// 1148/AC5: a directory that vendors resolves nothing: no go command, no listing, no repository placed.
func TestAVendoringDirectoryResolvesNothing(t *testing.T) {
h, states := newPinHarness(t, pinnedGoMod, true, pullList)
_, st := h.fetch(states)
if st.State != delivery.StepPassed {
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
}
if h.ran("go -C") || h.ran("ls-remote") || h.ran("pinned") {
t.Errorf("a vendoring directory was resolved:\n%s", strings.Join(h.f.lines(), "\n"))
}
}
// 1148/AC1 (a repository with several go.mod directories, as mesh-catalog has): two directories pinning the same pull
// head share one placement and both are handed a modfile; the fetch does not fail placing it twice.
func TestTwoDirectoriesPinningOneHeadShareOnePlacement(t *testing.T) {
h, states := newPinHarness(t, pinnedGoMod, false, pullList)
h.f.gomods = "go.mod\nsub/go.mod\n"
res, st := h.fetch(states)
if st.State != delivery.StepPassed {
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
}
checkouts := 0
for _, c := range h.cmds {
if strings.Contains(c.line, "pinned") && strings.Contains(c.line, " checkout ") {
checkouts++
}
}
if checkouts != 1 {
t.Errorf("the repository was placed %d times, want once:\n%s", checkouts, strings.Join(h.f.lines(), "\n"))
}
for _, d := range []string{"tree", "tree/sub"} {
if _, ok := h.m.pinModfile(filepath.Join(h.m.Work, res.Workspace, d)); !ok {
t.Errorf("%s was not handed a modfile", d)
}
}
}
// 1148/AC1: two directories pinning two different commits of one repository get two placements, each its own.
func TestTwoCommitsOfOneRepositoryGetTwoPlacements(t *testing.T) {
other := "fedcba9876543210fedcba9876543210fedcba98"
h, states := newPinHarness(t, pinnedGoMod, false, commitSHA+"\trefs/pull/7/head\n"+other+"\trefs/pull/8/head\n")
h.f.gomods = "go.mod\nsub/go.mod\n"
base := h.m.Run
h.m.Run = func(ctx context.Context, env []string, name string, args ...string) forgekey.Ran {
if name == "git" && strings.Contains(strings.Join(args, " "), "pinned/mesh-sdk@"+other+"/") && strings.Contains(strings.Join(args, " "), " rev-parse HEAD") {
return forgekey.Ran{Stdout: other + "\n"}
}
// the second directory pins the other commit
if name == "git" && strings.Contains(strings.Join(args, " "), " checkout ") && !strings.Contains(strings.Join(args, " "), "pinned") {
r := base(ctx, env, name, args...)
for i, a := range args {
if a == "-C" {
os.WriteFile(filepath.Join(args[i+1], "sub", "go.mod"), []byte(strings.ReplaceAll(pinnedGoMod, pinShort, other[:12])), 0o644)
}
}
return r
}
if name == "go" && strings.Contains(strings.Join(args, " "), "/sub mod download") && !strings.Contains(strings.Join(env, " "), "-modfile=") {
return forgekey.Ran{Status: 1, Stderr: "go: git.novox.be/novox/mesh-sdk/go@v0.1.15-0.20261022120000-" + other[:12] + ": invalid version: unknown revision " + other[:12] + "\n"}
}
return base(ctx, env, name, args...)
}
_, st := h.fetch(states)
if st.State != delivery.StepPassed {
t.Fatalf("the step ended %s: %s", st.State, st.Reason)
}
for _, c := range []string{commitSHA, other} {
if !h.ran("pinned/mesh-sdk@" + c) {
t.Errorf("no placement of mesh-sdk at %s", c)
}
}
}