oficina / calendar Public

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>
Paul Campbell pushed by paul@keymail.dev b9b6c916c4f7bc8942ba011a7bdf38ea017736c9 parent e60b239
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