//go:build integration package nodes_test import ( "context" "fmt" "os" "os/exec" "testing" "time" ) // TestMain starts a throw-away MySQL 8 Docker container when PANGOLIN_TEST_DSN // is not already set, runs all tests, and tears the container down on exit. // // If Docker is unavailable the tests are skipped gracefully (requireDSN in // lifecycle_test.go handles the skip). func TestMain(m *testing.M) { if os.Getenv("PANGOLIN_TEST_DSN") != "" { // Caller already provided a DSN — run directly. os.Exit(m.Run()) } const ( containerName = "pangolin-lc-test-mysql" hostPort = "13399" dbName = "pangolin_test" rootPwd = "secret" ) ctx := context.Background() // Remove any leftover container from a previous crashed run. _ = exec.CommandContext(ctx, "docker", "rm", "-f", containerName).Run() // Start MySQL 8. startCmd := exec.CommandContext(ctx, "docker", "run", "-d", "--name", containerName, "-e", "MYSQL_ROOT_PASSWORD="+rootPwd, "-e", "MYSQL_DATABASE="+dbName, "-p", hostPort+":3306", "mysql:8", ) if out, err := startCmd.CombinedOutput(); err != nil { fmt.Fprintf(os.Stderr, "docker run failed: %v\n%s\n", err, out) fmt.Fprintln(os.Stderr, "skipping lifecycle integration tests (no MySQL)") os.Exit(0) // graceful skip } // Tear down on exit regardless of test outcome. defer func() { _ = exec.Command("docker", "stop", containerName).Run() _ = exec.Command("docker", "rm", containerName).Run() }() // multiStatements=true is required by golang-migrate's MySQL driver so it // can execute migration files containing multiple SQL statements in one call. // store.Open / buildDSN preserves unrecognised params, so this flag // survives the DSN round-trip through buildDSN. dsn := fmt.Sprintf("root:%s@tcp(127.0.0.1:%s)/%s?multiStatements=true", rootPwd, hostPort, dbName) // Wait until MySQL accepts connections (up to 60 s). deadline := time.Now().Add(60 * time.Second) for time.Now().Before(deadline) { ping := exec.CommandContext(ctx, "docker", "exec", containerName, "mysqladmin", "ping", "--silent") if err := ping.Run(); err == nil { break } time.Sleep(2 * time.Second) } // Verify connectivity. check := exec.CommandContext(ctx, "docker", "exec", containerName, "mysqladmin", "ping", "--silent") if err := check.Run(); err != nil { fmt.Fprintln(os.Stderr, "MySQL did not become ready within 60 s; skipping tests") os.Exit(0) } os.Setenv("PANGOLIN_TEST_DSN", dsn) os.Exit(m.Run()) }