Files
Alessio Caiazza 9c790a7d1c desktop: Drop wall-clock limits from mention tests
The hostile input tests compared elapsed time against fixed limits, so
they measured the machine as well as the code. They fail on slower or
loaded builders, such as the nixpkgs x86_64-linux check phase, even
though rendering stays linear.

Compare the CPU time of an input against one four times as long
instead. Linear rendering gives about 4 and a quadratic regression
about 16. CPU time is needed because a wall-clock ratio inflates under
bursty load, when a short run fits between bursts and a long one does
not.

Rendering past the reference limit gets its own test, since the link
count cannot catch a limit that stops linking but keeps rescanning.

The bare reference test now puts a valid reference before the long
run, so giving up is visible in the result without any timing.

AI-assisted: Claude Code (claude-opus-5-5)
2026-10-31 13:59:28 +01:00

360 lines
11 KiB
TypeScript

import type { Config } from "@bindings/config/Config";
import { describe, expect, test } from "vitest";
import { markdownWithExtensions } from "@app/lib/markdown";
import {
bareReferenceStart,
describeLink,
entityMarkdown,
entityUri,
entityUrl,
matchBareReference,
parseBareIdentifier,
parseEntityHref,
referenceMarkdown,
referenceRoute,
} from "@app/lib/mentions";
import { parseReference } from "@app/lib/radUri";
const repo = "z3gqcJUoA1n9HaHKufZs5FCSGazv5";
const rid = `rad:${repo}`;
const nid = "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi";
const oid = "5e4683023476778201ab25c87e1343fbfbe67c89";
describe("parseEntityHref", () => {
test.each([
[`did:key:${nid}`, { type: "node", nid }],
[rid, { type: "repo", rid }],
[`${rid}/commit/${oid}`, { type: "commit", rid, oid }],
[
`${rid}/cob/xyz.radicle.issue/${oid}`,
{ type: "cob", kind: "issue", rid, oid },
],
[
`${rid}/cob/xyz.radicle.patch/${oid}`,
{ type: "cob", kind: "patch", rid, oid },
],
[
`${rid}/cob/dev.radicle.artifact/${oid}`,
{ type: "cob", kind: "release", rid, oid },
],
[
`https://app.radicle.xyz/nodes/seed/${rid}/releases/${oid}`,
{ type: "cob", kind: "release", rid, oid },
],
[
`https://app.radicle.xyz/nodes/seed/${rid}/patches/${oid}`,
{ type: "cob", kind: "patch", rid, oid },
],
[` ${rid} `, { type: "repo", rid }],
])("%s", (href, expected) => {
expect(parseEntityHref(href)).toEqual(expected);
});
test.each([
["a tag", `${rid}/tag/v1.0`],
["a tree", `${rid}/tree/${oid}`],
["a commit by branch", `${rid}/commit/master`],
["an unknown cob type", `${rid}/cob/org.example/${oid}`],
["a cob list", `${rid}/cob/xyz.radicle.issue`],
["a namespace", `${rid}/${nid}`],
["a query", `${rid}/commit/${oid}?path=src`],
["a fragment", `${rid}/commit/${oid}#L10`],
["an unrelated link", "https://example.com"],
])("has no chip for %s", (_, href) => {
expect(parseEntityHref(href)).toBeUndefined();
});
});
describe("parseBareIdentifier", () => {
test.each([
[nid, { type: "node", nid }],
[repo, { type: "repo", rid }],
["zlib", undefined],
[oid, undefined],
])("%s", (token, expected) => {
expect(parseBareIdentifier(token)).toEqual(expected);
});
});
describe("bareReferenceStart", () => {
test.each([
[`${rid} at the start`, 0],
[`see ${rid}`, 4],
[`(did:key:${nid})`, 1],
[`Conrad:${repo}`, undefined],
[`Conrad:${repo} and ${rid}`, 41],
])("%s", (src, expected) => {
expect(bareReferenceStart(src)).toBe(expected);
});
});
describe("matchBareReference", () => {
test.each([
[`${rid}.`, rid],
[`${rid}).`, rid],
[`${rid}'s`, rid],
[`${rid}, then`, rid],
[`did:key:${nid}!`, `did:key:${nid}`],
[`${rid}/commit/master?path=src.`, `${rid}/commit/master?path=src`],
])("%s", (src, raw) => {
expect(matchBareReference(src)?.raw).toBe(raw);
});
test("gives up on a run too long to be a reference", () => {
expect(matchBareReference(`${rid}${"'a".repeat(50_000)}`)).toBeUndefined();
});
test("leaves an invalid reference as prose", () => {
expect(matchBareReference(`${rid}/bogus here`)).toBeUndefined();
});
});
describe("markdown", () => {
const render = (src: string) => markdownWithExtensions.parse(src) as string;
test("links a bare reference", () => {
expect(render(`See ${rid}.`)).toBe(
`<p>See <a href="${rid}">${rid}</a>.</p>\n`,
);
});
test("leaves the tail of a word alone", () => {
expect(render(`Conrad:${repo}`)).not.toContain("<a");
});
test("does not nest a link inside a link label", () => {
expect(render(`[see ${rid}](https://example.com)`)).toBe(
`<p><a href="https://example.com">see ${rid}</a></p>\n`,
);
});
test("leaves code alone", () => {
expect(render(`\`${rid}\``)).toBe(`<p><code>${rid}</code></p>\n`);
});
});
describe("serialising", () => {
const issue = { type: "cob", kind: "issue", rid, oid } as const;
test("entityUri writes the RIP 4 form", () => {
expect(entityUri(issue)).toBe(`${rid}/cob/xyz.radicle.issue/${oid}`);
expect(entityUri({ type: "node", nid })).toBe(`did:key:${nid}`);
expect(entityUri({ ...issue, kind: "patch" })).toBe(
`${rid}/cob/xyz.radicle.patch/${oid}`,
);
expect(entityUri({ type: "commit", rid, oid })).toBe(
`${rid}/commit/${oid}`,
);
});
test("referenceMarkdown links any reference", () => {
expect(
referenceMarkdown(
{
type: "uri",
uri: { repo, resource: { type: "commit", ref: "main" } },
},
"heartwood: README",
),
).toBe(`[heartwood: README](${rid}/commit/main)`);
});
test("entityMarkdown escapes the label", () => {
expect(entityMarkdown(issue, "Fix [x] \\ y")).toBe(
`[Fix \\[x\\] \\\\ y](${rid}/cob/xyz.radicle.issue/${oid})`,
);
});
test("entityUrl uses the configured explorer and preferred seed", () => {
const config = {
publicExplorer: "https://explorer.example/nodes/$host/$rid$path",
preferredSeeds: [`${nid}@seed.example.com:8776`],
} as Config;
expect(entityUrl(issue, config)).toBe(
`https://explorer.example/nodes/seed.example.com/${rid}/issues/${oid}`,
);
expect(entityUrl({ type: "node", nid }, config)).toBe(
`https://explorer.example/nodes/seed.example.com/users/did:key:${nid}`,
);
});
test("entityUrl keeps a path prefix in the explorer URL", () => {
const config = {
publicExplorer: "https://example.com/explorer/nodes/$host/$rid$path",
preferredSeeds: [],
} as unknown as Config;
expect(entityUrl({ type: "repo", rid }, config)).toBe(
`https://example.com/explorer/nodes/rosa.radicle.network/${rid}`,
);
});
});
describe("referenceRoute", () => {
test.each([
[
`${rid}/commit/master?path=.github/README.md`,
{
resource: "repo.home",
rid,
revision: "master",
path: ".github/README.md",
},
],
[
`${rid}/commit/main?path=My%20File.md`,
{ resource: "repo.home", rid, revision: "main", path: "My File.md" },
],
[
`${rid}/commit/refs/heads/main?path=`,
{ resource: "repo.home", rid, revision: "main" },
],
[
`${rid}/${nid}/commit/main?path=src`,
{ resource: "repo.home", rid, peer: nid, revision: "main", path: "src" },
],
[`${rid}/tag/v1.0`, { resource: "repo.home", rid, revision: "v1.0" }],
[`${rid}/commit/${oid}`, { resource: "repo.commit", rid, commit: oid }],
[
`${rid}/cob/xyz.radicle.issue/${oid}`,
{ resource: "repo.issue", rid, issue: oid, status: "all" },
],
[
`${rid}/cob/xyz.radicle.patch/${oid}`,
{
resource: "repo.patch",
rid,
patch: oid,
status: undefined,
reviewId: undefined,
},
],
[`${rid}/${nid}`, { resource: "repo.home", rid, peer: nid }],
[
`${rid}/cob/xyz.radicle.issue`,
{ resource: "repo.issues", rid, status: "all" },
],
[
`${rid}/cob/xyz.radicle.patch`,
{ resource: "repo.patches", rid, status: undefined },
],
[
`${rid}/cob/dev.radicle.artifact/${oid}`,
{ resource: "repo.release", rid, release: oid },
],
[`${rid}/cob/dev.radicle.artifact`, { resource: "repo.releases", rid }],
])("%s", (href, expected) => {
expect(referenceRoute(parseReference(href)!)).toEqual(expected);
});
test.each([`${rid}/tree/${oid}`, `did:key:${nid}`])(
"has no page for %s",
href => {
expect(referenceRoute(parseReference(href)!)).toBeUndefined();
},
);
});
describe("describeLink", () => {
test.each([
[`${rid}/commit/main?path=src/lib.rs`, "heartwood: src/lib.rs", "document"],
[
`${rid}/commit/main?path=src/lib.rs#L10`,
"heartwood: src/lib.rs#L10",
"document",
],
[`${rid}/commit/main?path=`, "heartwood: main", "folder"],
[`${rid}/commit/${oid}?path=`, "heartwood: 5e46830", "folder"],
[`${rid}/commit/refs/heads/main`, "heartwood: main", "branch"],
[`${rid}/tag/v1.0`, "heartwood: v1.0", "label"],
[`${rid}/cob/xyz.radicle.issue`, "heartwood: issues", "issue"],
[`${rid}/cob/xyz.radicle.patch`, "heartwood: patches", "patch"],
[`${rid}/cob/dev.radicle.artifact`, "heartwood: releases", "parcel"],
[
`${rid}/cob/dev.radicle.artifact/${oid}`,
"heartwood: release 5e46830",
"parcel",
],
[`${rid}/${nid}`, "heartwood: z6MknS…tBU8Vi", "repository"],
])("%s", (href, label, icon) => {
expect(describeLink(parseReference(href)!, "heartwood")).toMatchObject({
label,
icon,
});
});
test("names a remote by its alias", () => {
expect(
describeLink(parseReference(`${rid}/${nid}`)!, "heartwood", "cloudhead"),
).toMatchObject({ label: "heartwood: cloudhead" });
});
test.each([
rid,
`${rid}/commit/${oid}`,
`${rid}/cob/xyz.radicle.issue/${oid}`,
`${rid}/tree/${oid}`,
`${rid}/${nid}/cob/xyz.radicle.issue/${oid}`,
`did:key:${nid}`,
])("is not a link for %s", href => {
expect(describeLink(parseReference(href)!, "heartwood")).toBeUndefined();
});
});
describe("hostile input", () => {
const renderCpuTime = (src: string) => {
const start = process.cpuUsage();
void markdownWithExtensions.parse(src);
const { user, system } = process.cpuUsage(start);
return user + system;
};
const growthOverFourfoldInput = (unit: string, count: number) => {
let small = Infinity;
let large = Infinity;
for (let round = 0; round < 5; round++) {
small = Math.min(small, renderCpuTime(unit.repeat(count)));
large = Math.min(large, renderCpuTime(unit.repeat(4 * count)));
}
return large / small;
};
test(
"renders a run of references in linear time",
{ timeout: 120_000 },
() => {
expect(growthOverFourfoldInput("rad:", 1_250)).toBeLessThan(8);
},
);
test(
"renders references past the limit in linear time",
{ timeout: 120_000 },
() => {
expect(growthOverFourfoldInput(`${rid} `, 625)).toBeLessThan(8);
},
);
test("links at most a fixed number of references per document", () => {
const html = markdownWithExtensions.parse(
`${rid} `.repeat(1_000),
) as string;
expect(html.match(/<a /g)?.length).toBe(200);
});
test.each([
`${rid}/commit/../../../inbox`,
`${rid}/commit/.hidden`,
`${rid}/commit/main.lock`,
`${rid}/tag/v1.`,
`${rid}/commit/a..b?path=src`,
])("has no route or link for the unsafe ref in %s", href => {
const reference = parseReference(href)!;
expect(referenceRoute(reference)).toBeUndefined();
expect(describeLink(reference, "heartwood")).toBeUndefined();
});
});