mirror of
https://github.com/morluto/rea.git
synced 2026-11-13 10:12:50 +00:00
fix(macho): preserve import identity in retained views
Use distinct row contracts for imports and exports, retaining ambiguous names and historical import defaults without adding import-only fields to export pages. Verify the SDK-advertised schemas and both facets through MCP.
This commit is contained in:
1 parent
aa357d6d91
commit
23e68cbdfb
3 files changed
+124
-1
No files matched your search
@@ -107,6 +107,29 @@ const parent = (
|
||||
limitations: ["Parent limitation."],
|
||||
});
|
||||
|
||||
it("preserves ambiguous import facts without adding import fields to exports", () => {
|
||||
const imported = {
|
||||
...symbol("unused"),
|
||||
name: null,
|
||||
raw_name: "_value+0x4",
|
||||
name_status: "ambiguous",
|
||||
addend: null,
|
||||
};
|
||||
const observed = { ...report, imports: coverage([imported], false) };
|
||||
for (const facet of ["imports", "exports"] as const) {
|
||||
const projected = projectAnalysisView(parent(observed), {
|
||||
kind: "macho",
|
||||
facet,
|
||||
offset: 0,
|
||||
limit: 64,
|
||||
});
|
||||
if (!projected.ok) throw projected.error;
|
||||
expect(projected.value.kind === "macho" && projected.value.item).toEqual(
|
||||
observed[facet].items,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
describe("Mach-O analysis views", () => {
|
||||
it("summarizes slice identity and collection counts without rows", () => {
|
||||
const projected = projectAnalysisView(parent(), { kind: "summary" });
|
||||
|
||||
@@ -51,7 +51,8 @@ export const machoViewItemSchema = z.union([
|
||||
exactRows(shape.build_metadata.shape.items),
|
||||
exactRows(shape.load_commands.shape.items),
|
||||
exactRows(shape.dependencies.shape.items),
|
||||
// Imports and exports share one row shape.
|
||||
// Exports must precede imports, whose historical defaults also accept export keys.
|
||||
exactRows(shape.exports.shape.items),
|
||||
exactRows(shape.imports.shape.items),
|
||||
exactRows(shape.segments.shape.items),
|
||||
exactRows(shape.provenance),
|
||||
|
||||
@@ -14,6 +14,8 @@ import { parseMcpToolError } from "../../fixtures/mcpToolError.js";
|
||||
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
||||
import { mcpTest } from "../../support/mcp/mcpFixture.js";
|
||||
import { openProductionTarget } from "../../support/mcp/productionSession.js";
|
||||
import { createEvidence, parseEvidence } from "../../../src/domain/evidence.js";
|
||||
import { advertisedMcpToolSchemas } from "../../fixtures/advertisedMcpTool.js";
|
||||
|
||||
const evidenceOf = <Schema extends z.ZodType>(schema: Schema) =>
|
||||
z.object({
|
||||
@@ -41,6 +43,103 @@ const pageSchema = z.object({
|
||||
coverage: z.object({ next_offset: z.number().nullable() }),
|
||||
});
|
||||
|
||||
mcpTest(
|
||||
"pages ambiguous imports and exact exports under the advertised MCP schema",
|
||||
async ({ mcp, onTestFinished }) => {
|
||||
const empty = { items: [], total: 0, exhaustive: true, limitations: [] };
|
||||
const imported = {
|
||||
name: null,
|
||||
raw_name: "_value+0x4",
|
||||
name_status: "ambiguous",
|
||||
addend: null,
|
||||
address: null,
|
||||
weak: null,
|
||||
reexport: null,
|
||||
source: "libFixture",
|
||||
};
|
||||
const exported = {
|
||||
name: "_value",
|
||||
address: "0x1000",
|
||||
weak: null,
|
||||
reexport: null,
|
||||
source: null,
|
||||
};
|
||||
const report = inspectMachoSchema.parse({
|
||||
format: "mach-o",
|
||||
selected_architecture: { name: "arm64", container_file_offset: 0 },
|
||||
endian: "little",
|
||||
word_size: 64,
|
||||
file_type: "MH_DYLIB",
|
||||
flags: [],
|
||||
uuid: null,
|
||||
entrypoints: empty,
|
||||
architectures: empty,
|
||||
build_metadata: empty,
|
||||
load_commands: empty,
|
||||
dependencies: empty,
|
||||
imports: { ...empty, items: [imported], total: 1 },
|
||||
exports: { ...empty, items: [exported], total: 1 },
|
||||
segments: empty,
|
||||
provenance: [],
|
||||
limitations: [],
|
||||
});
|
||||
const source = createEvidence(
|
||||
undefined,
|
||||
{ id: "fixture", name: "Fixture", version: "1" },
|
||||
{
|
||||
operation: "inspect_macho",
|
||||
parameters: {},
|
||||
result: report,
|
||||
},
|
||||
);
|
||||
const session = createTestBinarySession(() => {
|
||||
throw new Error("Imported views must not start a provider");
|
||||
});
|
||||
onTestFinished(async () => {
|
||||
await session.close();
|
||||
});
|
||||
const client = await mcp.connect(
|
||||
createServer(
|
||||
{ kind: "session", session },
|
||||
{
|
||||
environment: { ...process.env, REA_MCP_INPUT_SCHEMA_PROFILE: "full" },
|
||||
},
|
||||
),
|
||||
);
|
||||
const schemas = await advertisedMcpToolSchemas(
|
||||
client,
|
||||
"inspect_analysis_view",
|
||||
);
|
||||
for (const [facet, expected] of [
|
||||
["imports", imported],
|
||||
["exports", exported],
|
||||
] as const) {
|
||||
const request = {
|
||||
source: { kind: "inline", evidence: source },
|
||||
view: { kind: "macho", facet },
|
||||
};
|
||||
expect(
|
||||
schemas.ajv.validate(schemas.inputSchema, request),
|
||||
JSON.stringify(schemas.ajv.errors),
|
||||
).toBe(true);
|
||||
const reply = await client.callTool({
|
||||
name: "inspect_analysis_view",
|
||||
arguments: request,
|
||||
});
|
||||
expect(reply.isError, JSON.stringify(reply.content)).not.toBe(true);
|
||||
expect(
|
||||
schemas.ajv.validate(schemas.outputSchema, reply.structuredContent),
|
||||
JSON.stringify(schemas.ajv.errors),
|
||||
).toBe(true);
|
||||
expect(
|
||||
pageSchema.parse(
|
||||
parseEvidence(reply.structuredContent).normalized_result,
|
||||
).item,
|
||||
).toEqual([expected]);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
describe.skipIf(process.platform !== "darwin")(
|
||||
"Mach-O summary delivery through MCP",
|
||||
() => {
|
||||
|
||||
Reference in new issue
Block a user