mirror of
https://github.com/morluto/rea.git
synced 2026-10-31 11:56:12 +00:00
* fix(artifacts): preserve known metadata without content hashes * fix(artifacts): keep missing relation projections unknown
90 lines
3.5 KiB
TypeScript
90 lines
3.5 KiB
TypeScript
import { TextReader, Uint8ArrayWriter, ZipWriter } from "@zip.js/zip.js";
|
|
import { execFile } from "node:child_process";
|
|
import { writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { promisify } from "node:util";
|
|
import { expect, it } from "vitest";
|
|
|
|
import { artifactInspectionResultSchema } from "../../src/domain/artifactInspection.js";
|
|
import { parseEvidence } from "../../src/domain/evidence.js";
|
|
import { connectLocalToolsMcp } from "../fixtures/localToolsMcp.js";
|
|
import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js";
|
|
|
|
const cli = fileURLToPath(new URL("../../scripts/rea.mjs", import.meta.url));
|
|
const execute = promisify(execFile);
|
|
|
|
it("keeps observed entry differences when content identity is unknown", async () => {
|
|
const root = await createTestTempDirectory("rea-artifact-unavailable-");
|
|
const inspect = async (name: string, mode: number, content: string) => {
|
|
const writer = new ZipWriter(new Uint8ArrayWriter(), {
|
|
useWebWorkers: false,
|
|
});
|
|
await writer.add("entry", new TextReader(content), { unixMode: mode });
|
|
const path = join(root, `${name}.zip`);
|
|
await writeFile(path, Buffer.from(await writer.close()));
|
|
const result = await execute(process.execPath, [
|
|
cli,
|
|
"inspect-artifact",
|
|
path,
|
|
"--detail",
|
|
"complete",
|
|
"--json",
|
|
]);
|
|
const inspection = artifactInspectionResultSchema.parse(
|
|
parseEvidence(JSON.parse(result.stdout)).normalized_result,
|
|
);
|
|
const inventory = inspection.substeps.find(
|
|
({ operation }) => operation === "inventory_artifact",
|
|
)?.evidence;
|
|
if (inventory === undefined) throw new Error("Missing inventory Evidence");
|
|
return parseEvidence(inventory);
|
|
};
|
|
const symlink = await inspect("symlink", 0o120777, "target.txt");
|
|
const executable = await inspect("executable", 0o100755, "target\n");
|
|
const plain = await inspect("plain", 0o100644, "target\n");
|
|
const { call } = await connectLocalToolsMcp();
|
|
const compare = async (left: typeof symlink, right: typeof symlink) => {
|
|
const response = await call("compare_artifacts", { left, right });
|
|
expect(response.isError, JSON.stringify(response)).not.toBe(true);
|
|
return parseEvidence(response.structuredContent).normalized_result as {
|
|
status: string;
|
|
changes: {
|
|
logical_path: string;
|
|
classification: string;
|
|
dimensions: string[];
|
|
left_occurrence_id: string | null;
|
|
right_occurrence_id: string | null;
|
|
}[];
|
|
};
|
|
};
|
|
for (const [left, right] of [
|
|
[symlink, executable],
|
|
[executable, symlink],
|
|
] as const) {
|
|
const result = await compare(left, right);
|
|
expect(result.status).toBe("unknown");
|
|
const entry = result.changes.find(
|
|
({ logical_path }) => logical_path === "entry",
|
|
);
|
|
expect(entry?.dimensions).not.toContain("relations");
|
|
expect(entry).toMatchObject({
|
|
classification: "unknown",
|
|
dimensions: expect.arrayContaining([
|
|
"executable",
|
|
"metadata",
|
|
"availability",
|
|
]),
|
|
left_occurrence_id: expect.any(String),
|
|
right_occurrence_id: expect.any(String),
|
|
});
|
|
}
|
|
expect((await compare(symlink, symlink)).status).toBe("unknown");
|
|
expect(
|
|
(await compare(executable, plain)).changes.find(
|
|
({ logical_path }) => logical_path === "entry",
|
|
),
|
|
).toMatchObject({ classification: "changed", dimensions: ["executable"] });
|
|
expect((await compare(executable, executable)).status).toBe("unchanged");
|
|
});
|