mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 19:17:10 +00:00
installs, builds and serves each implementation on its own port, then hands the campaign tool the same seed slice, step budget and generator for all of them, so a difference between implementations is not a difference in exploration. the generator and platform are fixed rather than exposed.
120 lines
2.8 KiB
Go
120 lines
2.8 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"syscall"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// bunSpawningAServer serves from a child process and then waits, which is the
|
|
// shape of `bun run preview`: the port belongs to something below the process
|
|
// the sweep started.
|
|
const bunSpawningAServer = `#!/bin/sh
|
|
port=""
|
|
previous=""
|
|
for argument in "$@"; do
|
|
if [ "$previous" = "--port" ]; then port="$argument"; fi
|
|
previous="$argument"
|
|
done
|
|
SWEEP_TEST_SERVE_PORT="$port" "%[1]s" &
|
|
wait
|
|
`
|
|
|
|
func TestServerStop_TakesTheProcessBelowItWithTheServer(t *testing.T) {
|
|
directory := t.TempDir()
|
|
testBinary, err := filepath.Abs(os.Args[0])
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
bunPath := writeScript(
|
|
t,
|
|
filepath.Join(directory, "stub-bun"),
|
|
fmt.Sprintf(bunSpawningAServer, testBinary),
|
|
)
|
|
port := freePortRange(t, 1)
|
|
target := implementation{Name: "impl-01", Directory: directory, Port: port}
|
|
|
|
// Background rather than the test context: only stop() may end this
|
|
// server, or a leak would be hidden by the context being cancelled.
|
|
running, err := startServer(
|
|
context.Background(),
|
|
config{bunPath: bunPath},
|
|
target,
|
|
filepath.Join(directory, "serve.log"),
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(
|
|
func() { syscall.Kill(-running.command.Process.Pid, syscall.SIGKILL) },
|
|
)
|
|
if err := running.waitReady(context.Background(), readinessURL(port)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
running.stop()
|
|
|
|
client := &http.Client{Timeout: time.Second}
|
|
deadline := time.Now().Add(5 * time.Second)
|
|
for time.Now().Before(deadline) {
|
|
response, err := client.Get(readinessURL(port))
|
|
if err != nil {
|
|
return
|
|
}
|
|
response.Body.Close()
|
|
time.Sleep(100 * time.Millisecond)
|
|
}
|
|
t.Fatalf(
|
|
"port %d is still served after stop(): the server below bun outlived the sweep and holds the port",
|
|
port,
|
|
)
|
|
}
|
|
|
|
func TestServerWaitReady_ReportsAServerThatExited(t *testing.T) {
|
|
directory := t.TempDir()
|
|
bunPath := writeScript(
|
|
t,
|
|
filepath.Join(directory, "stub-bun"),
|
|
"#!/bin/sh\necho 'port is already in use' >&2\nexit 1\n",
|
|
)
|
|
port := freePortRange(t, 1)
|
|
|
|
running, err := startServer(
|
|
context.Background(),
|
|
config{bunPath: bunPath},
|
|
implementation{
|
|
Name: "impl-01",
|
|
Directory: directory,
|
|
Port: port,
|
|
},
|
|
filepath.Join(directory, "serve.log"),
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer running.stop()
|
|
|
|
started := time.Now()
|
|
err = running.waitReady(context.Background(), readinessURL(port))
|
|
if err == nil {
|
|
t.Fatal(
|
|
"a server that exited should not be waited on until the start timeout",
|
|
)
|
|
}
|
|
if elapsed := time.Since(started); elapsed > 30*time.Second {
|
|
t.Errorf("waited %s for a server that had already exited", elapsed)
|
|
}
|
|
log, err := os.ReadFile(filepath.Join(directory, "serve.log"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if string(log) == "" {
|
|
t.Error("serve.log did not capture why the server exited")
|
|
}
|
|
}
|