Imported as a linear vendor commit so forge rebases do not replay the upstream root tree. git-subtree-dir: third_party/pi git-subtree-split: d981de1229ef899957bbe968bc8dcda02a21f477
76 lines
2.6 KiB
TypeScript
76 lines
2.6 KiB
TypeScript
import { mkdirSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { ENV_AGENT_DIR } from "../../../src/config.ts";
|
|
import { SessionManager } from "../../../src/core/session-manager.ts";
|
|
|
|
const DIRECTORY_LINK_TYPE = process.platform === "win32" ? "junction" : "dir";
|
|
|
|
describe("regression #7497: discover sessions through symlinked directories", () => {
|
|
let tempDir: string;
|
|
let sessionsDir: string;
|
|
|
|
beforeEach(() => {
|
|
tempDir = mkdtempSync(join(tmpdir(), "pi-session-discovery-"));
|
|
const agentDir = join(tempDir, "agent");
|
|
sessionsDir = join(agentDir, "sessions");
|
|
mkdirSync(sessionsDir, { recursive: true });
|
|
vi.stubEnv(ENV_AGENT_DIR, agentDir);
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllEnvs();
|
|
rmSync(tempDir, { recursive: true, force: true });
|
|
});
|
|
|
|
function writeSession(dir: string, id: string): void {
|
|
mkdirSync(dir, { recursive: true });
|
|
writeFileSync(
|
|
join(dir, `${id}.jsonl`),
|
|
`${JSON.stringify({
|
|
type: "session",
|
|
version: 3,
|
|
id,
|
|
timestamp: "2026-08-03T00:00:00.000Z",
|
|
cwd: join(tempDir, "project"),
|
|
})}\n`,
|
|
);
|
|
}
|
|
|
|
it("discovers a session through a directory link and preserves the alias path", async () => {
|
|
const targetDir = join(tempDir, "linked-sessions");
|
|
writeSession(targetDir, "linked");
|
|
const aliasDir = join(sessionsDir, "--linked--");
|
|
symlinkSync(targetDir, aliasDir, DIRECTORY_LINK_TYPE);
|
|
|
|
const sessions = await SessionManager.listAll();
|
|
|
|
expect(sessions.map((session) => session.id)).toEqual(["linked"]);
|
|
expect(sessions[0]?.path).toBe(join(aliasDir, "linked.jsonl"));
|
|
});
|
|
|
|
it("ignores a broken directory link without hiding valid sessions", async () => {
|
|
writeSession(join(sessionsDir, "--regular--"), "regular");
|
|
const targetDir = join(tempDir, "removed-sessions");
|
|
mkdirSync(targetDir);
|
|
symlinkSync(targetDir, join(sessionsDir, "--broken--"), DIRECTORY_LINK_TYPE);
|
|
rmSync(targetDir, { recursive: true });
|
|
|
|
const sessions = await SessionManager.listAll();
|
|
|
|
expect(sessions.map((session) => session.id)).toEqual(["regular"]);
|
|
});
|
|
|
|
it.skipIf(process.platform === "win32")("ignores links to files", async () => {
|
|
writeSession(join(sessionsDir, "--regular--"), "regular");
|
|
const targetFile = join(tempDir, "not-a-directory");
|
|
writeFileSync(targetFile, "");
|
|
symlinkSync(targetFile, join(sessionsDir, "--file--"), "file");
|
|
|
|
const sessions = await SessionManager.listAll();
|
|
|
|
expect(sessions.map((session) => session.id)).toEqual(["regular"]);
|
|
});
|
|
});
|