Files
gori-agent/test/workspace-scope.test.ts
T
2026-08-18 18:48:47 +08:00

103 lines
5.0 KiB
TypeScript

import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import test from "node:test";
import { canonicalWorkspace, findOverlappingWorkspace } from "../src/core/workspace-scope.js";
function writePeer(instances: string, botId: string, workspace: string): void {
const directory = path.join(instances, botId);
fs.mkdirSync(directory, { recursive: true });
fs.writeFileSync(path.join(directory, "config.json"), JSON.stringify({
configVersion: 3,
bot: {
id: botId,
workspace,
persona: "test",
agent: { id: "fake", command: process.execPath, args: ["agent.mjs"], env: {} },
skills: [],
permissions: { mode: "deny", allowedTools: [], allowedCommandPatterns: [] }
},
gateway: { platform: { type: "webhook", secret: "test-secret" } },
runtime: { acp: {} }
}));
}
test("peer scan rejects identical, parent, and child workspaces and allows disjoint scopes", async (t) => {
const directory = await fs.promises.mkdtemp(path.join(os.tmpdir(), "gori-workspace-scope-"));
t.after(() => fs.rmSync(directory, { recursive: true, force: true }));
const instances = path.join(directory, "instances");
const workspace = path.join(directory, "workspace");
const child = path.join(workspace, "child");
const sibling = path.join(directory, "sibling");
fs.mkdirSync(child, { recursive: true });
fs.mkdirSync(sibling, { recursive: true });
// Disjoint workspaces are allowed.
writePeer(instances, "sibling-bot", sibling);
assert.equal(findOverlappingWorkspace(workspace, "current", instances), undefined);
// An identical workspace is rejected.
writePeer(instances, "same-bot", workspace);
assert.equal(findOverlappingWorkspace(workspace, "current", instances), "same-bot");
// A child workspace is rejected from the parent's perspective.
fs.rmSync(path.join(instances, "same-bot"), { recursive: true, force: true });
writePeer(instances, "child-bot", child);
assert.equal(findOverlappingWorkspace(workspace, "current", instances), "child-bot");
// From the child's perspective an identical or parent peer workspace is rejected.
assert.equal(findOverlappingWorkspace(child, "current", instances), "child-bot");
writePeer(instances, "same-bot", workspace);
assert.ok(["same-bot", "child-bot"].includes(findOverlappingWorkspace(child, "current", instances)!));
// The instance's own entry is skipped even when its workspace matches.
fs.rmSync(path.join(instances, "child-bot"), { recursive: true, force: true });
assert.equal(findOverlappingWorkspace(workspace, "same-bot", instances), undefined);
// A missing instances directory means no peers to conflict with.
assert.equal(findOverlappingWorkspace(workspace, "current", path.join(directory, "absent")), undefined);
});
test("peer scan fails closed for unsafe or invalid peer configs", async (t) => {
const directory = await fs.promises.mkdtemp(path.join(os.tmpdir(), "gori-workspace-scope-closed-"));
t.after(() => fs.rmSync(directory, { recursive: true, force: true }));
const instances = path.join(directory, "instances");
const workspace = path.join(directory, "workspace");
fs.mkdirSync(workspace, { recursive: true });
// Peer config declares a workspace that cannot be canonicalized.
writePeer(instances, "missing-bot", path.join(directory, "missing"));
assert.throws(() => findOverlappingWorkspace(workspace, "current", instances));
fs.rmSync(path.join(instances, "missing-bot"), { recursive: true, force: true });
// Corrupt peer config fails closed.
const corrupt = path.join(instances, "corrupt-bot");
fs.mkdirSync(corrupt);
fs.writeFileSync(path.join(corrupt, "config.json"), "not-json");
assert.throws(() => findOverlappingWorkspace(workspace, "current", instances), /Invalid peer Config v3/);
fs.rmSync(path.join(corrupt, "config.json"));
// Missing peer config fails closed.
assert.throws(() => findOverlappingWorkspace(workspace, "current", instances), /missing config/);
fs.rmSync(corrupt, { recursive: true, force: true });
// Symlinked peer entries are refused.
writePeer(instances, "real-bot", path.join(directory, "sibling-real"));
fs.mkdirSync(path.join(directory, "sibling-real"), { recursive: true });
fs.symlinkSync(path.join(instances, "real-bot"), path.join(instances, "linked-bot"));
assert.throws(() => findOverlappingWorkspace(workspace, "current", instances), /unsafe peer instance entry/);
});
test("canonicalWorkspace resolves symlinks and relative segments", async (t) => {
const directory = await fs.promises.mkdtemp(path.join(os.tmpdir(), "gori-workspace-canonical-"));
t.after(() => fs.rmSync(directory, { recursive: true, force: true }));
const real = path.join(directory, "real");
fs.mkdirSync(real, { recursive: true });
const link = path.join(directory, "link");
fs.symlinkSync(real, link);
assert.equal(canonicalWorkspace(link), fs.realpathSync.native(real));
assert.equal(canonicalWorkspace(path.join(real, "..", "real")), fs.realpathSync.native(real));
assert.throws(() => canonicalWorkspace(path.join(directory, "missing")));
});