server/bot-cwd.test.ts
Rama main · réplica de lectura del repositorio original.
import { existsSync, mkdirSync, mkdtempSync, realpathSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
import { homedir, tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { afterAll, describe, expect, it } from "vitest";
import { folderToFollow, inMyFolderCwd, sharedProjectFolder, validateBotCwd } from "./bot-cwd.ts";
const dir = mkdtempSync(join(tmpdir(), "omb-cwd-"));
afterAll(() => rmSync(dir, { recursive: true, force: true }));
describe("validateBotCwd", () => {
it("accepts an existing absolute directory", () => {
expect(validateBotCwd(dir)).toEqual({ ok: true, cwd: dir });
});
it("treats null and empty as clearing the folder", () => {
expect(validateBotCwd(null)).toEqual({ ok: true, cwd: null });
expect(validateBotCwd("")).toEqual({ ok: true, cwd: null });
expect(validateBotCwd(" ")).toEqual({ ok: true, cwd: null });
});
it("expands a leading ~ to the home folder", () => {
// compare against homedir() itself: a Windows home like C:\Users\RUNNER~1
// legitimately contains "~", so "no ~ in the output" is not a valid check
expect(validateBotCwd("~")).toEqual({ ok: true, cwd: resolve(homedir()) });
});
it("rejects relative paths, files, and missing folders with a reason", () => {
expect(validateBotCwd("relative/path")).toEqual({ ok: false, error: expect.stringMatching(/absolute/) });
const file = join(dir, "a-file.txt");
writeFileSync(file, "x");
expect(validateBotCwd(file)).toEqual({ ok: false, error: expect.stringMatching(/not a folder/) });
expect(validateBotCwd(join(dir, "nope"))).toEqual({ ok: false, error: expect.stringMatching(/doesn't exist/) });
expect(validateBotCwd(42)).toEqual({ ok: false, error: expect.stringMatching(/path/) });
});
});
describe("sharedProjectFolder", () => {
const data = join(dir, "omb-data");
const project = join(dir, "omb-data-site");
const privateWorkspace = join(data, "task-workspaces", "bot", "thread");
mkdirSync(join(data, "task-workspaces", "bot", "thread"), { recursive: true });
mkdirSync(project);
const linkKind = process.platform === "win32" ? "junction" : "dir";
const intoData = join(dir, "site-link-into-data");
const outside = join(dir, "omb-outside-site");
const anotherOutside = join(dir, "omb-another-outside");
mkdirSync(outside);
mkdirSync(anotherOutside);
try { symlinkSync(privateWorkspace, intoData, linkKind); } catch {}
const outLink = join(dir, "site-link-outside");
try { symlinkSync(outside, outLink, linkKind); } catch {}
const linked = existsSync(intoData) && existsSync(outLink);
it("shares an existing folder outside the data folder, even one whose name starts the same", () => {
expect(sharedProjectFolder(project, data)).toBe(project);
});
it("never shares the data folder, a private workspace in it, a missing folder or no folder", () => {
expect(sharedProjectFolder(data, data)).toBeUndefined();
expect(sharedProjectFolder(privateWorkspace, data)).toBeUndefined();
expect(sharedProjectFolder(join(dir, "gone"), data)).toBeUndefined();
expect(sharedProjectFolder(null, data)).toBeUndefined();
expect(sharedProjectFolder(undefined, data)).toBeUndefined();
});
it.runIf(linked)("never shares a folder whose link resolves into the data folder, while a link out stays shareable", () => {
expect(sharedProjectFolder(intoData, data)).toBeUndefined();
expect(sharedProjectFolder(outLink, data)).toBe(realpathSync(outside));
});
it.runIf(linked)("returns the folder a link resolved to at check time, so retargeting it cannot move a shared task", () => {
expect(sharedProjectFolder(outLink, data)).toBe(realpathSync(outside));
rmSync(outLink);
symlinkSync(join(dir, "omb-another-outside"), outLink, linkKind);
expect(realpathSync(outLink)).not.toBe(realpathSync(outside));
expect(sharedProjectFolder(outLink, data)).toBe(realpathSync(join(dir, "omb-another-outside")));
});
it.runIf(linked)("checks the data root as it really resolves, so a linked data folder is still a boundary", () => {
const linkedData = join(dir, "omb-data-link");
try { symlinkSync(data, linkedData, linkKind); } catch {}
if (!existsSync(linkedData)) return;
expect(sharedProjectFolder(privateWorkspace, linkedData)).toBeUndefined();
expect(sharedProjectFolder(project, linkedData)).toBe(project);
});
it.runIf(linked)("still validates a linked folder as a plain working folder", () => {
expect(validateBotCwd(intoData)).toEqual({ ok: true, cwd: intoData });
});
});
describe("inMyFolderCwd", () => {
const data = join(dir, "omb-data");
const project = join(dir, "omb-data-site");
const privateFolder = join(data, "task-workspaces", "bot", "thread");
it("shares the sender's project folder outside a room unless asked not to", () => {
expect(inMyFolderCwd(undefined, true, project, data)).toEqual({ cwd: project });
expect(inMyFolderCwd(true, true, project, data)).toEqual({ cwd: project });
expect(inMyFolderCwd(false, true, project, data)).toEqual({});
});
it("hands off without a folder where there is nothing to share, even when asked for one", () => {
expect(inMyFolderCwd(undefined, false, project, data)).toEqual({});
expect(inMyFolderCwd(undefined, true, privateFolder, data)).toEqual({});
expect(inMyFolderCwd(true, false, project, data)).toEqual({});
expect(inMyFolderCwd(true, true, privateFolder, data)).toEqual({});
});
});
describe("folderToFollow", () => {
const own = join(dir, "omb-data", "task-workspaces", "bot", "thread");
const bot = { id: "bot", cwd: join(dir, "omb-data-site") };
const folders = { worksInFolders: true, keepsPrivateFolder: false };
it("moves a thread from its own folder to the working folder its bot has been given", () => {
expect(folderToFollow(bot, "thread", own, folders)).toBe(bot.cwd);
});
it("leaves a thread where it is when the bot has no working folder or the thread is not in its own folder", () => {
expect(folderToFollow({ id: "bot" }, "thread", own, folders)).toBeUndefined();
expect(folderToFollow(bot, "thread", join(dir, "chosen"), folders)).toBeUndefined();
expect(folderToFollow(bot, "thread", bot.cwd, folders)).toBeUndefined();
expect(folderToFollow(bot, "thread", join(dir, "omb-data", "task-workspaces", "bot", "other-thread"), folders)).toBeUndefined();
expect(folderToFollow(bot, "thread", null, folders)).toBeUndefined();
expect(folderToFollow(bot, "thread", undefined, folders)).toBeUndefined();
});
it("never moves a conversation that keeps a private folder, or one on an engine that works in no folder", () => {
expect(folderToFollow(bot, "thread", own, { ...folders, keepsPrivateFolder: true })).toBeUndefined();
expect(folderToFollow(bot, "thread", own, { ...folders, worksInFolders: false })).toBeUndefined();
});
});