Files
mesh-controller/internal/builder/check_changed_test.go
T

131 lines
4.9 KiB
Go

package builder
import (
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
)
// manyPaths is a list shaped like the real one of mesh-delivery feat/delivery-parts 9e9c2a7 (novox/hq issue
// 1247): 2494 vendored files of nats-server across four new modules, about 208 KB comma-joined.
func manyPaths() []string {
modules := []string{"mesh-delivery-forge-bridge", "mesh-delivery-indexer", "mesh-delivery-teller", "mesh-delivery-view"}
var paths []string
for i := 0; i < 2494; i++ {
paths = append(paths, fmt.Sprintf("modules/%s/vendor/github.com/nats-io/nats-server/v2/server/file_%04d.go",
modules[i%len(modules)], i))
}
return paths
}
func envValue(env []string, name string) string {
for _, e := range env {
if v, ok := strings.CutPrefix(e, name+"="); ok {
return v
}
}
return "\x00unset"
}
func TestALongListOfChangedPathsGoesInAFileAndTheStringHoldsTheModules(t *testing.T) { // 1247/AC1
paths := manyPaths()
if n := len(strings.Join(paths, ",")); n < 200<<10 {
t.Fatalf("the fixture is only %d bytes: not the real list's shape", n)
}
root := t.TempDir()
var said []string
env, err := changedEnvironment(root, paths, func(l string) { said = append(said, l) })
if err != nil {
t.Fatal(err)
}
for _, e := range env {
if len(e) > 64<<10 {
t.Errorf("an environment string of %d bytes is handed to docker: %.60s", len(e), e)
}
}
raw, err := os.ReadFile(envValue(env, EnvChangedFile))
if err != nil {
t.Fatal(err)
}
if got := strings.Split(strings.TrimSuffix(string(raw), "\n"), "\n"); strings.Join(got, "\n") != strings.Join(paths, "\n") {
t.Errorf("the file holds %d lines, not the %d paths in the order given", len(got), len(paths))
}
want := "modules/mesh-delivery-forge-bridge/,modules/mesh-delivery-indexer/,modules/mesh-delivery-teller/,modules/mesh-delivery-view/"
if got := envValue(env, EnvChanged); got != want {
t.Errorf("%s is %q, want each module's directory once: %q", EnvChanged, got, want)
}
if len(said) != 1 || !strings.Contains(said[0], EnvChangedFile) {
t.Errorf("the check's output says %q, want one line naming %s", said, EnvChangedFile)
}
}
func TestAShortListOfChangedPathsStaysAsItWasAndIsInTheFileToo(t *testing.T) { // 1247/AC2
var said []string
env, err := changedEnvironment(t.TempDir(), []string{"a.go", "b.go"}, func(l string) { said = append(said, l) })
if err != nil {
t.Fatal(err)
}
if got := envValue(env, EnvChanged); got != "a.go,b.go" {
t.Errorf("%s is %q", EnvChanged, got)
}
raw, err := os.ReadFile(envValue(env, EnvChangedFile))
if err != nil || string(raw) != "a.go\nb.go\n" {
t.Errorf("the file holds %q, %v", raw, err)
}
if len(said) != 0 {
t.Errorf("a short list said %q", said)
}
}
// theGatesCommand is the command gateLayer hands the toolchain for the merge gate, given the environment of the check.
func theGatesCommand(t *testing.T, env []string) string {
t.Helper()
var args []string
gateLayer(t.Context(), CheckSpec{}, t.TempDir(), t.TempDir(), "gate", filepath.Join(t.TempDir(), "verdict.json"), env,
func(dir string, env []string, command ...string) []string { return command },
func(a []string) error { args = a; return errors.New("stop here") }, &tail{}, "bus", "name", false, "", nil,
func(string, string, ...any) {})
return strings.Join(args, " ")
}
func TestTheGateIsGivenAShortListAsBeforeSoAnOlderJudgeStillRuns(t *testing.T) { // 1247/AC3
env, err := changedEnvironment(t.TempDir(), []string{"a.go", "b.go"}, func(string) {})
if err != nil {
t.Fatal(err)
}
command := theGatesCommand(t, env)
if !strings.Contains(command, `--changed "$MESH_CHECK_CHANGED"`) || strings.Contains(command, "--changed-file") {
t.Errorf("a short list is not given to the gate as --changed alone: %s", command)
}
}
func TestTheGateReadsACutListFromTheFile(t *testing.T) { // 1247/AC3
env, err := changedEnvironment(t.TempDir(), manyPaths(), func(string) {})
if err != nil {
t.Fatal(err)
}
command := theGatesCommand(t, env)
if !strings.Contains(command, `--changed-file "$MESH_CHECK_CHANGED_FILE"`) || strings.Contains(command, `--changed "`) {
t.Errorf("a cut list is not given to the gate through the file alone: %s", command)
}
}
func TestACommandThatCannotStartFailsWithItsError(t *testing.T) { // 1247/AC4
var out tail
layer := ownCheck(t.Context(), CheckSpec{Toolchain: "go-image"}, []ScriptPart{{Toolchain: "go", Script: CheckScript}}, t.TempDir(),
&out, func() bool { return false }, func(image, script string) *exec.Cmd {
return exec.CommandContext(t.Context(), "/nonexistent/docker", script)
}, func(string, string, ...any) {})
if layer == nil || layer.Verdict != "fail" {
t.Fatalf("a command that cannot start answered %+v", layer)
}
if !strings.Contains(layer.Summary, CheckScript) || !strings.Contains(layer.Summary, "no such file or directory") ||
strings.HasSuffix(strings.TrimSpace(layer.Summary), "failed:") {
t.Errorf("the summary does not say why: %q", layer.Summary)
}
}