runa/vscode/integration/server.test.ts

92 lines
3.2 KiB
TypeScript

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<void>((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, []);
},
);