package configkit import ( "bytes" "os" "os/exec" "path/filepath" "strings" "testing" ) func TestDevelopmentCheckHandlesFilenamesAndStopsOnFailure(t *testing.T) { for _, scriptName := range []string{"check.sh"} { t.Run(scriptName, func(t *testing.T) { script, err := os.ReadFile("scripts/" + scriptName) if err != nil { t.Fatal(err) } for _, name := range []string{"success", "failed-go-check", "untracked-whitespace"} { fail := name != "success" t.Run(name, func(t *testing.T) { root := t.TempDir() bin := t.TempDir() log := filepath.Join(bin, "gofmt.args") calls := filepath.Join(bin, "go.calls") for _, directory := range []string{"scripts", "nested"} { if err := os.Mkdir(filepath.Join(root, directory), 0700); err != nil { t.Fatal(err) } } write := func(path string, data []byte, mode os.FileMode) { t.Helper() if err := os.WriteFile(path, data, mode); err != nil { t.Fatal(err) } } write(filepath.Join(root, "scripts", scriptName), script, 0700) write(filepath.Join(root, "go.mod"), []byte("module example.invalid/check\n"), 0600) write(filepath.Join(root, "go.sum"), nil, 0600) write(filepath.Join(root, "obsolete.go"), []byte("package obsolete\n"), 0600) files := []string{"nested/a space.go", "nested/a\nnewline.go"} for _, file := range files { write(filepath.Join(root, file), []byte("package example\n"), 0600) } if name == "untracked-whitespace" { write(filepath.Join(root, "notes.md"), []byte("trailing space \n"), 0600) } // Isolate expensive toolchain commands: this test owns shell orchestration, // not Go's compiler or the production tests exercised by the real checker. write(filepath.Join(bin, "go"), []byte("#!/bin/sh\nprintf '%s/%s %s\\n' \"${GOOS-native}\" \"${GOARCH-native}\" \"$*\" >> \"$CONFIGKIT_TEST_GO_LOG\"\nif [ \"${CONFIGKIT_TEST_FAIL-}\" = yes ] && [ \"${1-}\" = test ]; then exit 37; fi\n"), 0700) write(filepath.Join(bin, "gofmt"), []byte("#!/bin/sh\nprintf '%s\\000' \"$@\" > \"$CONFIGKIT_TEST_FMT_LOG\"\n"), 0700) init := exec.Command("git", "init", "-q") init.Dir = root if out, err := init.CombinedOutput(); err != nil { t.Fatalf("git init: %v %s", err, out) } // HEAD is required by the whitespace gate. Commit only the module baseline. for _, args := range [][]string{{"add", "go.mod", "go.sum", "obsolete.go"}, {"-c", "user.name=Test", "-c", "user.email=test@example.invalid", "-c", "commit.gpgsign=false", "commit", "-qm", "Test fixture"}} { cmd := exec.Command("git", args...) cmd.Dir = root if out, err := cmd.CombinedOutput(); err != nil { t.Fatalf("git fixture: %v %s", err, out) } } if err := os.Remove(filepath.Join(root, "obsolete.go")); err != nil { t.Fatal(err) } cmd := exec.Command("/bin/bash", filepath.Join(root, "scripts", scriptName)) cmd.Env = append(os.Environ(), "PATH="+bin+string(os.PathListSeparator)+os.Getenv("PATH"), "CONFIGKIT_BASELINE_TOOLCHAIN=test-baseline", "CONFIGKIT_NEWER_TOOLCHAIN=", "CONFIGKIT_TEST_FMT_LOG="+log, "CONFIGKIT_TEST_GO_LOG="+calls) // Log only targets selected by the script, not inherited host settings. cmd.Env = append(cmd.Env, "GOOS=", "GOARCH=") if name == "failed-go-check" { cmd.Env = append(cmd.Env, "CONFIGKIT_TEST_FAIL=yes") } out, err := cmd.CombinedOutput() if fail { if err == nil { t.Fatal("checker ignored failed Go test") } } else if err != nil { t.Fatalf("checker: %v %s", err, out) } arguments, err := os.ReadFile(log) if err != nil { t.Fatal(err) } parts := bytes.Split(arguments, []byte{0}) for _, file := range files { found := false for _, arg := range parts { if string(arg) == file { found = true } } if !found { t.Fatalf("filename was split: %q", file) } } invoked, err := os.ReadFile(calls) if err != nil { t.Fatal(err) } if fail && strings.Contains(string(invoked), "vet ./...") { t.Fatal("checker continued after failed test") } }) } }) } } func TestCheckScriptsReleaseGates(t *testing.T) { script, err := os.ReadFile("scripts/release-check.sh") if err != nil { t.Fatal(err) } for _, scenario := range []string{"success", "failed-test", "missing-toolchain"} { t.Run(scenario, func(t *testing.T) { root := t.TempDir() bin := t.TempDir() for _, directory := range []string{"scripts", "nested"} { if err := os.Mkdir(filepath.Join(root, directory), 0700); err != nil { t.Fatal(err) } } checker := filepath.Join(root, "scripts", "release-check.sh") if err := os.WriteFile(checker, script, 0700); err != nil { t.Fatal(err) } calls := filepath.Join(bin, "go.calls") goStub := `#!/bin/sh printf '%s|%s|%s|%s|%s|%s\n' "$GOTOOLCHAIN" "$GOPROXY" "${CGO_ENABLED-}" "${GOOS-}" "${GOARCH-}" "$*" >> "$CONFIGKIT_TEST_GO_LOG" if [ "${CONFIGKIT_TEST_FAIL-}" = toolchain ] && [ "${1-}" = version ]; then exit 37; fi if [ "${CONFIGKIT_TEST_FAIL-}" = test ] && [ "${1-}" = test ]; then exit 37; fi ` if err := os.WriteFile(filepath.Join(bin, "go"), []byte(goStub), 0700); err != nil { t.Fatal(err) } cmd := exec.Command("/bin/bash", checker) cmd.Dir = filepath.Join(root, "nested") cmd.Env = append(os.Environ(), "PATH="+bin+string(os.PathListSeparator)+os.Getenv("PATH"), "CONFIGKIT_BASELINE_TOOLCHAIN=test-baseline", "CONFIGKIT_TEST_GO_LOG="+calls, "GOOS=", "GOARCH=", "CGO_ENABLED=", ) switch scenario { case "failed-test": cmd.Env = append(cmd.Env, "CONFIGKIT_TEST_FAIL=test") case "missing-toolchain": cmd.Env = append(cmd.Env, "CONFIGKIT_TEST_FAIL=toolchain") } output, err := cmd.CombinedOutput() if scenario == "success" && err != nil { t.Fatalf("checker: %v\n%s", err, output) } if scenario != "success" && err == nil { t.Fatalf("checker accepted %s", scenario) } data, err := os.ReadFile(calls) if err != nil { t.Fatal(err) } lines := strings.Split(strings.TrimSpace(string(data)), "\n") for _, line := range lines { if !strings.HasPrefix(line, "test-baseline|off|") { t.Fatalf("toolchain or offline setting lost: %q", line) } } if scenario == "missing-toolchain" { if len(lines) != 1 || !strings.HasSuffix(lines[0], "|version") { t.Fatalf("checker ran after unavailable toolchain: %q", lines) } return } if len(lines) < 2 || lines[1] != "test-baseline|off||||test -mod=readonly -vet=off -race -v ./..." { t.Fatalf("release suite command changed: %q", lines) } if scenario == "failed-test" { if len(lines) != 2 { t.Fatalf("checker continued after failure: %q", lines) } return } want := []string{ "test-baseline|off||||version", "test-baseline|off||||test -mod=readonly -vet=off -race -v ./...", "test-baseline|off||||vet -mod=readonly ./...", "test-baseline|off|0|linux|amd64|build -mod=readonly ./...", "test-baseline|off|0|linux|arm64|build -mod=readonly ./...", } if strings.Join(lines, "\n") != strings.Join(want, "\n") { t.Fatalf("unexpected release gates:\n%s", data) } }) } }