2026-11-03 14:54:07 +00:00
|
|
|
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(
|
2026-11-03 16:01:24 +00:00
|
|
|
"extension controls spawn source paths, send lossless values, and apply system messages",
|
2026-11-03 14:54:07 +00:00
|
|
|
{ timeout: 10000 },
|
|
|
|
|
async (t) => {
|
|
|
|
|
const image = await startImage();
|
|
|
|
|
let client: ImageClient | undefined;
|
|
|
|
|
t.after(async () => {
|
|
|
|
|
client?.close();
|
|
|
|
|
await image.stop();
|
|
|
|
|
});
|
|
|
|
|
client = await ImageClient.connect(image.address, (error) => assert.equal(error, undefined));
|
2026-11-03 16:01:24 +00:00
|
|
|
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);
|
2026-11-03 14:54:07 +00:00
|
|
|
assert.match(ref, /^[A-Za-z0-9_-]{21}$/);
|
|
|
|
|
await client.send(ref, parseValue('{"op":"add","value":7}'));
|
2026-11-03 16:01:24 +00:00
|
|
|
await client.system(ref, { kind: "pause" });
|
2026-11-03 14:54:07 +00:00
|
|
|
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);
|
2026-11-03 16:01:24 +00:00
|
|
|
await client.system(ref, { kind: "resume" });
|
2026-11-03 14:54:07 +00:00
|
|
|
await client.tick(100);
|
|
|
|
|
snapshot = await client.inspectActor(ref);
|
|
|
|
|
assert.deepEqual(snapshot.state, parseValue("12"));
|
|
|
|
|
assert.deepEqual(snapshot.queued, []);
|
2026-11-03 16:01:24 +00:00
|
|
|
// Graceful stop completes only when the scheduler advances.
|
|
|
|
|
await client.system(ref, { kind: "stop" });
|
|
|
|
|
assert.notEqual((await client.inspectActor(ref)).status.status, "stopped");
|
|
|
|
|
await client.tick(10);
|
2026-11-03 14:54:07 +00:00
|
|
|
assert.equal((await client.inspectActor(ref)).status.status, "stopped");
|
2026-11-03 16:01:24 +00:00
|
|
|
await client.system(ref, { kind: "restart" });
|
2026-11-03 14:54:07 +00:00
|
|
|
snapshot = await client.inspectActor(ref);
|
|
|
|
|
assert.equal(snapshot.incarnation, 2n);
|
|
|
|
|
assert.deepEqual(snapshot.state, parseValue("12"));
|
2026-11-03 16:01:24 +00:00
|
|
|
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,
|
|
|
|
|
);
|
|
|
|
|
await client.system(ref, { kind: "kill" });
|
|
|
|
|
assert.equal((await client.inspectActor(ref)).status.status, "stopped");
|
|
|
|
|
await client.system(ref, { kind: "restart" });
|
|
|
|
|
assert.equal((await client.inspectActor(ref)).incarnation, 4n);
|
2026-11-03 14:54:07 +00:00
|
|
|
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);
|
|
|
|
|
},
|
|
|
|
|
);
|