first commit
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
# `testing` — `createInMemoryBrokerHarness`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
This module exposes a small, opinionated test harness around the
|
||||
in-memory broker. It removes the boilerplate of wiring an observer
|
||||
subscription and a teardown helper, so most tests collapse to "build
|
||||
harness, publish, assert on `received`, dispose". The harness deliberately
|
||||
shares the production broker — there is no separate "fake" broker hiding
|
||||
underneath — which means every assertion exercises the real fan-out
|
||||
pipeline and every regression caught in tests is one that would also
|
||||
manifest in production single-host deployments.
|
||||
|
||||
The harness lives under `./testing` (not the main entry) so consumer
|
||||
production bundles never pay for the helper machinery; only test bundles
|
||||
import this subpath.
|
||||
|
||||
## Features
|
||||
|
||||
- `createInMemoryBrokerHarness(opts?)` factory that returns a
|
||||
ready-to-use bundle of `{ broker, received, observerSubscription,
|
||||
publishOne, dispose }`.
|
||||
- A pre-registered observer subscription on the harness's default topic
|
||||
(`"test.events"`) plus an extensible `extraTopics` list so tests that
|
||||
probe the registry shape don't need to subscribe manually.
|
||||
- `publishOne(payload, opts?)` convenience that materialises a full
|
||||
`BrokerEnvelope` from a payload, defaulting `topic` and `tenant` from
|
||||
the harness configuration.
|
||||
- `dispose()` is idempotent and tolerant of partial teardown — it can
|
||||
be called from `afterEach` even if the test itself disposed the broker
|
||||
directly.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { describe, expect, it } from "@suaveplan/testing/runner";
|
||||
import { createInMemoryBrokerHarness } from "@suaveplan/api-ws-broker-in-memory/testing";
|
||||
|
||||
describe("broker harness", () => {
|
||||
it("captures every published envelope", { timeout: 5000 }, async () => {
|
||||
const harness = createInMemoryBrokerHarness<{ id: number }>();
|
||||
try {
|
||||
await harness.publishOne({ id: 1 });
|
||||
await harness.publishOne({ id: 2 });
|
||||
expect(harness.received.map((e) => e.payload.id)).toEqual([1, 2]);
|
||||
} finally {
|
||||
harness.dispose();
|
||||
}
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { describe, expect, it } from "@suaveplan/testing/runner";
|
||||
import type { TenantId } from "@suaveplan/api-types/tenant";
|
||||
import { createInMemoryBrokerHarness } from "@suaveplan/api-ws-broker-in-memory/testing";
|
||||
|
||||
describe("broker harness — await-all + multi-topic", () => {
|
||||
it("delivers across additional topics", { timeout: 5000 }, async () => {
|
||||
const harness = createInMemoryBrokerHarness<string>({
|
||||
broker: { deliveryMode: "await-all" },
|
||||
topic: "orders",
|
||||
tenant: "t1" as unknown as TenantId,
|
||||
extraTopics: ["audit", "billing"],
|
||||
});
|
||||
try {
|
||||
await harness.publishOne("ok", { topic: "orders" });
|
||||
// The probe subscribers on audit/billing keep them
|
||||
// registered for `subscriberCount` introspection.
|
||||
expect(harness.broker.subscriberCount("orders")).toBe(1);
|
||||
expect(harness.broker.subscriberCount("audit")).toBe(1);
|
||||
expect(harness.received).toHaveLength(1);
|
||||
expect(harness.received[0]?.payload).toBe("ok");
|
||||
} finally {
|
||||
harness.dispose();
|
||||
}
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `createInMemoryBrokerHarness<Payload>(opts?): InMemoryBrokerHarness<Payload>`
|
||||
- `interface CreateInMemoryBrokerHarnessOptions<Payload>` —
|
||||
`{ broker?, topic?, tenant?, extraTopics?, initialPayload? }`.
|
||||
- `interface InMemoryBrokerHarness<Payload>` —
|
||||
`{ broker, received, observerSubscription, publishOne, dispose }`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The harness is deliberately tiny — it intentionally does NOT
|
||||
attempt to drive the `runWsEngineConformance` harness, because that
|
||||
one expects a full `(bus, registry, broker, makeConnection)` tuple
|
||||
while this package only owns the broker leg. The package's
|
||||
conformance test in `tests/conformance/` runs broker-shaped type
|
||||
assertions instead, which is the right scope for a broker-only
|
||||
package.
|
||||
- `extraTopics` register a no-op subscriber so the topic survives in
|
||||
the registry for introspection (`subscriberCount(topic) === 1`).
|
||||
Without a probe subscriber the in-memory broker reaps zero-size
|
||||
topic entries from its `Map` and `subscriberCount` would report 0,
|
||||
which is the correct production behaviour but inconvenient when a
|
||||
test wants to assert "this topic exists right now".
|
||||
- The harness wraps the observer unsubscribe and broker dispose in
|
||||
separate `try`/`catch` blocks so an out-of-order teardown (test
|
||||
disposes the broker, then `afterEach` calls `harness.dispose()`)
|
||||
does not surface stale `BROKER_DISPOSED` errors.
|
||||
Reference in New Issue
Block a user