runa/vscode/integration/repl.test.ts

96 lines
3.1 KiB
TypeScript

import assert from "node:assert/strict";
import { spawn } from "node:child_process";
import { once } from "node:events";
import { resolve } from "node:path";
import test from "node:test";
import { ImageClient } from "../src/client";
import { startImage } from "./server";
test(
"the real REPL mutates the same WebSocket image and notifies the inspection client",
{ timeout: 10000 },
async (t) => {
const image = await startImage();
let inspector: ImageClient | undefined;
t.after(async () => {
inspector?.close();
await image.stop();
});
const revisions: bigint[] = [];
let onChanged = () => {};
const changed = new Promise<void>((resolve) => {
onChanged = resolve;
});
inspector = await ImageClient.connect(
image.address,
(error) => assert.equal(error, undefined),
(revision) => {
revisions.push(revision);
onChanged();
},
);
const executable = resolve(
__dirname,
"../../../target/debug",
process.platform === "win32" ? "runa-cli.exe" : "runa-cli",
);
const repl = spawn(executable, [], {
env: { ...process.env, RUNA_CONNECT: image.address },
stdio: ["pipe", "pipe", "pipe"],
});
t.after(() => {
if (repl.exitCode === null && repl.signalCode === null) repl.kill();
});
let stdout = "";
let stderr = "";
repl.stdout.on("data", (chunk: Buffer) => {
stdout += chunk.toString();
});
repl.stderr.on("data", (chunk: Buffer) => {
stderr += chunk.toString();
});
const exited = once(repl, "exit");
repl.stdin.end(
[
"let actor = spawn actors/counter 5",
'send actor {"op":"add","value":7}',
"tick 100",
"inspect actor",
"quit",
].join("\n") + "\n",
);
const [code] = await exited;
assert.equal(code, 0, stderr);
assert.equal(stderr, "");
assert.doesNotMatch(image.diagnostics(), /protocol error|reset without closing handshake/i);
assert.doesNotMatch(stdout, /error:/i);
await changed;
assert.ok(revisions.length > 0);
const [actor] = await inspector.listActors();
assert.ok(actor);
assert.ok(stdout.includes(actor.actor_ref));
assert.deepEqual(actor.state, { type: "integer", data: 12n });
assert.deepEqual(actor.queued, []);
assert.equal(actor.reserved, null);
for (const input of ["actors\nquit\n", "exit\n", ""]) {
const overridden = spawn(executable, ["--connect", image.address], {
env: { ...process.env, RUNA_CONNECT: "invalid" },
stdio: ["pipe", "pipe", "pipe"],
});
t.after(() => {
if (overridden.exitCode === null && overridden.signalCode === null) overridden.kill();
});
let diagnostics = "";
overridden.stderr.on("data", (chunk: Buffer) => {
diagnostics += chunk.toString();
});
const overriddenExit = once(overridden, "exit");
overridden.stdin.end(input);
const [overrideCode] = await overriddenExit;
assert.equal(overrideCode, 0, diagnostics);
assert.equal(diagnostics, "");
assert.doesNotMatch(image.diagnostics(), /protocol error|reset without closing handshake/i);
}
},
);