|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114 |
- # Webhook Testing Risk Guidance
-
- ## Principle
-
- Webhook integration points are high-risk boundaries — they represent asynchronous side effects that cross service boundaries. A missing or malformed webhook means a downstream system never received its trigger. Default risk level: **P2 × I3** (medium probability, high impact = Risk Score 6) → must be covered by integration tests.
-
- ## When Webhook Tests Are Required
-
- Webhook tests are **required** (not optional) when:
-
- | Condition | Rationale |
- | ------------------------------------------------------------------ | ---------------------------------------------------------------------- |
- | Application publishes events to external subscribers | External consumers depend on correct payload shape and delivery timing |
- | Event-driven architecture (Kafka/SQS/event bus → webhook delivery) | The delivery pipeline is a risk boundary; delivery failures are silent |
- | Payment, order, or notification side effects | Business-critical; missed webhooks = missed transactions |
- | Integration with third-party services via webhooks | Breaking payload changes won't surface in unit or component tests |
- | Any async side effect that a consumer polls-on or reacts-to | Polling tests (`recurse`) can mask webhook delivery failures entirely |
-
- ## Risk Scoring
-
- ```
- Risk = Probability × Impact
-
- Probability factors (P1–P3):
- P1 (low): Webhook system is mature, well-tested, no history of failures
- P2 (medium): Kafka pipeline, multiple consumers, new integrations
- P3 (high): New delivery mechanism, external third-party webhooks, no retry logic
-
- Impact factors (I1–I3):
- I1 (low): Non-critical notifications (e.g. audit logs)
- I2 (medium): Feature-level side effects (e.g. search index updates)
- I3 (high): Business-critical events (payments, orders, compliance)
- ```
-
- Default webhook integrations: **P2 × I3 = 6** → High → must be tested.
-
- ## What a Complete Webhook Test Looks Like
-
- A complete webhook test covers:
-
- 1. **Happy path**: Action fires → webhook arrives with correct payload
- 2. **Sequential events (drain pattern)**: Preceding event drained before asserting on next
- 3. **Parallel isolation**: Template scoped by entity ID — workers don't cross-contaminate
- 4. **Timeout/error shape**: `WebhookTimeoutError` tested for negative path coverage
- 5. **Cleanup verification**: Fixture auto-cleans; no leaked webhooks after test
-
- **Minimal complete example** (from playwright-utils E2E suite):
-
- ```typescript
- // Template factories scoped by ID — parallel safety
- const movieCreated = (movieId: number) =>
- webhookTemplate<{ event: string; data: { id: number } }>('movie.created')
- .matchField('event', 'movie.created')
- .matchField('data.id', movieId)
- .withTimeout(15_000)
- .withInterval(500)
- .build();
-
- const movieDeleted = (movieId: number) =>
- webhookTemplate<{ event: string; data: { id: number } }>('movie.deleted')
- .matchField('event', 'movie.deleted')
- .matchField('data.id', movieId)
- .withTimeout(15_000)
- .withInterval(500)
- .build();
-
- test('movie deletion triggers a webhook with correct payload', async ({ authToken, addMovie, deleteMovie, webhookRegistry }) => {
- const movie = generateMovieWithoutId();
- const { body: createResponse } = await addMovie(authToken, movie);
- const movieId = createResponse.data.id;
-
- // Drain: consume the create webhook before testing the delete path
- await webhookRegistry.waitFor(movieCreated(movieId));
-
- await deleteMovie(authToken, movieId);
- const webhook = await webhookRegistry.waitFor(movieDeleted(movieId));
-
- expect(webhook.body).toMatchObject({
- event: 'movie.deleted',
- data: { id: movieId, name: movie.name },
- });
- });
- ```
-
- ## Common Failure Patterns
-
- | Failure pattern | Root cause | How the module addresses it |
- | -------------------------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------- |
- | Test passes but webhook never verified | Test asserted on status endpoint, not delivery | `waitFor` forces assertion on actual webhook arrival |
- | Flaky under `fullyParallel: true` | `full-reset` cleanup deletes another worker's webhooks | `matched-only` strategy — only matched webhooks are deleted |
- | Timeout gives no useful information | No payload inspection on failure | `WebhookTimeoutError.receivedWebhooks` snapshot |
- | Template matches wrong test's webhook | Template not scoped by entity ID | Template factories accept ID parameter; `matchPredicate` for complex scoping |
- | Test hangs at 30s default timeout | Webhook not arriving; pipeline is slow | Use `withTimeout()` and `withInterval(500)` per template |
- | Journal grows unbounded | No cleanup strategy configured | Configure `cleanupStrategy` in `webhookConfig`; fixture auto-cleans |
-
- ## Risk Mitigation Checklist (for TA assessment)
-
- When a system uses webhooks, verify the test suite covers:
-
- - [ ] Happy path for each event type that has an external subscriber
- - [ ] Template factories scoped by entity ID (parallel-safe)
- - [ ] Drain pattern applied to all sequential event assertions
- - [ ] Cleanup strategy matches provider capability: `matched-only` for providers that support `deleteById` (e.g. WireMock); `full-reset` with serial execution or an isolated provider instance per worker for MockServer/Mockoon
- - [ ] Timeout values appropriate for the delivery pipeline latency (Kafka pipelines need 15s+)
- - [ ] `WebhookTimeoutError` imported and tested in negative path coverage
- - [ ] Mock server (WireMock/MockServer/Mockoon) in Docker Compose / test infra
-
- ## Related Fragments
-
- - `webhook-testing-fundamentals.md` — Why webhook tests are hard
- - `webhook-module-setup.md` — Fixture wiring for each provider
- - `webhook-template-matchers.md` — Template and matcher patterns
- - `risk-governance.md` — Risk scoring framework
- - `probability-impact.md` — P×I scale definitions
|