import assert from "node:assert/strict"; import test from "node:test"; import { ImageClient } from "../src/client"; import { control, spawnCounter, startImage } from "./server"; test("extension disconnect and reconnect complete clean WebSocket handshakes", async (t) => { const image = await startImage(); t.after(() => image.stop()); for (let attempt = 0; attempt < 3; attempt++) { const client = await ImageClient.connect(image.address, (error) => { assert.equal(error, undefined); }); await client.listActors(); await client.close(); } assert.doesNotMatch(image.diagnostics(), /protocol error|reset without closing handshake/i); }); test( "TypeScript inspection shares the real Rust image across clients and reconnects", { timeout: 10000 }, async (t) => { const image = await startImage(); t.after(() => image.stop()); const actorRef = await spawnCounter(image.address); const reference = { type: "actor_ref", data: actorRef }; const payload = { type: "record", data: { nested: { type: "list", data: [reference, { type: "integer", data: -9223372036854775808n }], }, }, }; assert.deepEqual( await control(image.address, { operation: "send", parameters: { actor_ref: actorRef, message: payload }, }), { version: 5, request_id: "integration-control", result: { Ok: { kind: "acknowledged" } } }, ); const failures: Error[] = []; const disconnected = (error?: Error) => { if (error) failures.push(error); }; const first = await ImageClient.connect(image.address, disconnected); t.after(() => first.close()); const [snapshot] = await first.listActors(); assert.ok(snapshot); assert.equal(snapshot.actor_ref, actorRef); assert.deepEqual(snapshot.state, { type: "integer", data: 9223372036854775807n }); assert.deepEqual(snapshot.queued, [payload]); assert.equal(snapshot.reserved, null); first.close(); const revisions: bigint[] = []; let onChanged = () => {}; const changed = new Promise((resolve) => { onChanged = resolve; }); const second = await ImageClient.connect(image.address, disconnected, (revision) => { revisions.push(revision); onChanged(); }); t.after(() => second.close()); assert.deepEqual(await second.inspectActor(actorRef), snapshot); await control(image.address, { operation: "pause_actor", parameters: { actor_ref: actorRef } }); await changed; assert.equal(revisions.length, 1); assert.ok(revisions[0] !== undefined && revisions[0] > 0n); const paused = await second.inspectActor(actorRef); assert.equal(paused.status.status, "paused"); // Reading from the extension must not drain the shared runtime event queue. const events = await control(image.address, { operation: "get_events" }); assert.deepEqual(events, { version: 5, request_id: "integration-control", result: { Ok: { kind: "events", data: [ { event: "spawned", data: { actor_ref: actorRef } }, { event: "paused", data: { actor_ref: actorRef } }, ], }, }, }); assert.deepEqual(failures, []); }, );