package main import ( "bytes" "encoding/json" "fmt" "os" "os/exec" "path/filepath" "runtime" "strings" "testing" ) func TestGeneratedConsumerAndArtifactBoundary(t *testing.T) { root, err := filepath.Abs("../..") if err != nil { t.Fatal(err) } dir, err := filepath.EvalSymlinks(t.TempDir()) if err != nil { t.Fatal(err) } execute := func(cwd, name string, args ...string) []byte { t.Helper() cmd := exec.CommandContext(t.Context(), name, args...) cmd.Dir = cwd out, err := cmd.CombinedOutput() if err != nil { t.Fatalf("%s %v: %v\n%s", name, args, err, out) } return out } tool := filepath.Join(dir, "corestack-goose") execute(root, "go", "build", "-o", tool, "./cmd/corestack-goose") migrations := filepath.Join(dir, "migrations") if err := os.Mkdir(migrations, 0o700); err != nil { t.Fatal(err) } osName := "windows" if runtime.GOOS == "windows" { osName = "linux" } for _, tc := range []struct{ name, kind string }{{"initial", "sql"}, {"test", "go"}, {"create_test", "go"}, {osName, "go"}} { execute(dir, tool, "create", "--dir", migrations, tc.name, tc.kind) execute(dir, tool, "fix", "--dir", migrations) } execute(dir, tool, "fix", "--dir", migrations) source, err := os.ReadFile("testdata/consumer/main.go") if err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(dir, "main.go"), source, 0o600); err != nil { t.Fatal(err) } module := fmt.Sprintf("module example.com/consumer\n\ngo 1.27.1\n\nrequire go.auvem.com/corestack v0.0.0\nreplace go.auvem.com/corestack => %s\n", filepath.ToSlash(root)) if err := os.WriteFile(filepath.Join(dir, "go.mod"), []byte(module), 0o600); err != nil { t.Fatal(err) } binary := filepath.Join(dir, "consumer") execute(dir, "go", "build", "-mod=mod", "-o", binary, ".") out := execute(dir, binary, filepath.Join(dir, "consumer.db")) if !strings.Contains(string(out), "version=4 pending=false versions=1,2,3,4 fresh=true isolated-pending=true") { t.Fatal(string(out)) } // Decode package identities rather than guessing from substring matches in go list. deps := execute(dir, "go", "list", "-deps", "-json", ".") decoder := json.NewDecoder(bytes.NewReader(deps)) adapterFound := false for decoder.More() { var dep struct{ ImportPath string } if err := decoder.Decode(&dep); err != nil { t.Fatal(err) } if dep.ImportPath == "go.auvem.com/corestack/migrate/lifecycle" { adapterFound = true } if dep.ImportPath == "go.auvem.com/corestack/cmd/corestack-goose" || strings.HasPrefix(dep.ImportPath, "go.auvem.com/corestack/cmd/corestack-goose/") { t.Fatalf("production imports authoring: %s", dep.ImportPath) } } if !adapterFound { t.Fatal("production fixture did not import runtime adapter") } toolSymbols := execute(dir, "go", "tool", "nm", tool) toolEntries := map[string]bool{} for _, line := range strings.Split(string(toolSymbols), "\n") { fields := strings.Fields(line) if len(fields) >= 3 { toolEntries[fields[len(fields)-1]] = true } } for _, entry := range []string{"github.com/pressly/goose/v3.CreateWithTemplate", "go.auvem.com/corestack/cmd/corestack-goose/internal/authoring.Create", "go.auvem.com/corestack/cmd/corestack-goose/internal/authoring.Fix"} { if !toolEntries[entry] { t.Fatalf("standalone authoring entrypoint missing: %s", entry) } } runtimeFound := false symbols := execute(dir, "go", "tool", "nm", binary) for _, line := range strings.Split(string(symbols), "\n") { fields := strings.Fields(line) if len(fields) < 3 { continue } symbol := fields[len(fields)-1] if symbol == "go.auvem.com/corestack/migrate/lifecycle.Commands" { runtimeFound = true } if strings.HasPrefix(symbol, "go.auvem.com/corestack/cmd/corestack-goose/") { t.Fatalf("authoring symbol linked: %s", symbol) } for _, entry := range []string{"Create", "CreateWithTemplate", "Fix", "SetSequential"} { full := "github.com/pressly/goose/v3." + entry if symbol == full || strings.HasPrefix(symbol, full+".") { t.Fatalf("upstream authoring entrypoint linked: %s", symbol) } } } if !runtimeFound { t.Fatal("runtime command symbol missing from production executable") } t.Log("standalone create/fix; unchanged generated SQL+safe-suffix Go compiled and applied; runtime dependency identities and linker symbols verified") }