runa/vscode/integration/host.ts

135 lines
5.5 KiB
TypeScript
Raw Normal View History

import assert from "node:assert/strict";
import * as vscode from "vscode";
import { control, spawnCounter, startImage } from "./server";
export async function run(): Promise<void> {
const image = await startImage();
try {
await vscode.commands.executeCommand("runa.connect", image.address);
const spawned = await vscode.commands.executeCommand<unknown>("runa.spawn", {
behavior: "counter",
state: "5",
});
assert.equal(typeof spawned, "string");
const selection = { actor_ref: String(spawned) };
await vscode.commands.executeCommand("runa.inspect", selection);
const controlled = vscode.window.activeTextEditor?.document;
assert.ok(controlled);
await vscode.commands.executeCommand("runa.send", selection, '{"op":"add","value":7}');
const controlPaused = documentChanged(controlled, (text) =>
text.includes('"status": "paused"'),
);
await vscode.commands.executeCommand("runa.pause", selection);
await vscode.commands.executeCommand("runa.tick", 100);
await controlPaused;
let snapshot = JSON.parse(controlled.getText()).actor;
assert.equal(snapshot.status.status, "paused");
assert.equal(snapshot.state.data, 5);
assert.equal(snapshot.queued.length, 1);
const controlExecuted = documentChanged(
controlled,
(text) => JSON.parse(text).actor.state.data === 12,
);
await vscode.commands.executeCommand("runa.resume", selection);
await vscode.commands.executeCommand("runa.tick", 100);
await controlExecuted;
snapshot = JSON.parse(controlled.getText()).actor;
assert.equal(snapshot.state.data, 12);
assert.equal(snapshot.queued.length, 0);
const controlStopped = documentChanged(controlled, (text) =>
text.includes('"status": "stopped"'),
);
await vscode.commands.executeCommand("runa.stop", selection);
await controlStopped;
assert.equal(JSON.parse(controlled.getText()).actor.status.status, "stopped");
const controlRestarted = documentChanged(
controlled,
(text) => JSON.parse(text).actor.incarnation === 2,
);
await vscode.commands.executeCommand("runa.restart", selection);
await controlRestarted;
snapshot = JSON.parse(controlled.getText()).actor;
assert.equal(snapshot.incarnation, 2);
assert.equal(snapshot.state.data, 12);
const actorRef = await spawnCounter(image.address);
await vscode.commands.executeCommand("runa.connect", image.address);
await vscode.commands.executeCommand("runa.refresh");
await vscode.commands.executeCommand("runa.inspect", { actor_ref: actorRef });
let document = vscode.window.activeTextEditor?.document;
assert.ok(document);
assert.equal(document.uri.scheme, "runa");
assert.equal(document.languageId, "json");
assert.match(document.getText(), /9223372036854775807/);
assert.match(document.getText(), /"connected": true/);
const firstUri = document.uri.toString();
const refreshed = documentChanged(document, (text) =>
text.includes("queued from another client"),
);
assert.deepEqual(
await control(image.address, {
operation: "send",
parameters: {
actor_ref: actorRef,
message: { type: "string", data: "queued from another client" },
},
}),
{ version: 3, request_id: "integration-control", result: { Ok: { kind: "acknowledged" } } },
);
await refreshed;
assert.match(document.getText(), /queued from another client/);
const paused = documentChanged(document, (text) => text.includes('"status": "paused"'));
await control(image.address, { operation: "pause_actor", parameters: { actor_ref: actorRef } });
await paused;
const failed = documentChanged(document, (text) => text.includes('"status": "failed"'));
await control(image.address, {
operation: "resume_actor",
parameters: { actor_ref: actorRef },
});
await control(image.address, {
operation: "tick",
parameters: { ticks: 100, instruction_budget: 10 },
});
await failed;
const disconnected = documentChanged(document, (text) => text.includes('"connected": false'));
await vscode.commands.executeCommand("runa.disconnect");
await disconnected;
assert.match(document.getText(), /"connected": false/);
await vscode.commands.executeCommand("runa.connect", image.address);
await vscode.commands.executeCommand("runa.inspect", { actor_ref: actorRef });
document = vscode.window.activeTextEditor?.document;
assert.ok(document);
assert.notEqual(document.uri.toString(), firstUri);
assert.match(document.getText(), /queued from another client/);
const old = await vscode.workspace.openTextDocument(vscode.Uri.parse(firstUri));
assert.match(old.getText(), /"connected": false/);
console.log(
"Runa extension host: spawn, send, lifecycle, ticks, inspection, automatic refresh, reconnect passed",
);
} finally {
await vscode.commands.executeCommand("runa.disconnect");
await image.stop();
}
}
function documentChanged(
document: vscode.TextDocument,
expected: (text: string) => boolean,
): Promise<void> {
return new Promise((resolve, reject) => {
const subscription = vscode.workspace.onDidChangeTextDocument((event) => {
if (
event.document.uri.toString() === document.uri.toString() &&
expected(event.document.getText())
) {
clearTimeout(timeout);
subscription.dispose();
resolve();
}
});
const timeout = setTimeout(() => {
subscription.dispose();
reject(new Error("Actor inspector did not refresh"));
}, 5000);
});
}