From ginkgo
Makes Ginkgo specs interruptible with SpecContext cancellation, timeouts, and graceful cleanup. For hanging specs, polling, streams, goroutines, or async testing with Gomega.
How this skill is triggered — by the user, by Claude, or both
Slash command
/ginkgo:timeouts-and-asyncThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Builds on the failure mental model in `ginkgo:overview` (failure is a panic Ginkgo recovers; a goroutine that fails needs `defer GinkgoRecover()`). Docs: <https://onsi.github.io/ginkgo/#spec-timeouts-and-interruptible-nodes> and <https://onsi.github.io/ginkgo/#patterns-for-asynchronous-testing>.
Builds on the failure mental model in ginkgo:overview (failure is a panic Ginkgo recovers; a goroutine that fails needs defer GinkgoRecover()). Docs: https://onsi.github.io/ginkgo/#spec-timeouts-and-interruptible-nodes and https://onsi.github.io/ginkgo/#patterns-for-asynchronous-testing.
A setup or subject node becomes interruptible simply by accepting a SpecContext (or plain context.Context) — Ginkgo supplies one automatically:
It("likes to sleep in", func(ctx context.Context) {
select {
case <-ctx.Done(): return // honor cancellation and exit promptly
case <-time.After(time.Hour): ...
}
}, NodeTimeout(time.Second))
On timeout or interrupt, Ginkgo cancels the context to tell the node to stop. Pass ctx down into every blocking call (libraryClient.SaveBook(ctx, book), exec.CommandContext(ctx, ...)) so the cancellation actually propagates. Only setup/subject nodes are interruptible — container nodes are not (they run at construction time).
ctx.Deadline() does NOT report the node's deadline. Ginkgo manages cancellation timing itself (to snapshot a progress report first), so it does not use a WithDeadline context. Trust <-ctx.Done(), not Deadline().SpecContext satisfies context.Context; you may wrap it (context.WithValue) and Ginkgo still cancels the result on time.ginkgo:decorators)| Decorator | Scope |
|---|---|
NodeTimeout(d) | Deadline for one interruptible node. |
SpecTimeout(d) | Deadline for the whole spec lifecycle — It only. |
GracePeriod(d) | How long Ginkgo waits after cancelling before leaking the node. |
SpecTimeout can only be more lenient than a node's NodeTimeout — it caps the sum of all nodes (BeforeEach+It+AfterEach); a per-node NodeTimeout inside it is always more stringent.SpecTimeout fires, Ginkgo cancels the current node, then runs AfterEach/AfterAll/DeferCleanup (each under its own NodeTimeout/grace). The timeout is a "mark failed" threshold, not a hard kill.Fail/AddReportEntry and pollute a later spec. Always make blocking code respond to ctx.Done().DeferCleanup/DescribeTable entries are interruptible too — give the cleanup/entry func a ctx first arg; don't capture and reuse the parent node's ctx (it's already cancelled by cleanup time).ginkgo --timeout=DURATION — suite-wide budget across all suites (default 1h).Abort("reason") — end the suite immediately from within a spec (programmatic interrupt).SIGINT/SIGTERM (^C) — interrupt: cancel the current interruptible node, run its cleanup + reporting nodes, skip the rest, exit failed. Escalation: a 2nd interrupt skips cleanup (still runs reporting); a 3rd bails immediately. To inspect a suite without stopping it, send SIGINFO/SIGUSR1 for a progress report → ginkgo:debugging-failures.Use Gomega's Eventually (polls until the matcher passes or it times out) and Consistently (polls and requires the matcher to hold the whole interval — the way to assert something doesn't happen). Three input shapes: bare values (channels, gbytes), functions returning (value[, error]), and functions taking a Gomega.
It("publishes a book", func(ctx SpecContext) {
buffer := gbytes.NewBuffer()
c := publisher.Publish(ctx, book, buffer) // pass ctx so it cancels cleanly
Eventually(ctx, buffer).Should(gbytes.Say(`Publish complete!`))
var result publisher.PublishResult
Eventually(ctx, c).WithTimeout(time.Second).Should(Receive(&result)) // poll, don't <-c
}, SpecTimeout(time.Second*30))
Propagate the spec deadline into the poll with .WithContext(ctx) or the positional Eventually(ctx, ...) — now a node timeout/interrupt makes the Eventually exit immediately instead of running its own clock. Gomega also auto-injects the context and .WithArguments(...) into a polled function whose first params are (ctx), so Eventually(client.Connect).WithContext(ctx).Should(Succeed()) works (pass the method reference, not client.Connect()).
g, never global ExpectPass a function taking func(g Gomega, ...) and assert with g.Expect so a failed poll retries instead of failing the spec outright:
Eventually(func(g Gomega, ctx SpecContext) { // g Gomega must be first
messages, err := gmail.Fetch(ctx, jane.EmailAddress)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(messages).To(ContainElement(WithTransform(subjectOf, Equal(want))))
}).WithContext(ctx).Should(Succeed()) // Succeed() = "no failures in the func"
Using the global Expect inside an Eventually defeats the retry — the first failure calls Ginkgo's Fail and the spec dies with no second attempt. The local g lets Eventually catch and re-poll.
It("repaginates", func() {
done := make(chan any)
go func() {
defer GinkgoRecover() // RULE 1
Expect(book.SetFontSize(28)).To(Succeed())
close(done)
}()
Eventually(done).Should(BeClosed()) // RULE 2: poll, don't block on <-done
})
Fail or a Gomega assertion needs defer GinkgoRecover(). Ginkgo can't recover a panic raised on a goroutine it didn't start — without this, one failed assertion crashes the entire suite. (Ginkgo's crash message reminds you.)close(done), a bare <-done blocks until the node times out instead of reporting the real failure. Eventually(done).Should(BeClosed()) lets the failure surface immediately.NodeTimeout, SpecTimeout, GracePeriod, PollProgressAfter) → ginkgo:decoratorsginkgo:debugging-failures--timeout and friends in a CI config → ginkgo:cinpx claudepluginhub onsi/ginkgo --plugin ginkgoProvides Gomega's Eventually and Consistently for polling asynchronous assertions in Go tests. Use when assertions depend on goroutines, channels, network calls, or eventual consistency.
Guides writing Ginkgo specs with container/subject/setup nodes, the 'declare in container, initialize in BeforeEach' rule, and reusable test helpers. Use when writing or reviewing Ginkgo tests.
Writes and reviews production-ready Go tests: table-driven tests, testify suites, parallel tests, fuzzing, goroutine leak detection, snapshot testing, and integration tests. Use when writing, reviewing, or debugging Go tests.