mirror of
https://github.com/morluto/rea
synced 2026-10-27 06:33:32 -04:00
fc935d0df3
Use the owning DEX/JVM and Ark/JavaScript component lists for runtime summaries. Remove the independent filename checks and Harmony path copy. Preserve native format checks and framework path conventions. Adapt @eco-null's proposal and regressions from PR #3627. Cover all eight supported source extensions and unrelated content, plus the Android inverse mismatch discovered during consumer review: .dex/.class files had no bytecode components but produced runtime hints. Validation: 44 focused checks, build, check:fast and docs:check pass. Six Harmony assertions and one Android built CLI assertion fail before this fix. 36 actual built CLI cost controls preserve four ordinary complete replies and projection IDs byte for byte. No heavy profiling, target or real deep-provider execution; npm release unverified. Fixes #3350 Fixes #4782
165 lines
5.9 KiB
TypeScript
165 lines
5.9 KiB
TypeScript
import { createHash } from "node:crypto";
|
|
import { readFile, writeFile } from "node:fs/promises";
|
|
import { resolve } from "node:path";
|
|
|
|
import { Client } from "@modelcontextprotocol/client";
|
|
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";
|
|
import { TextReader, Uint8ArrayWriter, ZipWriter } from "@zip.js/zip.js";
|
|
import { expect } from "vitest";
|
|
|
|
import { artifactInspectionResultSchema } from "../../src/domain/artifactInspection.js";
|
|
import { parseEvidence } from "../../src/domain/evidence.js";
|
|
import { harmonyApplicationProjectionResultSchema } from "../../src/domain/harmony/harmonyApplication.js";
|
|
import { workspaceCliTest as test } from "../support/cli/workspaceCliFixture.js";
|
|
|
|
test("preserves Harmony archive formats through the public CLI/MCP lifecycle", async ({
|
|
cli,
|
|
workspace,
|
|
}) => {
|
|
const writer = new ZipWriter(new Uint8ArrayWriter());
|
|
await writer.add(
|
|
"module.json",
|
|
new TextReader('{"module":{"name":"fixture"}}'),
|
|
);
|
|
await writer.add(
|
|
"ets/modules.abc",
|
|
new TextReader("opaque bytecode fixture"),
|
|
);
|
|
const javascriptPaths = ["cts", "jsx", "mts", "ts", "tsx"].map(
|
|
(extension) => `assets/js/index.${extension}`,
|
|
);
|
|
for (const path of javascriptPaths)
|
|
await writer.add(path, new TextReader("export const value = 1;"));
|
|
const bytes = await writer.close();
|
|
const environment = {
|
|
PATH: process.env.PATH ?? "",
|
|
HOME: workspace.root,
|
|
USERPROFILE: workspace.root,
|
|
XDG_CONFIG_HOME: workspace.path("config"),
|
|
XDG_CACHE_HOME: workspace.path("cache"),
|
|
XDG_STATE_HOME: workspace.path("state"),
|
|
NODE_OPTIONS: "--max-old-space-size=512",
|
|
REA_LOG_LEVEL: "silent",
|
|
};
|
|
const client = new Client({ name: "rea-harmony-acceptance", version: "1" });
|
|
const transport = new StdioClientTransport({
|
|
command: process.execPath,
|
|
args: [resolve("scripts/rea.mjs"), "mcp"],
|
|
env: environment,
|
|
stderr: "pipe",
|
|
});
|
|
try {
|
|
await client.connect(transport);
|
|
await client.listTools();
|
|
for (const { suffix, format } of [
|
|
{ suffix: "hap", format: "hap" },
|
|
{ suffix: "hsp", format: "hsp" },
|
|
{ suffix: "app", format: "app-pack" },
|
|
]) {
|
|
const path = workspace.path(`fixture.${suffix}`);
|
|
await writeFile(path, bytes);
|
|
const sha256 = createHash("sha256").update(bytes).digest("hex");
|
|
const cliReply = await cli.run({
|
|
arguments: ["inspect-artifact", path, "--format", "json"],
|
|
environment,
|
|
});
|
|
expect(cliReply.exitCode, cliReply.stderr).toBe(0);
|
|
const cliEvidence = parseEvidence(cliReply.json);
|
|
const cliInventory = artifactInspectionResultSchema.parse(
|
|
cliEvidence.normalized_result,
|
|
);
|
|
expect(cliInventory.subject).toMatchObject({
|
|
root_format: format,
|
|
root_sha256: sha256,
|
|
});
|
|
const opened = await client.callTool({
|
|
name: "open_binary",
|
|
arguments: { path },
|
|
});
|
|
expect(opened.isError, JSON.stringify(opened)).not.toBe(true);
|
|
expect(opened.structuredContent).toMatchObject({
|
|
result: { kind: "archive", format, sha256 },
|
|
});
|
|
const status = await client.callTool({
|
|
name: "binary_session",
|
|
arguments: {},
|
|
});
|
|
expect(status.isError, JSON.stringify(status)).not.toBe(true);
|
|
expect(status.structuredContent).toMatchObject({
|
|
result: { open: true, kind: "archive", format, sha256 },
|
|
});
|
|
const inspected = await client.callTool({
|
|
name: "inspect_artifact",
|
|
arguments: {},
|
|
});
|
|
expect(inspected.isError, JSON.stringify(inspected)).not.toBe(true);
|
|
const inventoryEvidence = parseEvidence(inspected.structuredContent);
|
|
expect(
|
|
artifactInspectionResultSchema.parse(
|
|
inventoryEvidence.normalized_result,
|
|
),
|
|
).toEqual(cliInventory);
|
|
|
|
const projected = await client.callTool({
|
|
name: "project_harmony_application_graph",
|
|
arguments: { inventory_evidence: [inventoryEvidence] },
|
|
});
|
|
expect(projected.isError, JSON.stringify(projected)).not.toBe(true);
|
|
const graph = harmonyApplicationProjectionResultSchema.parse(
|
|
parseEvidence(projected.structuredContent).normalized_result,
|
|
);
|
|
expect(graph).toMatchObject({
|
|
root_format: format,
|
|
root_sha256: sha256,
|
|
coverage: { status: "complete-within-inventory" },
|
|
});
|
|
expect(
|
|
graph.components.manifests.map(({ path: member }) => member),
|
|
).toEqual(["module.json"]);
|
|
expect(
|
|
graph.components.bytecode.map(({ path: member }) => member),
|
|
).toEqual(["ets/modules.abc"]);
|
|
expect(
|
|
graph.components.javascript.map(({ path: member }) => member),
|
|
).toEqual(javascriptPaths);
|
|
expect(graph.runtime_families).toEqual(["ark", "javascript"]);
|
|
const projectionInput = workspace.path("projection-input.json");
|
|
await writeFile(
|
|
projectionInput,
|
|
JSON.stringify({ inventory_evidence: [cliEvidence] }),
|
|
);
|
|
const cliProjection = await cli.run({
|
|
arguments: [
|
|
"project-harmony-application-graph",
|
|
projectionInput,
|
|
"--format",
|
|
"json",
|
|
],
|
|
environment,
|
|
});
|
|
expect(cliProjection.exitCode, cliProjection.stderr).toBe(0);
|
|
expect(
|
|
harmonyApplicationProjectionResultSchema.parse(
|
|
parseEvidence(cliProjection.json).normalized_result,
|
|
),
|
|
).toEqual(graph);
|
|
const closed = await client.callTool({
|
|
name: "close_binary",
|
|
arguments: {},
|
|
});
|
|
expect(closed.isError, JSON.stringify(closed)).not.toBe(true);
|
|
const empty = await client.callTool({
|
|
name: "binary_session",
|
|
arguments: {},
|
|
});
|
|
expect(empty.isError, JSON.stringify(empty)).not.toBe(true);
|
|
expect(empty.structuredContent).toMatchObject({
|
|
result: { open: false },
|
|
});
|
|
expect(await readFile(path)).toEqual(Buffer.from(bytes));
|
|
}
|
|
} finally {
|
|
await client.close();
|
|
}
|
|
}, 120_000);
|