check / check (push) Waiting to run
These tests fail until the change that follows: runApp and WaitForProcessing do not exist yet, the server has no shutdowner, and a Sentry DSN that cannot be used exits the process from a goroutine instead of failing the server's start hook. runApp must return the exit code a shutdown request carries, 0 without one, and 1 when the app fails to start or stop. A listen error must ask fx to shut down with exit code 1. WaitForProcessing must wait for an image being processed and report it when its context ends first. Model: opus-5-5
97 lines
2.4 KiB
Go
97 lines
2.4 KiB
Go
package server
|
|
|
|
import (
|
|
"log/slog"
|
|
"net"
|
|
"testing"
|
|
"time"
|
|
|
|
"go.uber.org/fx"
|
|
"go.uber.org/fx/fxtest"
|
|
|
|
"sneak.berlin/go/pixa/internal/config"
|
|
"sneak.berlin/go/pixa/internal/globals"
|
|
"sneak.berlin/go/pixa/internal/logger"
|
|
)
|
|
|
|
// shutdownRecorder is an fx.Shutdowner that sends the options of each
|
|
// shutdown request on requests.
|
|
type shutdownRecorder struct {
|
|
requests chan []fx.ShutdownOption
|
|
}
|
|
|
|
func (r shutdownRecorder) Shutdown(opts ...fx.ShutdownOption) error {
|
|
r.requests <- opts
|
|
|
|
return nil
|
|
}
|
|
|
|
// TestSentryInitFailureFailsStartup checks that a Sentry DSN that cannot be
|
|
// used makes the server's start hook fail, so fx stops what has already
|
|
// started, instead of the process exiting from a goroutine.
|
|
func TestSentryInitFailureFailsStartup(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
lc := fxtest.NewLifecycle(t)
|
|
|
|
log, err := logger.New(lc, logger.Params{Globals: &globals.Globals{}})
|
|
if err != nil {
|
|
t.Fatalf("logger.New() error = %v", err)
|
|
}
|
|
|
|
_, err = New(lc, Params{
|
|
Logger: log,
|
|
Globals: &globals.Globals{Appname: "pixad"},
|
|
Config: &config.Config{SentryDSN: "not-a-dsn"},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("New() error = %v", err)
|
|
}
|
|
|
|
err = lc.Start(t.Context())
|
|
t.Logf("Start() error = %v", err)
|
|
|
|
if err == nil {
|
|
t.Fatal("Start() error = nil, want the Sentry initialization error")
|
|
}
|
|
}
|
|
|
|
// TestListenErrorRequestsShutdownWithExitCode1 occupies the server's port
|
|
// and checks that the listen error asks fx to shut down with exit code 1.
|
|
func TestListenErrorRequestsShutdownWithExitCode1(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
busy, err := (&net.ListenConfig{}).Listen(t.Context(), "tcp", ":0")
|
|
if err != nil {
|
|
t.Fatalf("Listen() error = %v", err)
|
|
}
|
|
|
|
t.Cleanup(func() { _ = busy.Close() })
|
|
|
|
addr, ok := busy.Addr().(*net.TCPAddr)
|
|
if !ok {
|
|
t.Fatalf("listener address %v is not a TCP address", busy.Addr())
|
|
}
|
|
|
|
requests := make(chan []fx.ShutdownOption, 1)
|
|
s := &Server{
|
|
log: slog.New(slog.DiscardHandler),
|
|
config: &config.Config{Port: addr.Port},
|
|
shutdowner: shutdownRecorder{requests: requests},
|
|
}
|
|
s.httpServer = s.newHTTPServer()
|
|
|
|
go s.serveUntilShutdown()
|
|
|
|
select {
|
|
case opts := <-requests:
|
|
t.Logf("shutdown options = %v", opts)
|
|
|
|
if len(opts) != 1 || opts[0] != fx.ExitCode(1) {
|
|
t.Errorf("shutdown options = %v, want [fx.ExitCode(1)]", opts)
|
|
}
|
|
case <-time.After(5 * time.Second):
|
|
t.Fatal("no shutdown was requested after the listen error")
|
|
}
|
|
}
|