runa/vscode/integration/controls.test.ts

80 lines
3.6 KiB
TypeScript
Raw Permalink Normal View History

import assert from "node:assert/strict";
import test from "node:test";
import { ImageClient } from "../src/client";
import { ProtocolError } from "../src/protocol";
import { parseValue } from "../src/values";
import { startImage } from "./server";
test(
"extension controls spawn source paths, send lossless values, and apply system messages",
{ timeout: 10000 },
async (t) => {
const image = await startImage();
let client: ImageClient | undefined;
t.after(async () => {
await client?.close();
await image.stop();
});
client = await ImageClient.connect(image.address, (error) => assert.equal(error, undefined));
assert.deepEqual(
(await client.listSources()).map((file) => file.path),
["actors/counter"],
);
await assert.rejects(client.spawn("actors/missing", parseValue("0")), ProtocolError);
const ref = await client.spawn("actors/counter", parseValue("5"), 2);
assert.match(ref, /^[A-Za-z0-9_-]{21}$/);
await client.send(ref, parseValue('{"op":"add","value":7}'));
await client.system(ref, { kind: "pause" });
await client.tick(100);
let snapshot = await client.inspectActor(ref);
assert.equal(snapshot.status.status, "paused");
assert.deepEqual(snapshot.state, parseValue("5"));
assert.equal(snapshot.queued.length, 1);
await client.system(ref, { kind: "resume" });
await client.tick(100);
snapshot = await client.inspectActor(ref);
assert.deepEqual(snapshot.state, parseValue("12"));
assert.deepEqual(snapshot.queued, []);
await client.system(ref, { kind: "restart" });
snapshot = await client.inspectActor(ref);
assert.equal(snapshot.incarnation, 2n);
assert.deepEqual(snapshot.state, parseValue("12"));
const implementation = snapshot.implementation_id;
// Restart forces a new incarnation even when running, keeping implementation and state.
await client.system(ref, { kind: "restart" });
snapshot = await client.inspectActor(ref);
assert.equal(snapshot.incarnation, 3n);
assert.equal(snapshot.implementation_id, implementation);
assert.deepEqual(snapshot.state, parseValue("12"));
await client.system(ref, { kind: "reload" });
await client.tick(10);
assert.equal((await client.inspectActor(ref)).status.status, "idle");
await assert.rejects(
client.system(ref, { kind: "become", path: "actors/missing" }),
ProtocolError,
);
const payload = parseValue(`{"nested":[9223372036854775807,{"$actor":"${ref}"}]}`);
await client.send(ref, payload);
assert.deepEqual((await client.inspectActor(ref)).queued, [payload]);
// Whole-request size includes tagged values and the envelope.
await assert.rejects(
client.send(ref, { type: "string", data: "x".repeat(1024 * 1024) }),
/1 MiB/,
);
assert.equal((await client.listActors()).length, 1);
await client.system(ref, { kind: "stop" });
assert.equal((await client.inspectActor(ref)).actor_ref, ref);
await client.tick(100);
await assert.rejects(client.inspectActor(ref), /unknown actor/);
await assert.rejects(client.system(ref, { kind: "restart" }), /unknown actor/);
assert.deepEqual(await client.listActors(), []);
const failed = await client.spawn("actors/counter", parseValue("0"));
await client.send(failed, parseValue('"bad message"'));
await client.tick(100);
assert.equal((await client.inspectActor(failed)).status.status, "failed");
await client.system(failed, { kind: "kill" });
await assert.rejects(client.inspectActor(failed), /unknown actor/);
assert.deepEqual(await client.listActors(), []);
},
);