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,97 @@
# `testing`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
This module provides the in-memory test harness for
`@suaveplan/api-db-worker-proxy`. It exposes `createInMemoryAdminDb`
— a deterministic `AdminDatabaseLike` backed by a `Map` — and
`createDbProxyHarness`, which pairs that adapter with a real
`MessageChannel`, a `createDbProxyServer` on one port, and a
`createDbProxyClient` on the other. Tests can drive the full
client-port-server-adapter chain end-to-end without a real DB and
without any mocking framework.
The harness exists in `src/testing/` (rather than `tests/`) so it
can be imported by downstream Stage-4 adapter packages from the
package's `"./testing"` subpath export.
## Features
- `createInMemoryAdminDb({ engine?, seed?, cursorDelayMs? })` — a
full `AdminDatabaseLike` implementation including `prepare`,
`transaction`, and a synthetic `SELECT-CURSOR *` that returns an
`AsyncIterable<MemoryRow>` for cursor streaming tests.
- `createDbProxyHarness(opts)` — bundles adapter + ports + server +
client into one disposable handle.
- Tiny command DSL (`INSERT <id>`, `DELETE <id>`, `SELECT *`,
`SELECT-CURSOR *`, `COUNT`, `SELECT-SCALAR`, `THROW`) chosen so
every code path in the proxy can be exercised without parsing real
SQL.
## Usage: Basic
```ts
import { createDbProxyHarness } from "@suaveplan/api-db-worker-proxy/testing";
const h = createDbProxyHarness({ seed: [["a", { v: 1 }]] });
try {
expect(await h.client.query("SELECT *")).toEqual([{ v: 1 }]);
} finally {
await h.client.close();
h.close();
}
```
## Usage: Advanced
To assert that cursor cancellation propagates, set a per-row delay
on the in-memory cursor and abort partway through:
```ts
const h = createDbProxyHarness({
seed: Array.from({ length: 100 }, (_, i) => [String(i), { i }]),
cursorDelayMs: 5,
});
try {
const ac = new AbortController();
let n = 0;
for await (const _row of h.client.cursor("SELECT-CURSOR *", undefined, {
signal: ac.signal,
})) {
if (++n >= 3) {
ac.abort();
break;
}
}
expect(n).toBe(3);
} finally {
await h.client.close();
h.close();
}
```
## API Reference
- `createInMemoryAdminDb(opts?): InMemoryAdminDb` — `AdminDatabaseLike`
plus `rows: ReadonlyMap<string, MemoryRow>` and `closed(): boolean`
for assertions.
- `createDbProxyHarness(opts?): DbProxyHarness` — `{ db, server,
client, ports, close() }`.
- `InMemoryAdminDbOptions` — `{ engine?, seed?, cursorDelayMs? }`.
## Implementation Notes
The in-memory adapter ignores parameter placeholders for `INSERT` /
`DELETE` and instead encodes the row key in the SQL string. This is
intentional — the harness exists to exercise the proxy plumbing, not
to validate a real query language. Concrete adapters (Stage 4) ship
their own conformance suites under their own engine semantics.
The harness's `close()` does NOT call `client.close()` so tests can
choose between observing graceful close (`await client.close()`) and
abrupt disconnect (`client.dispose()`, then `harness.close()`). Both
paths are first-class.