mirror of
https://github.com/morluto/rea.git
synced 2026-11-04 05:46:17 +00:00
* test: strengthen integration oracles and verification * test: bound lifecycle gates and strengthen workflow checks * test: verify complete paged and extracted results * test: preserve bytecode and cancellation evidence * test: strengthen retained evidence and fixture cleanup * test: require cross-projection bridge identities * test: retain fixture owners through cleanup failures * test: verify graph examples and project selectors * test: tighten provider and lifecycle regressions * test: consolidate hex coverage and bound cutter cleanup * test: bound subprocess output waits and strengthen graph checks * test: check mcp recovery and preserve cleanup owners * test: verify structured mcp argument failures * test: preserve artifact cleanup and gdb limit evidence * test: preserve evidence identities and bound provider gates * test: retain browser and comparison cleanup owners * test: bound startup and strengthen fixture oracles * test: bound lifecycle gates and verify recovered evidence * test: verify public outcomes and lifecycle ownership * test: assert precise evidence and release lifecycle gates * test: verify selected facts and bound cleanup waits * test: check retained evidence and decoded records * test: verify final reports and retain cleanup ownership * test: bound reviewed startup gates and retain cleanup * test: verify PE errors and preserve MCP cleanup * test: preserve MCP fixture cleanup ownership * test: release provider gates before late startup
213 lines
7.1 KiB
TypeScript
213 lines
7.1 KiB
TypeScript
import { readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { expect, onTestFinished } from "vitest";
|
|
import { cliTest } from "../support/cli/cliFixture.js";
|
|
import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js";
|
|
import { parseEvidence } from "../../src/domain/evidence.js";
|
|
import { artifactInspectionResultSchema } from "../../src/domain/artifactInspection.js";
|
|
import {
|
|
artifactExtractionResultSchema,
|
|
artifactInventoryResultSchema,
|
|
} from "../../src/domain/artifactGraph.js";
|
|
import { dfuseFile } from "../../src/domain/firmware/dfuse.fixture.js";
|
|
import {
|
|
RP2040_FAMILY,
|
|
RP2350_ARM_S_FAMILY,
|
|
patternBytes,
|
|
uf2Block,
|
|
uf2File,
|
|
uf2Image,
|
|
} from "../../src/domain/firmware/uf2.fixture.js";
|
|
|
|
const RP2040_APP = patternBytes(300, 1);
|
|
const RP2350_APP = patternBytes(300, 2);
|
|
const FLASH = Uint8Array.from([1, 2, 3, 4]);
|
|
const OPTION_BYTES = Uint8Array.from([0xaa, 0x55]);
|
|
|
|
const cases = [
|
|
{
|
|
format: "uf2",
|
|
name: "universal.uf2",
|
|
bytes: uf2File([
|
|
...uf2Image(RP2040_FAMILY, 0x1000_0000, RP2040_APP),
|
|
...uf2Image(RP2350_ARM_S_FAMILY, 0x1000_0000, RP2350_APP),
|
|
]),
|
|
root: { kind: "uf2", family_count: 2, region_count: 2 },
|
|
regions: {
|
|
"family-0xe48bff56/0x10000000-0x1000012b.bin": RP2040_APP,
|
|
"family-0xe48bff59/0x10000000-0x1000012b.bin": RP2350_APP,
|
|
},
|
|
},
|
|
{
|
|
format: "dfuse",
|
|
name: "firmware.dfu",
|
|
bytes: dfuseFile({
|
|
targets: [
|
|
{
|
|
alternateSetting: 0,
|
|
name: "Internal Flash",
|
|
elements: [{ address: 0x0800_0000, data: FLASH }],
|
|
},
|
|
{
|
|
alternateSetting: 1,
|
|
name: "Option Bytes",
|
|
elements: [{ address: 0x1fff_c000, data: OPTION_BYTES }],
|
|
},
|
|
],
|
|
}),
|
|
root: { kind: "dfuse", target_count: 2, element_count: 2 },
|
|
regions: {
|
|
"target-0/element-0-0x08000000-0x08000003.bin": FLASH,
|
|
"target-1/element-0-0x1fffc000-0x1fffc001.bin": OPTION_BYTES,
|
|
},
|
|
},
|
|
] as const;
|
|
|
|
const graphFrom = (json: unknown) =>
|
|
artifactInventoryResultSchema.parse(
|
|
artifactInspectionResultSchema.parse(parseEvidence(json).normalized_result)
|
|
.substeps[0]?.evidence.normalized_result,
|
|
);
|
|
|
|
cliTest.for(cases)(
|
|
"inventories and extracts $format regions through the compiled CLI",
|
|
async ({ format, name, bytes, root, regions }, { cli }) => {
|
|
const directory = await createTestTempDirectory("rea-firmware-cli-");
|
|
const source = join(directory, name);
|
|
await writeFile(source, bytes);
|
|
const inspected = await cli.run({
|
|
arguments: ["inspect-artifact", source, "--json"],
|
|
});
|
|
expect(inspected.exitCode, inspected.stdout + inspected.stderr).toBe(0);
|
|
const inspection = artifactInspectionResultSchema.parse(
|
|
parseEvidence(inspected.json).normalized_result,
|
|
);
|
|
const graph = graphFrom(inspected.json);
|
|
expect(graph.manifest.root_format).toBe(format);
|
|
expect(graph.coverage.status).toBe("complete");
|
|
expect(
|
|
graph.occurrences.find((row) => row.logical_path === ".")
|
|
?.format_metadata,
|
|
).toMatchObject(root);
|
|
expect(
|
|
graph.occurrences
|
|
.filter((row) => row.logical_path !== ".")
|
|
.map((row) => row.logical_path),
|
|
).toEqual(Object.keys(regions));
|
|
expect(inspection.next_probes.map((probe) => probe.operation)).toEqual([
|
|
"open_binary",
|
|
]);
|
|
const extracted = await cli.run({
|
|
arguments: ["extract-artifact", source, "--json"],
|
|
environment: { TMPDIR: directory, TMP: directory, TEMP: directory },
|
|
});
|
|
expect(extracted.exitCode, extracted.stdout + extracted.stderr).toBe(0);
|
|
const result = artifactExtractionResultSchema.parse(
|
|
parseEvidence(extracted.json).normalized_result,
|
|
);
|
|
onTestFinished(() =>
|
|
rm(result.output_root, { recursive: true, force: true }),
|
|
);
|
|
expect(result.containment_verified).toBe(true);
|
|
for (const [path, expected] of Object.entries(regions))
|
|
expect(
|
|
Buffer.compare(
|
|
await readFile(join(result.output_root, path)),
|
|
expected,
|
|
),
|
|
).toBe(0);
|
|
},
|
|
);
|
|
|
|
cliTest(
|
|
"applies strict and record-and-continue policies to a DfuSe suffix CRC",
|
|
async ({ cli }) => {
|
|
const directory = await createTestTempDirectory("rea-dfuse-crc-cli-");
|
|
const source = join(directory, "firmware.dfu");
|
|
const valid = dfuseFile({
|
|
targets: [
|
|
{ alternateSetting: 0, elements: [{ address: 0, data: FLASH }] },
|
|
],
|
|
});
|
|
const observed = valid.readUInt32LE(valid.length - 4);
|
|
const corrupt = Buffer.from(valid);
|
|
corrupt.writeUInt32LE(0x1234_5678, corrupt.length - 4);
|
|
await writeFile(source, corrupt);
|
|
const rejected = await cli.run({
|
|
arguments: ["inspect-artifact", source, "--json"],
|
|
});
|
|
expect(rejected.exitCode).toBe(1);
|
|
expect(rejected.json).toMatchObject({
|
|
code: "artifact_integrity_mismatch",
|
|
details: {
|
|
logical_path: ".",
|
|
algorithm: "dfu-crc32",
|
|
declared_crc32: "12345678",
|
|
calculated_crc32: observed.toString(16).padStart(8, "0"),
|
|
unpacked: false,
|
|
},
|
|
});
|
|
const inspected = await cli.run({
|
|
arguments: [
|
|
"inspect-artifact",
|
|
source,
|
|
"--integrity-policy",
|
|
"record-and-continue",
|
|
"--json",
|
|
],
|
|
});
|
|
expect(inspected.exitCode, inspected.stdout + inspected.stderr).toBe(0);
|
|
expect(graphFrom(inspected.json).integrity_contradictions).toEqual([
|
|
expect.objectContaining({
|
|
logical_path: ".",
|
|
algorithm: "dfu-crc32",
|
|
declared_crc32: "12345678",
|
|
observed_crc32: observed.toString(16).padStart(8, "0"),
|
|
trust: "observed-untrusted",
|
|
}),
|
|
]);
|
|
// Extraction applies the same default policy and leaves no output behind.
|
|
const refused = await cli.run({
|
|
arguments: ["extract-artifact", source, "--json"],
|
|
environment: { TMPDIR: directory, TMP: directory, TEMP: directory },
|
|
});
|
|
expect(refused.exitCode).toBe(1);
|
|
expect(refused.json).toMatchObject({
|
|
code: "artifact_integrity_mismatch",
|
|
details: { logical_path: ".", algorithm: "dfu-crc32" },
|
|
});
|
|
expect(await readdir(directory)).toEqual(["firmware.dfu"]);
|
|
},
|
|
);
|
|
|
|
cliTest(
|
|
"refuses a conflicting UF2 rewrite through the CLI",
|
|
async ({ cli }) => {
|
|
const directory = await createTestTempDirectory("rea-uf2-conflict-cli-");
|
|
const source = join(directory, "firmware.uf2");
|
|
await writeFile(
|
|
source,
|
|
uf2File([
|
|
...uf2Image(RP2040_FAMILY, 0x1000_0000, RP2040_APP),
|
|
uf2Block({
|
|
address: 0x1000_0000,
|
|
payload: [0xff],
|
|
blockNo: 0,
|
|
numBlocks: 1,
|
|
familyId: RP2040_FAMILY,
|
|
}),
|
|
]),
|
|
);
|
|
const result = await cli.run({
|
|
arguments: ["inspect-artifact", source, "--json"],
|
|
});
|
|
expect(result.exitCode).toBe(1);
|
|
expect(result.json).toMatchObject({
|
|
details: { operation: "inspect_artifact", reason: "format" },
|
|
});
|
|
expect(result.stdout + result.stderr).toContain(
|
|
"UF2 is malformed: family 0xe48bff56 (RP2040) address 0x10000000 is written with different values by blocks 0-1 (0x01) and block 2 (0xff)",
|
|
);
|
|
},
|
|
);
|