Files
rea/tests/boundary/cli/asarSummaryHashUncertainty.test.ts
T

90 lines
3.5 KiB
TypeScript

import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { createPackageWithOptions } from "@electron/asar";
import { expect } from "vitest";
import { z } from "zod";
import { artifactInspectionResultSchema } from "../../../src/domain/artifactInspection.js";
import { parseEvidence } from "../../../src/domain/evidence.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { cliTest } from "../../support/cli/cliFixture.js";
const summarySchema = z.object({
unknowns: z.array(z.string()),
summary: z.object({ source_complete: z.boolean() }),
});
cliTest(
"discloses unavailable ASAR content hashes in both summary routes and retains the parent",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-asar-summary-");
const source = join(root, "source");
await mkdir(join(source, "native"), { recursive: true });
await writeFile(join(source, "main.js"), "console.log(1);\n");
const bytes = "inert addon bytes\n";
await writeFile(join(source, "native", "addon.node"), bytes);
const archive = join(root, "app.asar");
await createPackageWithOptions(source, archive, { unpack: "*.node" });
const companion = join(`${archive}.unpacked`, "native", "addon.node");
const caveat =
"Content hashes are unknown for some recorded files; missing node IDs do not prove content absence.";
for (const phase of ["present", "missing", "restored"]) {
if (phase === "missing") await rm(companion);
if (phase === "restored") await writeFile(companion, bytes);
const bundle = join(root, `${phase}-evidence.json`);
const direct = await cli.run({
arguments: [
"inspect-artifact",
archive,
"--detail",
"summary",
"--evidence_output",
bundle,
"--json",
],
});
expect(direct.exitCode).toBe(0);
const compact = z
.object({ summary: z.object({ normalized_result: summarySchema }) })
.parse(direct.json);
const retained = z
.object({ records: z.array(z.unknown()) })
.parse(JSON.parse(await readFile(bundle, "utf8")));
const parent = retained.records
.map(parseEvidence)
.find((record) => record.operation === "inspect_artifact");
expect(parent).toBeDefined();
const request = join(root, "request.json");
await writeFile(
request,
JSON.stringify({
source: { kind: "inline", evidence: parent },
view: { kind: "summary" },
}),
);
const inline = await cli.run({
arguments: ["inspect-analysis-view", request, "--json"],
});
expect(inline.exitCode).toBe(0);
const summary = summarySchema.parse(
parseEvidence(inline.json).normalized_result,
);
const inspection = artifactInspectionResultSchema.parse(
parent!.normalized_result,
);
const occurrence = inspection.substeps
.flatMap((step) => step.evidence.normalized_result.occurrences)
.find((row) => row.logical_path === "native/addon.node");
expect(occurrence).toMatchObject({
hash_status: phase === "missing" ? "unavailable" : "verified",
...(phase === "missing" ? { artifact_id: null } : {}),
});
for (const result of [compact.summary.normalized_result, summary]) {
expect(result.summary.source_complete).toBe(true);
expect(result.unknowns.includes(caveat), phase).toBe(
phase === "missing",
);
}
}
},
);