Files
kandev/apps/web/lib/markdown/file-link-target.test.ts
T
2026-09-05 13:26:25 +00:00

273 lines
8.9 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
buildMarkdownFileRootAliases,
resolveMarkdownFileTarget,
type MarkdownFileRootAlias,
} from "./file-link-target";
const workspaceRoot = "/root/.kandev/tasks/example";
const primaryRepositoryId = "repo-primary";
const siblingRepositoryId = "repo-sibling";
const primarySourceRoot = "/home/jcfs/projects/kandev";
const siblingSourceRoot = "/home/jcfs/projects/plugin";
const singleRepositoryId = "repo-1";
const singleSourceRoot = "/home/jcfs/projects/kandev";
const aliases: MarkdownFileRootAlias[] = [
{
repositoryId: primaryRepositoryId,
sourceRoot: primarySourceRoot,
workspaceRelativeRoot: "kandev",
},
{
repositoryId: siblingRepositoryId,
sourceRoot: siblingSourceRoot,
workspaceRelativeRoot: "plugin",
},
];
describe("buildMarkdownFileRootAliases", () => {
it("maps task-linked repositories to their active worktree roots", () => {
expect(
buildMarkdownFileRootAliases({
workspaceRoot,
taskRepositoryIds: [primaryRepositoryId, siblingRepositoryId],
repositories: [
{ id: primaryRepositoryId, localPath: primarySourceRoot },
{ id: siblingRepositoryId, localPath: siblingSourceRoot },
],
sessionWorktrees: [
{ repositoryId: primaryRepositoryId, path: `${workspaceRoot}/kandev` },
{ repositoryId: siblingRepositoryId, path: `${workspaceRoot}/plugin` },
],
}),
).toEqual([
{
repositoryId: primaryRepositoryId,
sourceRoot: primarySourceRoot,
workspaceRelativeRoot: "kandev",
},
{
repositoryId: siblingRepositoryId,
sourceRoot: siblingSourceRoot,
workspaceRelativeRoot: "plugin",
},
]);
});
it("supports a repository worktree that is the workspace root", () => {
expect(
buildMarkdownFileRootAliases({
workspaceRoot,
taskRepositoryIds: [singleRepositoryId],
repositories: [{ id: singleRepositoryId, localPath: singleSourceRoot }],
sessionWorktrees: [{ repositoryId: singleRepositoryId, path: workspaceRoot }],
}),
).toEqual([
{
repositoryId: singleRepositoryId,
sourceRoot: singleSourceRoot,
workspaceRelativeRoot: "",
},
]);
});
it("does not authorize session worktrees absent from the task repository set", () => {
expect(
buildMarkdownFileRootAliases({
workspaceRoot,
taskRepositoryIds: [primaryRepositoryId],
repositories: [
{ id: primaryRepositoryId, localPath: primarySourceRoot },
{ id: siblingRepositoryId, localPath: siblingSourceRoot },
],
sessionWorktrees: [
{ repositoryId: primaryRepositoryId, path: `${workspaceRoot}/kandev` },
{ repositoryId: siblingRepositoryId, path: `${workspaceRoot}/plugin` },
],
}),
).toEqual([
{
repositoryId: primaryRepositoryId,
sourceRoot: primarySourceRoot,
workspaceRelativeRoot: "kandev",
},
]);
});
it("does not infer repository aliases from session worktrees alone", () => {
expect(
buildMarkdownFileRootAliases({
workspaceRoot,
repositories: [{ id: siblingRepositoryId, localPath: siblingSourceRoot }],
sessionWorktrees: [{ repositoryId: siblingRepositoryId, path: `${workspaceRoot}/plugin` }],
}),
).toEqual([]);
});
});
describe("buildMarkdownFileRootAliases fail-closed cases", () => {
it("fails closed when repository identity or workspace containment is incomplete", () => {
expect(
buildMarkdownFileRootAliases({
workspaceRoot,
taskRepositoryIds: [singleRepositoryId],
repositories: [{ id: singleRepositoryId, localPath: singleSourceRoot }],
sessionWorktrees: [{ repositoryId: "other-repo", path: `${workspaceRoot}/kandev` }],
}),
).toEqual([]);
expect(
buildMarkdownFileRootAliases({
workspaceRoot,
taskRepositoryIds: ["repo-1"],
repositories: [{ id: "repo-1", localPath: "/home/jcfs/projects/kandev" }],
sessionWorktrees: [{ repositoryId: singleRepositoryId, path: "/tmp/kandev" }],
}),
).toEqual([]);
});
it("omits ambiguous source or worktree identities", () => {
expect(
buildMarkdownFileRootAliases({
workspaceRoot,
taskRepositoryIds: [singleRepositoryId],
repositories: [
{ id: singleRepositoryId, localPath: singleSourceRoot },
{ id: singleRepositoryId, localPath: "/home/jcfs/other-kandev" },
],
sessionWorktrees: [{ repositoryId: singleRepositoryId, path: `${workspaceRoot}/kandev` }],
}),
).toEqual([]);
expect(
buildMarkdownFileRootAliases({
workspaceRoot,
taskRepositoryIds: [singleRepositoryId],
repositories: [{ id: singleRepositoryId, localPath: singleSourceRoot }],
sessionWorktrees: [
{ repositoryId: singleRepositoryId, path: `${workspaceRoot}/kandev` },
{ repositoryId: singleRepositoryId, path: `${workspaceRoot}/other` },
],
}),
).toEqual([]);
});
});
describe("resolveMarkdownFileTarget mapped paths", () => {
it("resolves active workspace paths before repository aliases", () => {
expect(
resolveMarkdownFileTarget(`${workspaceRoot}/kandev/docs/README.md:12:4`, {
workspaceRoot,
fileRootAliases: aliases,
}),
).toEqual({ kind: "file", path: "kandev/docs/README.md" });
});
it("maps registered source paths to the active worktree", () => {
expect(
resolveMarkdownFileTarget(`${siblingSourceRoot}/ui/bundle.js:61`, {
workspaceRoot,
fileRootAliases: aliases,
}),
).toEqual({ kind: "file", path: "plugin/ui/bundle.js" });
});
it("maps Windows workspace and registered source paths case-insensitively", () => {
const windowsWorkspaceRoot = String.raw`C:\Users\Me\worktree`;
const windowsAliases: MarkdownFileRootAlias[] = [
{
repositoryId: siblingRepositoryId,
sourceRoot: String.raw`C:\Users\Me\source`,
workspaceRelativeRoot: "plugin",
},
];
expect(
resolveMarkdownFileTarget(String.raw`c:\users\me\worktree\src\file.ts:4`, {
workspaceRoot: windowsWorkspaceRoot,
}),
).toEqual({ kind: "file", path: "src/file.ts" });
expect(
resolveMarkdownFileTarget("/c:/users/me/source/ui/bundle.js:61", {
workspaceRoot: windowsWorkspaceRoot,
fileRootAliases: windowsAliases,
}),
).toEqual({ kind: "file", path: "plugin/ui/bundle.js" });
expect(
resolveMarkdownFileTarget(String.raw`C:\Windows\System32\kernel32.dll`, {
workspaceRoot: windowsWorkspaceRoot,
fileRootAliases: windowsAliases,
}),
).toEqual({ kind: "blocked" });
expect(resolveMarkdownFileTarget("c://example.com/file.ts")).toBeNull();
});
it("blocks contained paths that cannot be opened as files", () => {
expect(
resolveMarkdownFileTarget(`${workspaceRoot}/LICENSE`, {
workspaceRoot,
fileRootAliases: aliases,
}),
).toEqual({ kind: "blocked" });
expect(
resolveMarkdownFileTarget(`${siblingSourceRoot}/Makefile`, {
workspaceRoot,
fileRootAliases: aliases,
}),
).toEqual({ kind: "blocked" });
});
});
describe("resolveMarkdownFileTarget safety boundaries", () => {
it("rejects traversal and ambiguous aliases", () => {
expect(
resolveMarkdownFileTarget(`${siblingSourceRoot}/../kandev/secret.md`, {
workspaceRoot,
fileRootAliases: aliases,
}),
).toEqual({ kind: "blocked" });
expect(
resolveMarkdownFileTarget(`${siblingSourceRoot}/ui/bundle.js`, {
workspaceRoot,
fileRootAliases: [
...aliases,
{
repositoryId: "repo-other",
sourceRoot: siblingSourceRoot,
workspaceRelativeRoot: "other",
},
],
}),
).toEqual({ kind: "blocked" });
});
it("classifies unmatched host paths as blocked and leaves web links alone", () => {
expect(
resolveMarkdownFileTarget("/home/other-project/secret.md", {
workspaceRoot,
fileRootAliases: aliases,
}),
).toEqual({ kind: "blocked" });
expect(
resolveMarkdownFileTarget("/home/other-project/%E0%A4%A", {
workspaceRoot,
fileRootAliases: aliases,
}),
).toEqual({ kind: "blocked" });
expect(resolveMarkdownFileTarget("https://example.com/docs.md", { workspaceRoot })).toBeNull();
expect(resolveMarkdownFileTarget("/office/tasks/KAN-42", { workspaceRoot })).toBeNull();
});
it("preserves relative and workspace-root file links", () => {
expect(resolveMarkdownFileTarget("docs/README.md", { workspaceRoot })).toEqual({
kind: "file",
path: "docs/README.md",
});
expect(resolveMarkdownFileTarget("/docs/README.md", { workspaceRoot })).toEqual({
kind: "file",
path: "docs/README.md",
});
});
});