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