domainkit/testing
The fakes, the recording transport, and the conformance runners that let host tests drive the real services instead of stubbing global fetch.
import { Testing } from "domainkit/testing";
| Namespace | Carries |
|---|---|
Testing |
provider, resolver, storage, principal, transport, fakeRecords, conformance |
VERSION |
The installed package version, so a test can assert what it is running against |
Fakes
| Value | Gives |
|---|---|
Testing.provider |
A provider definition with a token method, optional OAuth, over in-memory zones |
Testing.resolver |
A Resolver answering from those zones, or from a table you pass |
Testing.storage |
Storage.layerMemory, re-exported here for discoverability |
Testing.principal |
A fixed { ownerId, actorId } for tests that do not care which tenant |
Testing.transport |
The whole lifecycle behind a Transport.Interface, recording every call |
Testing.fakeRecords |
What the fake providers currently hold for a name |
/**
* A provider definition over in-memory zones, with a token method and optionally OAuth. Give each
* test its own zone: `Testing.provider` registers zones in one process-wide table that
* `Testing.resolver` reads.
*/
export const fake = Testing.provider({
zones: ["plans.example.com"],
records: [
{
zone: "plans.example.com",
record: DnsRecord.txt({ name: "_acme.plans.example.com", value: "acme-verify=7f3a" }),
},
],
oauth: true,
});Options
| Option | Effect |
|---|---|
id |
The provider id. Default fake |
name |
The name a customer reads. Default Fake <id> |
zones |
Zones the fake credential reaches. Default example.com |
labels |
The label each zone carries, keyed by zone. Default: the zone name |
accountLabel |
What the fake calls the account it issues credentials for. Default: none, so the label is null |
records |
Pre-existing records, to produce Noop and Conflict operations |
oauth |
Offer OAuth beside the token method; the fake redirects to callbackUrl immediately |
failWrite |
A predicate over the zero-based write index; a write fails when it returns true, to exercise a partial receipt |
nameservers |
Nameservers per zone, which Testing.resolver answers NS queries from |
nameserverSuffixes |
Suffixes the fake declares as Definition.nameservers, so Connect.discover names it as a host |
The recording transport
/**
* The whole lifecycle behind a `Transport.Interface`, over an in-memory server, recording every
* call. Declare fewer capabilities to render a UI against a host that mounted only part of the
* group.
*/
export const transport = Testing.transport({ capabilities: ["connection", "provisioning"] });
export const callsSoFar = () => transport.calls.map((call) => call.method);Testing.transport mounts domainkit/server over memory storage and a fake provider, then wraps
Transport.fromFetch around that handler. calls holds { method, input } for every call, oldest
first, where method reads connection.inspect, provisioning.approve, and so on. There is
nothing to dispose.
Conformance
| Runner | Checks |
|---|---|
Testing.conformance.storage |
Tenant isolation and the one deliberate exception to it, apply leases, exactly-once continuations, revocation recovery, locks |
Testing.conformance.provider |
Create and read back, exact no-op, conflict, stale plan, partial apply, cleanup, rejected token |
/**
* Register every `Storage` invariant with your test runner: tenant isolation, apply leases,
* exactly-once continuations, revocation recovery, and lock semantics. Both shipped
* implementations pass this suite, so a host can swap them without a behaviour change.
*/
export const registerStorageCases = (
layer: Layer.Layer<Storage.Service, unknown>,
it: (name: string, run: () => Promise<void>) => void,
) => Testing.conformance.storage(layer, { it });/**
* A provider author runs this against a real account before shipping: create and read back, exact
* no-op, conflict, stale plan, and partial apply, all through the same services hosts use. Every
* record it creates carries the prefix and is removed again.
*/
export const check = (definition: Provider.Definition, token: string, zone: string) =>
Testing.conformance.provider(
definition,
{ secret: Redacted.make(token), context: { apiKey: token } },
zone,
{ prefix: "acme-conformance" },
);
export const run = (definition: Provider.Definition, token: string) =>
Effect.runPromise(check(definition, token, "example.com"));The provider runner writes to a real account. Every record it creates carries the prefix and is
removed again, so point it at a zone you own and can inspect.
The rejected-token case authenticates with every required token field set to an empty secret and
expects Unauthenticated. A token method with a required non-secret field, such as a region
selector, needs rejectedToken: the values the provider rejects, keyed by field name.