Files
reverse-engineering-anything/src/artifacts/ArtifactPaths.test.ts
T

218 lines
7.2 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
ArtifactPathRegistry,
destinationCaseCollision,
DestinationCaseCollisionFailure,
annotateEnglishUnicodeCaseCollisions,
ENGLISH_UNICODE_CASE_INVENTORY_LIMITATION,
normalizeArtifactPath,
} from "./ArtifactPaths.js";
it.each(["drive", "escaped", "normalization", "prefix", "collision"])(
"bounds path failure diagnostics at the owning boundary: %s",
(kind) => {
const name = "x".repeat(1024 * 1024);
const registry = new ArtifactPathRegistry();
const fail = () => {
if (kind === "drive") normalizeArtifactPath("C:/" + name);
else if (kind === "escaped")
normalizeArtifactPath("\0".repeat(name.length));
else if (kind === "normalization")
normalizeArtifactPath(name + "/../child");
else {
registry.add(name, "file");
registry.add(kind === "prefix" ? name + "/child" : name, "directory");
}
};
const failure = (() => {
try {
fail();
} catch (cause: unknown) {
return cause;
}
return undefined;
})();
expect(failure).toMatchObject({ reason: "path" });
expect((failure as Error).message.length).toBeLessThan(1024);
},
);
it.each(["a:b", "C:cache/file.txt", "a:"])(
"preserves a literal POSIX colon name %s",
(path) => {
expect(normalizeArtifactPath(path)).toBe(path);
},
);
const annotate = (paths: readonly string[]) => {
const occurrences = paths.map((logical_path) => ({
entry_kind: "file" as const,
logical_path,
limitations: [] as string[],
}));
const limitation = annotateEnglishUnicodeCaseCollisions(occurrences);
return {
limitation,
byPath: new Map(
occurrences.map(({ logical_path, limitations }) => [
logical_path,
limitations,
]),
),
};
};
const expectCollision = (
limitations: string[] | undefined,
...facts: string[]
) => {
expect(limitations).toHaveLength(1);
const warning = limitations?.[0] ?? "";
for (const fact of facts) expect(warning).toContain(fact);
expect(warning).toMatch(/collid.*English.*case folding/iu);
expect(warning).toMatch(/case.insensitive destination.*cannot store both/iu);
expect(warning).toMatch(/not a filesystem observation/iu);
};
describe("English Unicode case collisions", () => {
it("retains repeated same-kind entries while rejecting file-directory aliasing", () => {
const registry = new ArtifactPathRegistry();
registry.add("duplicate", "file");
expect(() => registry.add("duplicate", "file")).not.toThrow();
expect(() => registry.add("duplicate", "directory")).toThrow(
"Artifact path collision",
);
});
it("names the other spelling of a colliding file", () => {
const { limitation, byPath } = annotate([".", "Main.js", "main.js", "x"]);
expect(limitation).toBe(ENGLISH_UNICODE_CASE_INVENTORY_LIMITATION);
expectCollision(byPath.get("Main.js"), "Main.js", "main.js");
expectCollision(byPath.get("main.js"), "main.js", "Main.js");
expect(byPath.get("x")).toEqual([]);
});
it("records a directory collision once at the diverging segment", () => {
const { byPath } = annotate([
"res/Foo",
"res/Foo/a.txt",
"res/foo",
"res/foo/b.txt",
]);
expectCollision(byPath.get("res/Foo"), "res/Foo", "res/foo");
expectCollision(
byPath.get("res/foo/b.txt"),
"res/foo/b.txt",
"under res/foo",
"res/Foo",
);
});
it("counts further spellings, each of which is its own occurrence", () => {
const { byPath } = annotate(["A.txt", "a.txt", "a.TXT"]);
expectCollision(byPath.get("a.txt"), "a.txt", "A.txt", "1 other spelling");
});
it("retains every spelling in a high-cardinality folded sibling group", () => {
const count = 8_192;
const variants = caseVariants("abcdefghijklm", count);
const { limitation, byPath } = annotate(variants);
const ordered = [...variants].sort();
const first = ordered[0];
const second = ordered[1];
const original = variants[0];
if (first === undefined || second === undefined || original === undefined) {
throw new Error("Expected at least two case variants");
}
expect(limitation).toBe(ENGLISH_UNICODE_CASE_INVENTORY_LIMITATION);
expect(byPath.size).toBe(count);
expectCollision(
byPath.get(first),
first,
second,
`${String(count - 2)} other spellings`,
);
expect(byPath.get(original)?.[0]).toContain(
`with ${first} and ${String(count - 2)} other spellings;`,
);
expect(
[...byPath.values()].every((limitations) => limitations.length === 1),
).toBe(true);
});
it("bounds diagnostic lengths for large case-variant subtrees", () => {
const count = 5_000;
const paths = Array.from({ length: count }, (_, index) => [
`Foo/a${String(index)}.txt`,
`foo/b${String(index)}.txt`,
]).flat();
const { byPath } = annotate(paths);
for (const path of paths) {
const limitations = byPath.get(path) ?? [];
expect(limitations).toHaveLength(1);
expect(limitations[0]?.length).toBeLessThan(300);
}
});
it("walks names with more components than the call stack holds", () => {
const deep = Array.from({ length: 30_000 }, () => "a").join("/");
const { byPath } = annotate([deep, `${deep}/X`, `${deep}/x`]);
expect(byPath.get(`${deep}/X`)).toHaveLength(1);
expect(byPath.get(deep)).toEqual([]);
});
});
const caseVariants = (lowercase: string, count: number): string[] =>
Array.from({ length: count }, (_, mask) =>
[...lowercase]
.map((character, index) =>
(mask & (1 << index)) === 0 ? character : character.toUpperCase(),
)
.join(""),
);
it("retains both source spellings and the native failure in a destination collision", () => {
const cause = Object.assign(new Error("exclusive create refused"), {
code: "EEXIST",
});
const collision = destinationCaseCollision("res/café.js", ["CAFÉ.js"], {
cause,
});
expect(collision).toBeInstanceOf(DestinationCaseCollisionFailure);
expect(collision).toMatchObject({
reason: "path",
requestedPath: "res/café.js",
existingPath: "res/CAFÉ.js",
});
expect(collision?.cause).toBe(cause);
});
it.each([["app.js", "App.js"], ["other.js"]])(
"does not invent a destination alias from listing %j",
(...names) => {
expect(destinationCaseCollision("res/app.js", names)).toBeUndefined();
},
);
it("reserves prefix capacity atomically without declaring destination kinds", () => {
const registry = new ArtifactPathRegistry({ maxParts: 4, maxNodes: 3 });
registry.reservePath("a/b");
expect(() => registry.reservePath("a/c/d")).toThrow();
expect(() => registry.reservePath("z")).not.toThrow();
expect(() => registry.reservePath("a/b")).not.toThrow();
registry.add("a/b", "file");
expect(() => registry.add("a/b", "directory")).toThrow();
});
it("reports a file prefix conflict without inserting the candidate", () => {
const registry = new ArtifactPathRegistry({ maxParts: 4, maxNodes: 2 });
registry.reservePath("a/b");
expect(registry.conflicts("a/b")).toBe(true);
expect(registry.conflicts("a")).toBe(true);
expect(registry.conflicts("a/b/c")).toBe(true);
expect(registry.conflicts("a/c")).toBe(false);
expect(registry.conflicts("z")).toBe(false);
expect(() => registry.reservePath("z")).toThrow();
});