import { readFile, unlink, writeFile } from "node:fs/promises"; import { join } from "node:path"; import { expect } from "vitest"; import { parseEvidence, createEvidence } from "../../src/domain/evidence.js"; import { rustBinarySchema } from "../../src/domain/rust/rustBinary.js"; import { analysisViewResultSchema } from "../../src/domain/analysisView/analysisView.js"; import { readEvidenceBundle } from "../../src/application/EvidenceBundleFiles.js"; import { connectLocalToolsMcp } from "../fixtures/localToolsMcp.js"; import { parseMcpToolError } from "../fixtures/mcpToolError.js"; import { createRustImage, rustDependencyPayload, } from "../fixtures/rust/image.js"; import { createTestTempDirectory } from "../fixtures/temporaryDirectory.js"; import { cliTest } from "../support/cli/cliFixture.js"; /** Both authenticated retention and portable imports query the same captured facts. */ const assertRetainedPages = async ( call: Awaited>["call"], evidenceId: string, exported: string, ) => { for (const [filter, expected] of [ [ { names: ["same"], versions: ["2"], fields: ["name", "version"] }, [{ index: 1, name: "same", version: "2" }], ], [{ names: ["same"], indices: [0], fields: [] }, [{ index: 0 }]], [{ names: ["same"], versions: [], fields: [] }, []], ] as const) { const reply = await call("inspect_analysis_view", { source: { kind: "retained-evidence", evidence_id: evidenceId }, view: { kind: "rust", collection: "packages", ...filter }, }); expect(reply.isError).not.toBe(true); expect( parseEvidence(reply.structuredContent).normalized_result, ).toMatchObject({ page: { rows: expected } }); } for (const source of [ { kind: "retained-evidence", evidence_id: evidenceId }, { kind: "evidence-file", path: exported }, ]) { const reply = await call("inspect_analysis_view", { source, view: { kind: "rust", collection: "packages", names: ["same"], indices: [0, 1, 99], offset: 1, limit: 1, fields: [], }, }); expect(reply.isError, JSON.stringify(reply)).not.toBe(true); expect( parseEvidence(reply.structuredContent).normalized_result, ).toMatchObject({ parent_evidence_id: evidenceId, page: { rows: [{ index: 1 }] }, coverage: { total: 2, examined: 1, exhausted: true, next_offset: null, }, }); const edgeReply = await call("inspect_analysis_view", { source, view: { kind: "rust", collection: "edges", from_indices: [1, 3], to_indices: [0], fields: ["from_index", "to_index"], }, }); expect(edgeReply.isError).not.toBe(true); expect( parseEvidence(edgeReply.structuredContent).normalized_result, ).toMatchObject({ page: { collection: "edges", rows: [ { index: 1, from_index: 1, to_index: 0 }, { index: 3, from_index: 3, to_index: 0 }, ], }, }); const empty = await call("inspect_analysis_view", { source, view: { kind: "rust", collection: "packages", names: [], versions: ["1"], fields: ["name"], }, }); expect( parseEvidence(empty.structuredContent).normalized_result, ).toMatchObject({ page: { rows: [] }, coverage: { total: 0, exhausted: true }, }); } }; cliTest( "queries stable Rust packages and edges through real CLI/MCP and portable Evidence", async ({ cli }) => { const root = await createTestTempDirectory("rea-rust-views-"); const { call } = await connectLocalToolsMcp(); for (const format of ["elf", "pe", "macho"] as const) { const path = join(root, `source.${format}`); const output = join(root, `${format}-source.json`); const payload = rustDependencyPayload({ packages: [ { name: "same", version: "1", source: "crates.io", dependencies: [2], }, { name: "same", version: "2", source: "git+https://upstream.example/repo", dependencies: [0, 2], }, { name: "leaf", version: "1", source: "local" }, { name: "app", version: "1", source: "local", dependencies: [0, 1], root: true, }, ], }); await writeFile(path, createRustImage({ format, payload })); const fullReply = await call("inspect_rust_binary", { path }); expect(fullReply.isError).not.toBe(true); const full = parseEvidence(fullReply.structuredContent); const report = rustBinarySchema.parse(full.normalized_result); const explicit = await call("inspect_rust_binary", { path, detail: "complete", }); expect(parseEvidence(explicit.structuredContent)).toEqual(full); const selection = { collection: "packages", names: ["same"], fields: ["name", "version", "dependencies"], }; const direct = await call("inspect_rust_binary", { path, view: selection, }); expect(direct.isError, JSON.stringify(direct)).not.toBe(true); const selected = parseEvidence( direct.structuredContent, ).normalized_result; expect(selected).toMatchObject({ kind: "rust", parent_evidence_id: full.evidence_id, page: { collection: "packages", rows: [ { index: 0, name: "same", version: "1", dependencies: [2] }, { index: 1, name: "same", version: "2", dependencies: [0, 2] }, ], }, }); const summary = await call("inspect_rust_binary", { path, detail: "summary", }); expect(summary.isError).not.toBe(true); expect( parseEvidence(summary.structuredContent).normalized_result, ).toMatchObject({ kind: "summary", summary: { auditable_metadata: { counts: { packages: 4, edges: 5 }, format_version: null, }, }, source_coverage: { embedded_records: "complete", runtime_dependencies: "unknown", }, }); const delivered = await cli.run({ arguments: [ "inspect-rust-binary", path, "--view", JSON.stringify(selection), "--evidence_output", output, "--json", ], }); expect(delivered.exitCode, delivered.stderr).toBe(0); expect(delivered.json).toMatchObject({ summary: { normalized_result: selected }, }); const bundle = await readEvidenceBundle(output); if (!bundle.ok) throw bundle.error; expect( bundle.value.records.find( (row) => row.evidence_id === full.evidence_id, ), ).toEqual(full); const exported = join(root, `${format}-record.json`); await writeFile(exported, JSON.stringify(full)); await unlink(path); await assertRetainedPages(call, full.evidence_id, exported); const malformed = structuredClone(report); malformed.auditable_metadata!.dependency_edges[0]!.to_index = 999; if (full.subject == null) throw new Error("Missing complete Rust subject"); const invalid = createEvidence( { path, sha256: report.artifact.sha256, format: format === "macho" ? "mach-o" : format, ...(full.subject.architecture === null ? {} : { architecture: full.subject.architecture }), }, full.provider, { operation: full.operation, parameters: full.parameters, result: malformed, }, ); expect( parseMcpToolError( await call("inspect_analysis_view", { source: { kind: "inline", evidence: invalid }, view: { kind: "rust", collection: "edges" }, }), ).error, ).toMatchObject({ code: "invalid_request" }); } }, 60000, ); cliTest( "preserves unknown Rust metadata and refuses invalid compact CLI choices before reading", async ({ cli }) => { const root = await createTestTempDirectory("rea-rust-view-missing-"); const path = join(root, "plain.elf"); await writeFile( path, createRustImage({ format: "elf", omitSection: true }), ); const { call } = await connectLocalToolsMcp(); const summary = await call("inspect_rust_binary", { path, detail: "summary", }); expect(summary.isError).not.toBe(true); const view = analysisViewResultSchema.parse( parseEvidence(summary.structuredContent).normalized_result, ); expect(view).toMatchObject({ summary: { auditable_metadata: null }, source_coverage: { embedded_records: "unknown", runtime_dependencies: "unknown", }, }); const missing = join(root, "never-created"); for (const args of [ ["--detail", "summary"], ["--view", "{"], ["--detail", "complete", "--view", '{"collection":"packages"}'], ]) { const refused = await cli.run({ arguments: ["inspect-rust-binary", missing, ...args, "--json"], }); expect(refused.exitCode).toBe(1); expect(refused.json).toMatchObject({ code: "invalid_request" }); expect(JSON.stringify(refused.json)).not.toContain("ENOENT"); } expect((await readFile(path)).length).toBeGreaterThan(0); }, 30000, );