86 lines
2.9 KiB
TypeScript
86 lines
2.9 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { spawnSync } from "node:child_process";
|
|
import { resolve } from "node:path";
|
|
import { pathToFileURL } from "node:url";
|
|
import { test } from "node:test";
|
|
import { allocatePortablePath, validatePortablePaths } from "./portable-path";
|
|
|
|
test("a small deeply nested manifest fits within a bounded validation heap", () => {
|
|
const source = pathToFileURL(resolve(__dirname, "portable-path.ts")).href;
|
|
const result = spawnSync(
|
|
process.execPath,
|
|
[
|
|
"--max-old-space-size=64",
|
|
"--import",
|
|
require.resolve("tsx"),
|
|
"--input-type=module",
|
|
"--eval",
|
|
`
|
|
import { validatePortablePaths } from ${JSON.stringify(source)};
|
|
const directory = Array(20000).fill("a").join("/");
|
|
validatePortablePaths([directory + "/one.md", directory + "/two.md"]);
|
|
`
|
|
],
|
|
{ encoding: "utf8", timeout: 10000 }
|
|
);
|
|
assert.equal(result.status, 0, result.stderr || String(result.error));
|
|
});
|
|
|
|
test("conflict allocation renames the first obstructed component deterministically", () => {
|
|
const cases: { wanted: string; occupied: string[]; expected: string }[] = [
|
|
{
|
|
wanted: "Notes/a.md",
|
|
occupied: ["Notes/b.md"],
|
|
expected: "Notes/a.md"
|
|
},
|
|
{
|
|
wanted: "Notes/a.md",
|
|
occupied: ["notes/b.md"],
|
|
expected: "Notes (conflict new)/a.md"
|
|
},
|
|
{
|
|
wanted: "Notes/a.md",
|
|
occupied: ["Notes"],
|
|
expected: "Notes (conflict new)/a.md"
|
|
},
|
|
{
|
|
wanted: "Notes",
|
|
occupied: ["Notes/a.md"],
|
|
expected: "Notes (conflict new)"
|
|
},
|
|
{
|
|
wanted: "note.md",
|
|
occupied: ["note.md", "note (conflict new).md"],
|
|
expected: "note (conflict new) (1).md"
|
|
},
|
|
{
|
|
wanted: "Straße/a.md",
|
|
occupied: ["STRASSE/b.md"],
|
|
expected: "Straße (conflict new)/a.md"
|
|
}
|
|
];
|
|
for (const { wanted, occupied, expected } of cases) {
|
|
const actual = allocatePortablePath(wanted, "new", occupied);
|
|
assert.equal(actual, expected);
|
|
assert.equal(
|
|
allocatePortablePath(wanted, "new", occupied.toReversed()),
|
|
actual
|
|
);
|
|
validatePortablePaths([...occupied, actual]);
|
|
}
|
|
});
|
|
|
|
test("shared directories remain distinct from aliases and file ancestors", () => {
|
|
validatePortablePaths(["Notes/one.md", "Notes/two.md", "Other/one.md"]);
|
|
for (const paths of [
|
|
["Notes/one.md", "notes/two.md"],
|
|
["Straße/one.md", "STRASSE/two.md"],
|
|
["Notes", "Notes/one.md"],
|
|
["Notes/one.md", "Notes"],
|
|
["Notes/one.md", "Notes/one.md"]
|
|
]) {
|
|
assert.throws(() => {
|
|
validatePortablePaths(paths);
|
|
}, /Conflicting path/);
|
|
}
|
|
});
|