131 lines
4.9 KiB
Go
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)
|
|
}
|
|
}
|