oficina / sheets 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 e54dae7178b84b689a0b1c3c3fae6154e17fbdd7 parent b80fa0d
1 file changed, +25 −6
  • internal/sheets/mailqueue_test.go +25 −6

CI passed — run details

CI log
amadan: .amadan/timeout: this job's budget is 15m0s (this runner allows up to 30m0s)
=== vet ===
CGO_ENABLED=0 go vet ./...
CGO_ENABLED=0 go vet -tags browser ./internal/sheets/
=== fmt ===
=== build ===
CGO_ENABLED=0 go build ./...
=== migrations ===
go tool rastrillo migration check
rastrillo migration check: models and migrations agree
=== go-test ===
CGO_ENABLED=0 go test -count=1 ./...
?   	amadan.net/oficina/sheets/cmd/sheets	[no test files]
ok  	amadan.net/oficina/sheets/internal/guardstest	0.098s
ok  	amadan.net/oficina/sheets/internal/sheets	4.394s
ok  	amadan.net/oficina/sheets/internal/sheets/calc	0.012s
ok  	amadan.net/oficina/sheets/internal/sheets/format	0.003s
?   	amadan.net/oficina/sheets/internal/sheets/keymailtest	[no test files]
ok  	amadan.net/oficina/sheets/internal/sheets/live	0.054s
ok  	amadan.net/oficina/sheets/internal/sheets/seal	0.002s
ok  	amadan.net/oficina/sheets/internal/sheets/sealtest	0.003s
ok  	amadan.net/oficina/sheets/internal/sheets/store	3.274s
ok  	amadan.net/oficina/sheets/internal/sheets/xlsx	0.355s
?   	amadan.net/oficina/sheets/tools/gencorpus	[no test files]
=== race ===
go test -race -count=1 ./internal/sheets/store/... ./internal/sheets/live/...
ok  	amadan.net/oficina/sheets/internal/sheets/store	55.363s
ok  	amadan.net/oficina/sheets/internal/sheets/live	1.067s
diff --git a/internal/sheets/mailqueue_test.go b/internal/sheets/mailqueue_test.go
index 0a8ab71..eb4c471 100644
--- a/internal/sheets/mailqueue_test.go
+++ b/internal/sheets/mailqueue_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