How to test a surface
directLink is the identity transport: it wraps the served router and invokes
handlers in-process, with no socket and no subprocess. That makes it the tool for
testing a surface — the client you drive in the test is the exact typed client a
socket consumer would hold.
Build an in-process client
Implement the surface, flatten the fragment, and wrap it in directLink. Pass
it bare — directLink is the one link surfaceClient accepts without a watchdog,
because its transport is always-live.
function makeTestClient() {
const { router } = implementSurface(surface, {
channel: inMemoryChannelByName(),
cells: { load: { store: inMemoryStore(ZERO) } },
collections: {
processes: {
readAll: () => table,
upsert: (pid, proc) => {
table.set(pid, proc);
},
remove: (pid) => {
table.delete(pid);
},
},
},
streams: {
nodeLog: {
source: async function* (nodeId) {
yield { kind: "snapshot", text: `opened ${nodeId}`, done: false };
},
},
},
events: { autosave: {} },
procedures: {
proc: {
kill: async ({ input, ctx }) => {
ctx.collections.processes.remove(input.pid);
return { ok: true };
},
},
},
});
// The raw typed client — the wire client, in-process. `implementSurface`
// already returns a ready `{ router }`, so no re-wrap; pass it bare, because
// directLink is the one link `surfaceClient` accepts without a watchdog.
return directLink<typeof surface.contract>(router);
}
Assert on a snapshot
A cell or stream read yields a snapshot first, then deltas, as an async
iterable. To assert on the current value, take the first frame. firstFrameOrThrow
(@kolu/surface/first-frame) does exactly that, and throws if the stream closes
empty — so a broken read fails the test instead of asserting on undefined.
const client = makeTestClient();
const load = await firstFrameOrThrow(
await client.surface.load.get({}),
"load cell yielded no snapshot frame — link failure",
);
expect(load).toEqual(ZERO);
Drive a procedure and assert the effect
Procedures are plain awaited calls. Call one, then read the affected cell or collection back and assert the new snapshot.
await client.surface.proc.kill({ pid: 4321 });
const alive = await firstFrameOrThrow(
await client.surface.processes.keys({}),
"processes keys yielded no snapshot frame — link failure",
);
expect(alive).not.toContain(4321);
Read a run of stream frames
When the value under test is a stream that emits over time, iterate it and collect the frames you care about. Because there is no socket, delivery is deterministic — no timers, no flushing.
const frames: LogFrame[] = [];
for await (const frame of await client.surface.nodeLog.get({
nodeId: "node-1",
})) {
frames.push(frame);
if (frame.done) break;
}
expect(frames[0]?.kind).toBe("snapshot");