package sww import ( "log/slog" "net/url" "testing" "testing/synctest" "time" "sneak.berlin/go/smallwebwaf/internal/remotelog" ) // The stop's tests run in a synctest bubble, where the time package runs // on a clock of the test's own, so that how long the stop takes can be // told exactly. The sender is held up by its process log, not by the // network: a goroutine of the bubble that waits on the network keeps that // clock from moving on. func TestStopWaitsForTheSenderToFinish(t *testing.T) { t.Parallel() synctest.Test(t, func(t *testing.T) { took := stopHeldSender(t, time.Second) if took != time.Second { t.Errorf("the stop took %s, want the second the sender took", took) } }) } func TestStopWaitsForTheSenderAtMostTwoSeconds(t *testing.T) { t.Parallel() synctest.Test(t, func(t *testing.T) { took := stopHeldSender(t, time.Minute) if took != 2*time.Second { t.Errorf("the stop took %s, want 2s", took) } }) } // heldLog holds each line written to it until it is closed. type heldLog chan struct{} // Write waits until the log is closed. func (l heldLog) Write(p []byte) (int, error) { <-l return len(p), nil } // stopHeldSender starts sending to an endpoint the sender cannot connect // to, holds the sender as it logs that failure until release has passed, // stops the sending, and returns how long the stop took. It returns once // the sender has ended, as a bubble must. func stopHeldSender(t *testing.T, release time.Duration) time.Duration { t.Helper() log := make(heldLog) sender := remotelog.New(remotelog.Params{ // No port is 65536, so each attempt to connect fails at once, // before it reaches the network. URL: &url.URL{Scheme: remotelog.SchemeTCP, Host: "127.0.0.1:65536"}, Buffer: 1, }) stopSending := startSending(t.Context(), sender, slog.New(slog.NewJSONHandler(log, nil))) synctest.Wait() time.AfterFunc(release, func() { close(log) }) stopped := time.Now() stopSending() took := time.Since(stopped) time.Sleep(release) synctest.Wait() return took }