package configkit import ( "bytes" "os" "os/exec" "path/filepath" "strings" "testing" ) func TestCheckScriptsHandleFilenamesAndStopOnFailure(t *testing.T) { for _, scriptName := range []string{"release-check.sh", "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=test-newer", "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") } if scriptName == "release-check.sh" && !fail { // These are selected release gates, not incidental loop details. for _, target := range []string{"linux/amd64", "linux/arm64"} { if !strings.Contains(string(invoked), target+" build ./...") { t.Errorf("checker omitted required target %s", target) } } if strings.Contains(string(invoked), "darwin/") { t.Fatal("checker requires best-effort macOS validation") } } }) } }) } }