first commit
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
# `createInMemoryEventsBusHarness`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
The testing harness packages an {@link InMemoryEventsBus} with a
|
||||
`waitFor` helper for tests that publish asynchronously and need to
|
||||
assert on history once the publish has settled. It is the standard
|
||||
shape downstream plugin tests will import to avoid hand-rolling a
|
||||
poll-with-timeout loop for every assertion. Importing
|
||||
`@suaveplan/api-events-in-memory/testing` returns a tightly-scoped
|
||||
surface that won't leak production-only construction details into test
|
||||
code.
|
||||
|
||||
## Features
|
||||
|
||||
- Fresh bus per harness — call `createInMemoryEventsBusHarness()` once
|
||||
per test, `dispose()` at teardown; no shared state across cases.
|
||||
- `waitFor(predicate, { timeoutMs, intervalMs })` polls until the
|
||||
predicate holds or the timeout elapses. The default timeout (1 s)
|
||||
and interval (5 ms) work for the overwhelming majority of unit
|
||||
tests; override only when integrating with slower infra.
|
||||
- Fast path: if the predicate already holds at call time the function
|
||||
resolves on the next microtask without sleeping.
|
||||
- Reject-on-timeout includes the requested `timeoutMs` in the message
|
||||
so test failures are easy to triage.
|
||||
- All options forwarded verbatim to {@link createInMemoryEventsBus}, so
|
||||
the harness inherits the strict-throw history-overflow policy and
|
||||
the Zod-validated options boundary.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { createInMemoryEventsBusHarness } from "@suaveplan/api-events-in-memory/testing";
|
||||
|
||||
const harness = createInMemoryEventsBusHarness();
|
||||
const { bus, waitFor } = harness;
|
||||
|
||||
bus.on("user.signup", async () => {
|
||||
await Promise.resolve();
|
||||
bus.publish("welcome.email.queued", { to: "u@example.com" });
|
||||
});
|
||||
|
||||
await bus.publishAsync("user.signup", { userId: "u1" });
|
||||
await waitFor((b) => b.published("welcome.email.queued").length === 1);
|
||||
|
||||
console.log(bus.published("welcome.email.queued"));
|
||||
harness.dispose();
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Tighten the deadline for a fast unit test, or extend it for an
|
||||
integration test that sits behind a real database:
|
||||
|
||||
```ts
|
||||
import { createInMemoryEventsBusHarness } from "@suaveplan/api-events-in-memory/testing";
|
||||
|
||||
const harness = createInMemoryEventsBusHarness({ maxHistoryPerTopic: 50 });
|
||||
try {
|
||||
await harness.waitFor(
|
||||
(bus) => bus.published("payment.captured").length >= 3,
|
||||
{ timeoutMs: 5000, intervalMs: 25 }
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("payment.captured fan-out stalled:", error);
|
||||
throw error;
|
||||
} finally {
|
||||
harness.dispose();
|
||||
}
|
||||
```
|
||||
|
||||
`dispose()` is idempotent — calling it twice in `afterEach` after a
|
||||
mid-test failure is safe.
|
||||
|
||||
## API Reference
|
||||
|
||||
- `createInMemoryEventsBusHarness(options?): InMemoryEventsBusHarness` — factory.
|
||||
- `InMemoryEventsBusHarness.bus: InMemoryEventsBus` — the wrapped bus.
|
||||
- `InMemoryEventsBusHarness.waitFor(predicate, options?): Promise<void>` — poll-with-timeout.
|
||||
- `InMemoryEventsBusHarness.dispose(): void` — tear down the bus.
|
||||
- `WaitForPredicate = (bus: InMemoryEventsBus) => boolean`.
|
||||
- `WaitForOptions = { timeoutMs?: number; intervalMs?: number }`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`waitFor` uses `setTimeout` rather than `setImmediate`/`queueMicrotask`
|
||||
because real tests need to interleave with timers managed by the
|
||||
runtime; busy-looping on microtasks starves them. The harness owns
|
||||
`dispose` rather than asking the caller to call `bus.dispose()`
|
||||
directly so that future versions can add additional resources
|
||||
(meter providers, span recorders) without breaking the test surface.
|
||||
Reference in New Issue
Block a user