137 lines
5.0 KiB
JavaScript
137 lines
5.0 KiB
JavaScript
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { boardHref, createBoard, renameBoard, DEFAULT_BOARD_TITLE, loadWorkspaceHome } from "../src/lib/workspace.ts";
|
|
|
|
test("profile and workspace reads both start before either completes", async () => {
|
|
const calls = [];
|
|
const resolve = {};
|
|
const pending = loadWorkspaceHome((path) => {
|
|
calls.push(path);
|
|
return new Promise((done) => {
|
|
resolve[path] = done;
|
|
});
|
|
});
|
|
assert.deepEqual(calls, ["profile", "workspaces"]);
|
|
resolve.profile({ id: "user", avatar_url: "https://lh3.googleusercontent.com/test-avatar" });
|
|
resolve.workspaces({ items: [{ id: "space", name: "个人空间" }] });
|
|
const result = await pending;
|
|
assert.equal(result.profile.id, "user");
|
|
assert.equal(result.profile.avatar_url, "https://lh3.googleusercontent.com/test-avatar");
|
|
assert.equal(result.workspaces[0].id, "space");
|
|
});
|
|
|
|
test("a first board creates its personal workspace before posting the board", async () => {
|
|
const calls = [];
|
|
let selected = "";
|
|
const result = await createBoard(
|
|
async (path, options) => {
|
|
calls.push([path, JSON.parse(options.body)]);
|
|
if (path === "workspaces") return { id: "space", name: "个人空间" };
|
|
assert.equal(selected, "space");
|
|
return { id: "board" };
|
|
},
|
|
" 周末的灵感 ",
|
|
"",
|
|
(w) => {
|
|
selected = w.id;
|
|
},
|
|
);
|
|
assert.equal(result.id, "board");
|
|
assert.deepEqual(
|
|
calls.map(([p]) => p),
|
|
["workspaces", "workspaces/space/materials"],
|
|
);
|
|
assert.equal(calls[1][1].title, "周末的灵感");
|
|
});
|
|
|
|
test("a board failure retains the new workspace, so retry creates no duplicate space", async () => {
|
|
let selected = "";
|
|
let spaces = 0;
|
|
let fail = true;
|
|
const request = async (path) => {
|
|
if (path === "workspaces") {
|
|
spaces++;
|
|
return { id: "space", name: "个人空间" };
|
|
}
|
|
if (fail) throw new Error("upstream_unavailable");
|
|
return { id: "board" };
|
|
};
|
|
await assert.rejects(
|
|
createBoard(request, "作品", selected, (w) => {
|
|
selected = w.id;
|
|
}),
|
|
);
|
|
fail = false;
|
|
await createBoard(request, "作品", selected, () =>
|
|
assert.fail("duplicate workspace"),
|
|
);
|
|
assert.equal(spaces, 1);
|
|
});
|
|
|
|
test("an existing workspace posts a board directly", async () => {
|
|
const calls = [];
|
|
await createBoard(
|
|
async (path) => {
|
|
calls.push(path);
|
|
return { id: "board" };
|
|
},
|
|
"作品",
|
|
"existing",
|
|
() => assert.fail("unexpected workspace"),
|
|
);
|
|
assert.deepEqual(calls, ["workspaces/existing/materials"]);
|
|
});
|
|
|
|
|
|
test("new graphs use a default name and retain the Foundry-assigned identity", async () => {
|
|
const created = await createBoard(async (path, options) => {
|
|
assert.equal(path, "workspaces/space/materials");
|
|
assert.deepEqual(JSON.parse(options.body), {
|
|
kind: "board", title: DEFAULT_BOARD_TITLE, content: { schema_version: 1 },
|
|
});
|
|
return { id: "server-generated-id", workspace_id: "space", kind: "board", version: 1 };
|
|
}, " ", "space", () => assert.fail("unexpected workspace creation"));
|
|
assert.equal(created.id, "server-generated-id");
|
|
assert.equal(created.workspace_id, "space");
|
|
assert.equal(created.title, "未命名图谱");
|
|
assert.equal(boardHref(created.workspace_id, created.id), "/dashboard/space/server-generated-id");
|
|
});
|
|
|
|
test("graph URLs preserve workspace identity and safely encode route segments", () => {
|
|
assert.equal(boardHref("space/a", "id?b"), "/dashboard/space%2Fa/id%3Fb");
|
|
assert.equal(boardHref("demo", "board", true), "/dashboard/demo/board?demo=1");
|
|
});
|
|
|
|
|
|
test("renaming reads the latest version and preserves description and content", async () => {
|
|
const calls = [];
|
|
const content = { schema_version: 1, preserved: { key: "value" } };
|
|
const renamed = await renameBoard(async (path, options) => {
|
|
calls.push(path);
|
|
if (!options) return { id: "board", workspace_id: "space", kind: "board", title: "Old", version: 7, description: "Keep this", content };
|
|
assert.equal(options.method, "PATCH");
|
|
assert.equal(options.headers["If-Match"], '"7"');
|
|
assert.deepEqual(JSON.parse(options.body), { title: "新名称", description: "Keep this", content });
|
|
return null;
|
|
}, "space", "board", " 新名称 ");
|
|
assert.deepEqual(calls, ["workspaces/space/materials/board", "workspaces/space/materials/board"]);
|
|
assert.equal(renamed.title, "新名称");
|
|
assert.equal(renamed.version, 8);
|
|
});
|
|
|
|
test("rename rejects empty or oversized names without any API calls", async () => {
|
|
for (const name of [" ", "字".repeat(101)]) {
|
|
await assert.rejects(renameBoard(() => assert.fail("unexpected API call"), "space", "board", name), /invalid_title/);
|
|
}
|
|
});
|
|
|
|
test("rename surfaces a version conflict without retrying or overwriting", async () => {
|
|
let patches = 0;
|
|
await assert.rejects(renameBoard(async (_path, options) => {
|
|
if (!options) return { id: "board", workspace_id: "space", kind: "board", title: "Old", version: 1 };
|
|
patches++;
|
|
throw new Error("version_conflict");
|
|
}, "space", "board", "New"), /version_conflict/);
|
|
assert.equal(patches, 1);
|
|
});
|