first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -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.