Files
spinifex/tests/integration/main_test.go
T
Josh Siviour 7d2106bfb5 fix(test): reclaim fixture scratch dirs stranded in /tmp (#598)
* fix(test): reclaim fixture scratch dirs stranded in /tmp

The shared predastore fixture and the integration suite's JetStream store both
outlive any individual test, so neither can use t.TempDir() - the first test to
finish would delete files the daemon still has open. Both used os.MkdirTemp
with nothing removing the directory afterwards, so every run stranded one, and
a run killed by a timeout panic or the OOM killer stranded it regardless. /tmp
is a tmpfs on the dev boxes, so the leak is charged to RAM and swap, and
swapped tmpfs pages have no backing store to be dropped to.

Add a shared sweep that reclaims a directory only when it matches both the
prefix and an age no live run can reach. The NATS store also has a clean
shutdown point, so TestMain now removes it outright and the sweep is left to
cover the killed-run case. The predastore daemon runs until the process exits
and has no such point, so the sweep is its only mechanism.

* chore: repin viperblock to the dev tip carrying the scratch-dir fix

Test-only upstream change, so the build output is unchanged - this keeps
go.mod aligned with the submodule pointer rather than delivering a fix.
2026-07-29 14:57:50 +10:00

53 lines
1.9 KiB
Go

//go:build integration
package integration
import (
"fmt"
"os"
"testing"
)
// TestMain amortises the embedded NATS+JetStream server across every test in
// this package instead of each StartGateway call booting (and tearing down)
// its own, for two reasons:
//
// 1. spinifex/gateway/ec2/instance's ClientTokenStore is a process-wide
// sync.Once singleton bound to whichever *nats.Conn first initialises it.
// In production a gateway holds one long-lived connection for its whole
// life, so the singleton is correct there. But this test binary runs
// every test in one process, so a per-test embedded server closes that
// first connection the moment its owning test ends — every later
// RunInstances-calling test then inherits a KV handle bound to a dead
// connection. Keeping one server, and never closing an individual
// test's connection early, keeps whichever connection wins the
// singleton alive for the life of the whole binary.
// 2. It cuts per-test startup cost: standing up an embedded JetStream
// server is the dominant fixed cost of StartGateway.
//
// Per-test state isolation is preserved despite the shared server — see the
// accountAuthenticator doc comment in shared_nats.go for how.
func TestMain(m *testing.M) {
h, err := startSharedNATS()
if err != nil {
fmt.Fprintln(os.Stderr, "tests/integration: failed to start shared NATS server:", err)
os.Exit(1)
}
sharedNATSHarness = h
code := m.Run()
// Connections are closed here, together, rather than per-test: closing
// one early can kill the connection the clienttoken singleton latched
// onto (see above), so every connection's lifetime is pinned to the
// whole binary rather than to any single test.
h.closeAll()
h.srv.Shutdown()
h.srv.WaitForShutdown()
// Only reached on a clean exit; a killed run leaves the store for the
// sweep in startSharedNATS to reclaim.
h.removeStore()
os.Exit(code)
}