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") } }