Files
rea/tests/boundary/providers/native/artifactProvider.directoryInventory.test.ts
T
Kaoru0822 a0e5e9db2b perf(artifacts)!: name each edge's child through its occurrence (#1493)
Every inventory edge is created from one occurrence and copied that
occurrence's artifact_id and logical_path as child_artifact_id and
logical_path. Edges now keep the occurrence_id reference alone. Edge IDs
still hash the same child and path, read from the occurrence, so they are
unchanged; validation and artifact comparison read the occurrence too.

inspect_artifact output: Transmission.app 1.10 MB to 0.97 MB, Visual
Studio Code.app 15.37 MB to 13.48 MB.

BREAKING CHANGE: Artifact inventory edges no longer include
child_artifact_id or logical_path; read them from the occurrence named by
occurrence_id. The graph commitment changes, so inventory Evidence
recorded by earlier versions no longer validates.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 00:58:05 -07:00

85 lines
3.2 KiB
TypeScript

import { mkdir, symlink, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { createTestTempDirectory } from "../../../fixtures/temporaryDirectory.js";
import { ArtifactProvider } from "../../../../src/artifacts/ArtifactProvider.js";
import { artifactInventoryResultSchema } from "../../../../src/domain/artifactGraph.js";
import type { BinaryTarget } from "../../../../src/domain/binaryTargetTypes.js";
describe("artifact directory inventory", () => {
it("inventories app trees deterministically without following symlinks", async () => {
const root = await createTestTempDirectory("rea-artifacts-");
const app = join(root, "Fixture.app");
await mkdir(join(app, "Contents", "Resources"), { recursive: true });
await mkdir(join(app, "Contents", "MacOS"), { recursive: true });
await mkdir(join(app, "Contents", "Frameworks", "Fixture.framework"), {
recursive: true,
});
const machO = Buffer.from([0xcf, 0xfa, 0xed, 0xfe, 0x0c, 0, 0, 1]);
await writeFile(join(app, "Contents", "MacOS", "Fixture"), machO, {
mode: 0o755,
});
await writeFile(
join(app, "Contents", "Frameworks", "Fixture.framework", "Fixture"),
machO,
);
await writeFile(join(app, "Contents", "Resources", "main.js"), "main();\n");
await writeFile(join(app, "Contents", "Resources", "main.js.MAP"), "{}\n");
await symlink("/etc/passwd", join(app, "Contents", "Resources", "outside"));
const client = new ArtifactProvider(process.env).createClient(
target(app, "directory"),
);
const input = {};
const first = await client.execute("inventory_artifact", input);
const second = await client.execute("inventory_artifact", input);
expect(first.ok).toBe(true);
expect(second.ok).toBe(true);
if (!first.ok || !second.ok) return;
const left = artifactInventoryResultSchema.parse(first.value.result);
const right = artifactInventoryResultSchema.parse(second.value.result);
expect(left.manifest).toEqual(right.manifest);
expect(left.occurrences.map(({ artifact_kind }) => artifact_kind)).toEqual(
expect.arrayContaining([
"executable",
"framework",
"javascript",
"source-map",
]),
);
expect(
left.occurrences.find(({ logical_path: path }) =>
path.endsWith("outside"),
),
).toMatchObject({ artifact_id: null, entry_kind: "symlink" });
const script = left.occurrences.find(({ logical_path: path }) =>
path.endsWith("main.js"),
);
const sourceMap = left.occurrences.find(({ logical_path: path }) =>
path.endsWith("main.js.MAP"),
);
expect(left.edges).toEqual(
expect.arrayContaining([
expect.objectContaining({
relation: "maps-source",
parent_artifact_id: script?.artifact_id,
occurrence_id: sourceMap?.occurrence_id,
}),
]),
);
});
});
const target = (
path: string,
format: Extract<BinaryTarget, { kind: "archive" }>["format"] | "directory",
): BinaryTarget => ({
path,
sourcePath: path,
sha256: "0".repeat(64),
kind: "archive",
format: format === "directory" ? "asar" : format,
});