Hunt for handler panics¶
The module recovers a handler panic at the goroutine it owns and restarts the subscription under its own policy. That is a backstop, and a backstop is not a strategy. This is the strategy.
That is the whole integration. test.Handler runs your handler against every case in the corpus and
fails the test if any of them panics.
Add a fuzz target¶
func FuzzMyHandler(f *testing.F) {
for _, c := range test.Corpus() {
f.Add(c.Delivery.Event.Data)
}
f.Fuzz(func(t *testing.T, data []byte) {
test.Fuzz(t, myHandler, data)
})
}
The corpus seeds it, so the toolchain starts from inputs known to be awkward rather than from nothing.
What the corpus contains, and why these¶
The panics that actually happen in a message handler are predictable, which is what makes enumerating them once here worthwhile instead of rediscovering them in every consumer.
| Case | The mistake it finds |
|---|---|
Data is nil, or empty |
indexing or unmarshalling without checking |
Data is not JSON at all |
assuming the content type was honoured |
Data is JSON but not an object |
json.Unmarshal into a struct, then dereferencing |
Data is an object with none of the expected fields |
dereferencing a pointer field that stayed nil |
| a payload far larger than expected | a fixed-size buffer, or an allocation nobody bounded |
| only the required attributes set | reading Subject or DataContentType unconditionally |
| a nil extension map | writing to Extensions without allocating it |
Every one is a shape a real broker can deliver. Nothing in the corpus is impossible, only unwelcome.
A returned error is not a failure¶
test.Handler fails on a panic and not on a returned error.
That distinction is the one the whole module rests on. A handler returning an error is the contract working: the error is returned, counted and logged, and the message is not retried. A handler panicking is a defect. A harness that conflated them would report a working handler as broken.
It proves nothing, and that is not modesty¶
A corpus and a fuzzer are testing, not verification. Passing means no panic was found on those inputs, never that your handler is panic-free.
That wording is deliberate. If this helper let somebody believe otherwise, the module's panic counter would read as impossible rather than as unexpected, and a number nobody believes is a number nobody watches.
Where the untrusted input usually is¶
Anywhere the payload originates outside your own service. A bot processing chat messages, a gateway accepting webhooks, an ingest path reading somebody else's export. Those are the handlers worth fuzzing, and the reason this corpus is in the module rather than in the one consumer that thought of it.