Files
rea/tests/acceptance/tools/nativeFunctionMatching.test.ts
morluto 6b7aa124ed fix(native): own matching source validation and admission
Separate source corruption and selected-role errors from unexpected derivation failures. Normalize coordinates before body membership and conflict checks, reuse parsed requests, and account for outer observation arrays before validation copies.
2026-10-23 07:27:15 +08:00

524 lines
17 KiB
TypeScript

import { readFile, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect } from "vitest";
import { cliTest, type TestCli } from "../../support/cli/cliFixture.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js";
import { parseMcpToolError } from "../../fixtures/mcpToolError.js";
import {
nativeMatchingInventory,
nativeMatchingDetails,
nativeMatchingTarget,
} from "../../fixtures/nativeFunctionMatching.js";
import { createEvidence, parseEvidence } from "../../../src/domain/evidence.js";
import { nativeMatchingProvider } from "../../fixtures/nativeFunctionMatching.js";
import { procedureInfoSchema } from "../../../src/domain/native/procedureInfo.js";
import {
createEvidenceBundle,
parseEvidenceBundle,
} from "../../../src/domain/evidenceBundle.js";
import { nativeFunctionMatchingSchema } from "../../../src/domain/native/functionMatching.js";
const createMatchingFixture = async () => {
const root = await createTestTempDirectory("rea-native-matching-");
const ancestor = createEvidence(undefined, nativeMatchingProvider, {
operation: "fixture_provenance",
parameters: {},
result: { source: "Synthetic ancestor" },
});
const unrelated = createEvidence(undefined, nativeMatchingProvider, {
operation: "fixture_unrelated",
parameters: {},
result: { source: "Unselected synthetic record" },
});
const dependency = createEvidence(undefined, nativeMatchingProvider, {
operation: "fixture_dependency",
parameters: {},
result: { source: "Synthetic dependency" },
evidenceLinks: [ancestor.evidence_id],
});
const left = nativeMatchingInventory(
"1",
[
{ address: "0x10", value: "main" },
{ address: "0x20", value: "old_name" },
],
[dependency.evidence_id],
);
const right = nativeMatchingInventory("2", [
{ address: "0x30", value: "main" },
{ address: "0x40", value: "new_name" },
]);
const leftDetails = [
...nativeMatchingDetails("1", "0x10", "9000", true),
...nativeMatchingDetails("1", "0x20", "c3", true),
];
const rightDetails = [
...nativeMatchingDetails("2", "0x30", "9001", true),
...nativeMatchingDetails("2", "0x40", "c3", true),
];
const ancestorsPath = join(root, "ancestors.json");
await writeFile(
ancestorsPath,
JSON.stringify(createEvidenceBundle([ancestor, unrelated])),
);
const importedPath = join(root, "dependencies.json");
await writeFile(
importedPath,
JSON.stringify(createEvidenceBundle([ancestor, dependency])),
);
return {
root,
ancestor,
dependency,
unrelated,
left,
right,
leftDetails,
rightDetails,
ancestorsPath,
importedPath,
};
};
const verifyPortableCli = async (
cli: TestCli,
fixture: Awaited<ReturnType<typeof createMatchingFixture>>,
input: Record<string, unknown>,
evidenceId: string,
) => {
const {
root,
ancestor,
dependency,
unrelated,
left,
right,
leftDetails,
rightDetails,
ancestorsPath,
} = fixture;
const bundlePath = join(root, "sources.json"),
inputPath = join(root, "request.json"),
outputPath = join(root, "result.json");
await writeFile(
bundlePath,
JSON.stringify(
createEvidenceBundle([
left,
right,
...leftDetails,
...rightDetails,
dependency,
]),
),
);
const original = await readFile(bundlePath);
await writeFile(inputPath, JSON.stringify({ ...input, detail: "complete" }));
const cliFull = await cli.run({
arguments: [
"match-native-functions",
inputPath,
"--source_bundle",
ancestorsPath,
"--source_bundle",
bundlePath,
"--json",
],
});
expect(cliFull.exitCode, cliFull.stderr).toBe(0);
expect(parseEvidence(cliFull.json).evidence_id).toBe(evidenceId);
await writeFile(inputPath, JSON.stringify(input));
const cliSummary = await cli.run({
arguments: [
"match-native-functions",
inputPath,
"--source_bundle",
ancestorsPath,
"--source_bundle",
bundlePath,
"--evidence_output",
outputPath,
"--json",
],
});
expect(cliSummary.exitCode, cliSummary.stderr).toBe(0);
expect(cliSummary.json).toMatchObject({
summary: {
normalized_result: { parent_evidence_id: evidenceId },
},
evidence_bundle: { path: outputPath },
});
const bundle = parseEvidenceBundle(
JSON.parse(await readFile(outputPath, "utf8")),
);
const ids = new Set(bundle.records.map((x) => x.evidence_id));
for (const record of bundle.records)
for (const id of record.evidence_links)
expect(
ids.has(id),
`Missing dependency ${id} of ${record.evidence_id}`,
).toBe(true);
expect(ids.has(ancestor.evidence_id)).toBe(true);
expect(ids.has(dependency.evidence_id)).toBe(true);
expect(ids.has(unrelated.evidence_id)).toBe(false);
expect(ids.has(evidenceId)).toBe(true);
expect(await readFile(bundlePath)).toEqual(original);
const incomplete = await cli.run({
arguments: [
"match-native-functions",
inputPath,
"--source_bundle",
bundlePath,
"--evidence_output",
join(root, "incomplete.json"),
"--json",
],
});
expect(incomplete.json).toMatchObject({
code: "evidence_integrity_mismatch",
});
};
cliTest.for([{ profile: "full" }, { profile: "compact" }] as const)(
"shared public CLI/MCP matching retains sources, pages exact facts, and feeds selected dossier pairs directly to compare_functions ($profile)",
{ timeout: 60000 },
async ({ profile }, { cli }) => {
const fixture = await createMatchingFixture();
const { left, right, leftDetails, rightDetails, importedPath } = fixture;
const { call } = await connectLocalToolsMcp({
REA_MCP_INPUT_SCHEMA_PROFILE: profile,
});
const imported = await call("import_evidence_bundle", {
path: importedPath,
});
expect(imported.isError, JSON.stringify(imported)).not.toBe(true);
const full = await call("match_native_functions", {
left,
right,
left_observations: leftDetails,
right_observations: rightDetails,
detail: "complete",
});
expect(full.isError, JSON.stringify(full)).not.toBe(true);
const evidence = parseEvidence(full.structuredContent),
report = nativeFunctionMatchingSchema.parse(evidence.normalized_result);
expect(report.summary).toEqual({
matched: 2,
only_left: 0,
only_right: 0,
unresolved: 0,
});
const reference = (evidence_id: string) => ({
kind: "retained-evidence",
evidence_id,
});
const input = {
left: reference(left.evidence_id),
right: reference(right.evidence_id),
left_observations: leftDetails.map((x) => reference(x.evidence_id)),
right_observations: rightDetails.map((x) => reference(x.evidence_id)),
};
const summary = await call("match_native_functions", input);
expect(summary.isError, JSON.stringify(summary)).not.toBe(true);
expect(
parseEvidence(summary.structuredContent).normalized_result,
).toMatchObject({
kind: "summary",
artifact: null,
parent_evidence_id: evidence.evidence_id,
summary: { summary: report.summary },
});
expect(parseEvidence(summary.structuredContent).locations).not.toEqual(
expect.arrayContaining([
expect.objectContaining({ kind: "artifact-path" }),
]),
);
const page = await call("inspect_analysis_view", {
source: reference(evidence.evidence_id),
view: {
kind: "function-matches",
collection: "functions",
sides: ["left"],
fields: ["name", "body_sha256"],
offset: 1,
limit: 1,
},
});
expect(page.isError, JSON.stringify(page)).not.toBe(true);
expect(
parseEvidence(page.structuredContent).normalized_result,
).toMatchObject({
artifact: null,
coverage: { total: 2, next_offset: null },
items: [
{
name: "old_name",
function_id: `${left.evidence_id}/1`,
address: "0x20",
},
],
});
expect(parseEvidence(page.structuredContent).locations).not.toEqual(
expect.arrayContaining([
expect.objectContaining({ kind: "artifact-path" }),
]),
);
const compared = await call(
"compare_functions",
report.matches[0]!.compare_functions_input!,
);
expect(compared.isError, JSON.stringify(compared)).not.toBe(true);
expect(parseEvidence(compared.structuredContent).operation).toBe(
"compare_functions",
);
await verifyPortableCli(cli, fixture, input, evidence.evidence_id);
const missing = await call("match_native_functions", {
left: reference(`ev_${"a".repeat(64)}`),
right: reference(right.evidence_id),
});
expect(missing.isError).toBe(true);
},
);
cliTest(
"native matching rejects corrupt and misselected MCP sources without retaining them",
async () => {
const fixture = await createMatchingFixture();
const { left, right, leftDetails, root, importedPath } = fixture;
const { call } = await connectLocalToolsMcp();
const importedSources = await call("import_evidence_bundle", {
path: importedPath,
});
expect(importedSources.isError, JSON.stringify(importedSources)).not.toBe(
true,
);
const before = await call("get_evidence_bundle", {});
const corruptEnvelope = { ...left, normalized_result: [] };
const badCoordinate = nativeMatchingInventory("1", [
{ address: "not-an-address", value: "main" },
]);
const observationResult = procedureInfoSchema.parse(
nativeMatchingDetails("1", "0x10", "c3")[0]!.normalized_result,
);
const corruptObservation = createEvidence(
nativeMatchingTarget("1"),
nativeMatchingProvider,
{
operation: "procedure_info",
parameters: { procedure: "0x10" },
result: {
...observationResult,
entrypoint: "not-an-address",
},
},
);
for (const { request, expected } of [
{
request: { left: corruptEnvelope, right },
expected: { code: "evidence_integrity_mismatch" },
},
{
request: { left: badCoordinate, right },
expected: { code: "evidence_integrity_mismatch" },
},
{
request: { left, right, left_observations: [corruptObservation] },
expected: { code: "evidence_integrity_mismatch" },
},
{
request: { left: leftDetails[0], right },
expected: {
code: "invalid_request",
details: {
issues: [expect.objectContaining({ path: ["left"] })],
},
},
},
]) {
const rejected = await call("match_native_functions", {
...request,
detail: "complete",
});
expect(parseMcpToolError(rejected).error).toMatchObject(expected);
expect((await call("get_evidence_bundle", {})).structuredContent).toEqual(
before.structuredContent,
);
}
const corruptBundlePath = join(root, "corrupt-source.json");
await writeFile(
corruptBundlePath,
JSON.stringify(createEvidenceBundle([badCoordinate, right])),
);
const importedCorruptPayload = await call("import_evidence_bundle", {
path: corruptBundlePath,
});
expect(
importedCorruptPayload.isError,
JSON.stringify(importedCorruptPayload),
).not.toBe(true);
const retainedBefore = await call("get_evidence_bundle", {});
const retainedFailure = await call("match_native_functions", {
left: {
kind: "retained-evidence",
evidence_id: badCoordinate.evidence_id,
},
right: { kind: "retained-evidence", evidence_id: right.evidence_id },
detail: "complete",
});
expect(parseMcpToolError(retainedFailure).error).toMatchObject({
code: "evidence_integrity_mismatch",
details: {
reason: expect.stringContaining("Evidence validation failed"),
},
});
expect((await call("get_evidence_bundle", {})).structuredContent).toEqual(
retainedBefore.structuredContent,
);
const malformedOuterRequest = await call("match_native_functions", {
right,
detail: "complete",
});
expect(parseMcpToolError(malformedOuterRequest).error.code).toBe(
"invalid_request",
);
expect((await call("get_evidence_bundle", {})).structuredContent).toEqual(
retainedBefore.structuredContent,
);
},
60000,
);
cliTest(
"native matching CLI inline and portable sources report the same validation errors",
async ({ cli }) => {
const fixture = await createMatchingFixture();
const { left, right, leftDetails, root } = fixture;
const badCoordinate = nativeMatchingInventory("1", [
{ address: "not-an-address", value: "main" },
]);
const corruptBundlePath = join(root, "corrupt-source.json");
await writeFile(
corruptBundlePath,
JSON.stringify(createEvidenceBundle([badCoordinate, right])),
);
const requestPath = join(root, "wrong-role-request.json");
const portablePath = join(root, "wrong-role-sources.json");
const wrongRole = leftDetails[0]!;
const inlineRequest = { left: wrongRole, right, detail: "complete" };
const portableRequest = {
left: { kind: "retained-evidence", evidence_id: wrongRole.evidence_id },
right: { kind: "retained-evidence", evidence_id: right.evidence_id },
detail: "complete",
};
await writeFile(requestPath, JSON.stringify(inlineRequest));
const inline = await cli.run({
arguments: ["match-native-functions", requestPath, "--json"],
});
await writeFile(
portablePath,
JSON.stringify(createEvidenceBundle([wrongRole, right])),
);
await writeFile(requestPath, JSON.stringify(portableRequest));
const portable = await cli.run({
arguments: [
"match-native-functions",
requestPath,
"--source_bundle",
portablePath,
"--json",
],
});
expect(inline.exitCode).not.toBe(0);
expect(portable.exitCode).toBe(inline.exitCode);
const wrongRoleError = {
code: "invalid_request",
details: {
issues: [expect.objectContaining({ path: ["left"] })],
},
};
expect(inline.json).toMatchObject(wrongRoleError);
expect(portable.json).toMatchObject(wrongRoleError);
const corruptRequest = { left: badCoordinate, right, detail: "complete" };
await writeFile(requestPath, JSON.stringify(corruptRequest));
const corruptInline = await cli.run({
arguments: ["match-native-functions", requestPath, "--json"],
});
const corruptPortableRequest = {
left: {
kind: "retained-evidence",
evidence_id: badCoordinate.evidence_id,
},
right: { kind: "retained-evidence", evidence_id: right.evidence_id },
detail: "complete",
};
await writeFile(requestPath, JSON.stringify(corruptPortableRequest));
const corruptPortable = await cli.run({
arguments: [
"match-native-functions",
requestPath,
"--source_bundle",
corruptBundlePath,
"--json",
],
});
expect(corruptInline.json).toMatchObject({
code: "evidence_integrity_mismatch",
});
expect(corruptPortable.json).toMatchObject({
code: "evidence_integrity_mismatch",
});
const malformedBundlePath = join(root, "malformed-outer-frame.json");
await writeFile(malformedBundlePath, JSON.stringify({ records: [left] }));
const malformedBundle = await cli.run({
arguments: [
"match-native-functions",
requestPath,
"--source_bundle",
malformedBundlePath,
"--json",
],
});
expect(malformedBundle.exitCode).not.toBe(0);
expect(malformedBundle.json).toMatchObject({
code: "evidence_integrity_mismatch",
});
},
60000,
);
cliTest(
"native matching admits the request representation before authenticating observation carriers",
async () => {
const { call } = await connectLocalToolsMcp();
const left = nativeMatchingInventory("1", [
{ address: "0x10", value: "main" },
]);
const right = nativeMatchingInventory("2", [
{ address: "0x20", value: "main" },
]);
const before = await call("get_evidence_bundle", {});
const observations = Array.from({ length: 524288 }, () => ({}));
const response = await call("match_native_functions", {
left,
right,
left_observations: observations,
detail: "complete",
});
expect(parseMcpToolError(response).error).toMatchObject({
code: "resource_constraint",
details: {
operation: "match_native_functions",
resource: "capacity",
},
});
expect((await call("get_evidence_bundle", {})).structuredContent).toEqual(
before.structuredContent,
);
},
60000,
);