runa/vscode/integration/sources.test.ts

83 lines
3.8 KiB
TypeScript
Raw 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";
const conflict = (error: unknown) => error instanceof ProtocolError && error.code === "conflict";
test(
"image sources are saved with compare-and-swap, spawned uncommitted, committed, and rolled back",
{ 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));
const [counter, ...others] = await client.listSources();
assert.ok(counter);
assert.deepEqual(others, []);
assert.equal(counter.path, "actors/counter");
assert.match(counter.source, /pub fn handle\(ctx, state, sender, message\)/);
assert.deepEqual(await client.readSource("actors/counter"), counter);
assert.deepEqual(await client.sourceHistory("actors/counter"), []);
await assert.rejects(client.readSource("actors/missing"), ProtocolError);
const edited = "pub fn handle(ctx, state, sender, message) {\n state + 2 // 雪\n}\n";
await assert.rejects(client.writeSource("actors/counter", null, edited), conflict);
await assert.rejects(client.writeSource("actors/counter", "stale", edited), conflict);
const saved = await client.writeSource("actors/counter", counter.source_id, edited);
assert.equal(saved.source, edited);
assert.notEqual(saved.source_id, counter.source_id);
await assert.rejects(
client.writeSource("actors/counter", counter.source_id, counter.source),
conflict,
);
assert.deepEqual(await client.sourceHistory("actors/counter"), []);
// Spawn uses the latest saved working source, independent of history.
const actor = await client.spawn("actors/counter", parseValue("0"));
await client.send(actor, parseValue("null"));
await client.tick(100);
assert.deepEqual((await client.inspectActor(actor)).state, parseValue("2"));
const commit = await client.commitSource("actors/counter");
assert.match(commit.revision, /^[A-Za-z0-9]{1,64}$/);
assert.equal(commit.source_id, saved.source_id);
assert.deepEqual(await client.sourceHistory("actors/counter"), [commit]);
assert.equal(await client.revisionSource("actors/counter", commit.revision), edited);
await assert.rejects(client.revisionSource("actors/counter", "missing0"), ProtocolError);
const later = await client.writeSource("actors/counter", saved.source_id, counter.source);
assert.equal((await client.readSource("actors/counter")).source, counter.source);
const rolled = await client.rollbackSource("actors/counter", commit.revision);
assert.deepEqual(rolled, {
path: "actors/counter",
source_id: saved.source_id,
source: edited,
});
assert.notEqual(rolled.source_id, later.source_id);
assert.deepEqual(await client.sourceHistory("actors/counter"), [commit]);
assert.equal(await client.revisionSource("actors/counter", commit.revision), edited);
const created = await client.writeSource(
"actors/echo",
null,
"pub fn handle(ctx, state, sender, message) { message }\n",
);
assert.deepEqual((await client.listSources()).map((file) => file.path).sort(), [
"actors/counter",
"actors/echo",
]);
assert.deepEqual(await client.sourceHistory("actors/echo"), []);
await client.system(actor, { kind: "become", path: created.path });
await client.send(actor, parseValue('"hello"'));
await client.tick(100);
assert.deepEqual((await client.inspectActor(actor)).state, parseValue('"hello"'));
},
);