Queue-full test: read the drop from the worker's log, not a counter it may have reset
The worker swaps the drop counter to zero when it logs it, so reading the counter raced the worker taking its first link. The test now releases the stalled log sink, closes the queue and requires the "links dropped" line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 file changed,
+25
−6
internal/calendar/signin_mail_test.go+25 −6
CI passed — run details
CI log
=== vet === go vet ./... go vet -tags browser ./internal/calendartest/ === fmt === === test === go test ./... ? amadan.net/oficina/calendar/cmd/calendar [no test files] ok amadan.net/oficina/calendar/internal/calendar 6.047s ? amadan.net/oficina/calendar/internal/calendar/icons [no test files] ? amadan.net/oficina/calendar/internal/calendar/keymailtest [no test files] ? amadan.net/oficina/calendar/internal/calendar/seal [no test files] ok amadan.net/oficina/calendar/internal/calendartest (cached) ok amadan.net/oficina/calendar/internal/guardstest 0.167s ? amadan.net/oficina/calendar/scripts/writeestilo [no test files] === migration-check === go tool rastrillo migration check rastrillo migration check: models and migrations agree
diff --git a/internal/calendar/signin_mail_test.go b/internal/calendar/signin_mail_test.go| index ef87d73..8f36591 100644 |
| --- a/internal/calendar/signin_mail_test.go |
| +++ b/internal/calendar/signin_mail_test.go |
| @@ -5,18 +5,27 @@ import ( |
| "fmt" |
| "log/slog" |
| "runtime" |
| + "strings" |
| "sync" |
| "testing" |
| "time" |
| ) |
| // stuckHandler is a slog handler whose every write blocks until release |
| -// is closed: a log sink that has stalled, as a full pipe does. |
| -type stuckHandler struct{ release chan struct{} } |
| +// is closed: a log sink that has stalled, as a full pipe does. It |
| +// remembers what it was finally allowed to write. |
| +type stuckHandler struct { |
| + release chan struct{} |
| + mu *sync.Mutex |
| + msgs *[]string |
| +} |
| func (h stuckHandler) Enabled(context.Context, slog.Level) bool { return true } |
| -func (h stuckHandler) Handle(context.Context, slog.Record) error { |
| +func (h stuckHandler) Handle(_ context.Context, r slog.Record) error { |
| <-h.release |
| + h.mu.Lock() |
| + *h.msgs = append(*h.msgs, r.Message) |
| + h.mu.Unlock() |
| return nil |
| } |
| func (h stuckHandler) WithAttrs([]slog.Attr) slog.Handler { return h } |
| @@ -47,7 +56,9 @@ func TestMailQueueSendNeverBlocksWhenFull(t *testing.T) { |
| } |
| return nil |
| }) |
| - q := newMailQueue(stuck, everyone, nil, slog.New(stuckHandler{release})) |
| + var mu sync.Mutex |
| + var msgs []string |
| + q := newMailQueue(stuck, everyone, nil, slog.New(stuckHandler{release, &mu, &msgs})) |
| t.Cleanup(func() { free(); q.close() }) |
| done := make(chan struct{}) |
| @@ -64,9 +75,17 @@ func TestMailQueueSendNeverBlocksWhenFull(t *testing.T) { |
| <-done |
| t.Fatal("Send blocked with the queue full: the request path waited on the queue or the log") |
| } |
| - if n := q.dropped.Load(); n == 0 { |
| - t.Fatal("no drop was counted with twice the queue's depth sent") |
| + // The drops are the worker's to log, once it is free to. |
| + free() |
| + q.close() |
| + mu.Lock() |
| + defer mu.Unlock() |
| + for _, m := range msgs { |
| + if strings.Contains(m, "links dropped") { |
| + return |
| + } |
| } |
| + t.Fatalf("no drop was logged with twice the queue's depth sent; the log has %q", msgs) |
| } |
| // TestMailQueueQueuesEveryAddress: the queue's fill level must not |