runa/vscode/test/debugVariables.test.ts

108 lines
4.7 KiB
TypeScript

import assert from "node:assert/strict";
import { test } from "node:test";
import { ActorVariables } from "../src/debugVariables";
import { actor, REF } from "./fixtures";
test("actor variables preserve exact values, types, escaped strings and typed references", () => {
const variables = new ActorVariables();
variables.capture(actor());
const scope = variables.scope();
assert.match(scope.name, /committed; not frame locals/);
const roots = variables.variables(scope.variablesReference);
assert.deepEqual(
roots.map((variable) => variable.name),
["committed_state", "mailbox", "config", "metadata"],
);
const state = roots[0]!;
assert.equal(state.value, "Record(5)");
assert.equal(state.namedVariables, 5);
const fields = variables.variables(state.variablesReference);
assert.equal(fields.find((field) => field.name === "minimum")?.value, "-9223372036854775808");
assert.equal(fields.find((field) => field.name === "maximum")?.value, "9223372036854775807");
assert.equal(fields.find((field) => field.name === "reference")?.value, `@${REF}`);
assert.equal(fields.find((field) => field.name === "reference")?.type, "actor_ref");
assert.equal(fields.find((field) => field.name === "text")?.type, "string");
assert.equal(
fields.find((field) => field.name === "text")?.value,
JSON.stringify(`@${REF} "雪"`),
);
assert.ok(fields.every((field) => field.presentationHint.attributes[0] === "readOnly"));
const list = fields.find((field) => field.name === "list")!;
assert.equal(list.indexedVariables, 2);
assert.deepEqual(
variables.variables(list.variablesReference).map((child) => child.value),
["null", "true"],
);
const metadata = variables.variables(roots[3]!.variablesReference);
assert.equal(
metadata.find((field) => field.name === "mailbox_id")?.value,
"18446744073709551615",
);
const mailbox = variables.variables(roots[1]!.variablesReference);
const reserved = mailbox.find((field) => field.name === "reserved")!;
assert.equal(
reserved.indexedVariables,
1,
"a reserved null message must not be confused with no reservation",
);
assert.equal(variables.variables(reserved.variablesReference)[0]?.value, "null");
});
test("variable handles are stable per snapshot, stale after clear, and snapshots never alias inputs", () => {
const snapshot = actor();
const variables = new ActorVariables();
variables.capture(snapshot);
const scope = variables.scope();
const state = variables.variables(scope.variablesReference)[0]!;
assert.deepEqual(variables.variables(scope.variablesReference)[0], state);
snapshot.state = { type: "integer", data: 999n };
snapshot.queued.push({ type: "null" });
assert.equal(variables.variables(scope.variablesReference)[0]?.value, "Record(5)");
const mailbox = variables.variables(
variables.variables(scope.variablesReference)[1]!.variablesReference,
);
assert.equal(mailbox.find((field) => field.name === "queued")?.indexedVariables, 1);
variables.capture(snapshot);
assert.throws(() => variables.variables(scope.variablesReference), /Stale/);
const next = variables.scope();
assert.notEqual(scope.variablesReference, next.variablesReference);
assert.equal(variables.variables(next.variablesReference)[0]?.value, "999");
variables.clear();
assert.throws(() => variables.scope(), /unavailable/);
assert.throws(() => variables.variables(next.variablesReference), /Stale/);
});
test("variables support named/indexed paging and bounded previews without recursive serialization", () => {
const snapshot = actor();
snapshot.state = {
type: "list",
data: [
{ type: "string", data: "\n".repeat(300) },
{ type: "record", data: {} },
{ type: "list", data: [] },
{ type: "integer", data: 1n },
],
};
snapshot.reserved = null;
const variables = new ActorVariables();
variables.capture(snapshot);
const root = variables.scope().variablesReference;
const state = variables.variables(root)[0]!.variablesReference;
assert.deepEqual(variables.variables(state, "named"), []);
assert.deepEqual(variables.variables(root, "indexed"), []);
assert.deepEqual(
variables.variables(state, "indexed", 1, 2).map((field) => field.name),
["1", "2"],
);
assert.deepEqual(
variables.variables(state, undefined, 2, 0).map((field) => field.name),
["2", "3"],
);
assert.deepEqual(variables.variables(state, "indexed", 100), []);
const fields = variables.variables(state);
assert.equal(fields[0]?.value, `${JSON.stringify("\n".repeat(256))}... (300 UTF-16 code units)`);
assert.equal(fields[1]?.variablesReference, 0);
assert.equal(fields[2]?.variablesReference, 0);
assert.throws(() => variables.variables(root, "invalid"), /filter/);
});