Record the admitted selected path separately from canonical artifact identity in successful and partial Evidence. Anchor relative inputs consistently across both CLI routes using the existing caller-path owner. Adapt shared client verifier assertions and boundary coverage from PR #5261, and the dedicated-route anchoring correction proposed in PR #5265. Historical parsing remains independent of live paths, including legacy records without the parameter. Validation: 118 focused tests in eight files, static and docs checks, discovery, and installed Gemini CLI 0.29.5 loopback verification of 207 schemas/241 examples passed. Synthetic static-analysis fixtures; no live Google API or native target execution. Fixes #5258 Co-authored-by: kwakhyun <khyun2348@naver.com> Co-authored-by: vishal-tambi <tambivishal3@gmail.com>
143 lines
5.2 KiB
TypeScript
143 lines
5.2 KiB
TypeScript
import {
|
|
mkdir,
|
|
readFile,
|
|
realpath,
|
|
rm,
|
|
symlink,
|
|
writeFile,
|
|
} from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { expect } from "vitest";
|
|
|
|
import { parseEvidence } from "../../../src/domain/evidence.js";
|
|
import { parseEvidenceBundle } from "../../../src/domain/evidenceBundle.js";
|
|
import { parseApplicationGraphEvidence } from "../../../src/application/javascript/JavaScriptApplicationEvidenceGraph.js";
|
|
import { JAVASCRIPT_APPLICATION_EVIDENCE_EXAMPLE } from "../../../src/contracts/javascript/javascriptRuntimeReconciliationExample.js";
|
|
import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
import { cliTest } from "../../support/cli/cliFixture.js";
|
|
|
|
cliTest(
|
|
"retains selected JavaScript path separately from resolved identity",
|
|
async ({ cli }) => {
|
|
const root = await createTestTempDirectory("rea-js-selected-path-");
|
|
const physicalPath = join(root, "physical-app");
|
|
const selectedPath = join(root, "selected-app");
|
|
await mkdir(physicalPath);
|
|
await writeFile(join(physicalPath, "main.js"), "export const value = 7;\n");
|
|
await symlink(
|
|
physicalPath,
|
|
selectedPath,
|
|
process.platform === "win32" ? "junction" : "dir",
|
|
);
|
|
const resolvedPath = await realpath(physicalPath);
|
|
expect(await realpath(selectedPath)).toBe(resolvedPath);
|
|
|
|
const cliResponse = await cli.run({
|
|
arguments: [
|
|
"analyze-javascript-application",
|
|
selectedPath,
|
|
"--artifact-format",
|
|
"directory",
|
|
"--json",
|
|
],
|
|
environment: { REA_LOG_LEVEL: "silent" },
|
|
});
|
|
expect(cliResponse.exitCode, cliResponse.stderr).toBe(0);
|
|
const cliEvidence = parseEvidence(cliResponse.json);
|
|
|
|
for (const command of ["analyze", "analyze-javascript-application"]) {
|
|
const relativeResponse = await cli.run({
|
|
arguments: [command, "./selected-app", "--json"],
|
|
cwd: root,
|
|
environment: { REA_LOG_LEVEL: "silent" },
|
|
});
|
|
expect(relativeResponse.exitCode, relativeResponse.stderr).toBe(0);
|
|
const relativeEvidence = parseEvidence(relativeResponse.json);
|
|
expect(relativeEvidence.parameters.input_path, command).toBe(
|
|
selectedPath,
|
|
);
|
|
expect(relativeEvidence.subject).toEqual(cliEvidence.subject);
|
|
expect(relativeEvidence.normalized_result).toEqual(
|
|
cliEvidence.normalized_result,
|
|
);
|
|
}
|
|
|
|
const { call } = await connectLocalToolsMcp({ REA_LOG_LEVEL: "silent" });
|
|
const aliasResponse = await call("analyze_javascript_application", {
|
|
input_path: selectedPath,
|
|
format: "directory",
|
|
integrity_policy: "fail",
|
|
});
|
|
expect(
|
|
aliasResponse.isError,
|
|
JSON.stringify(aliasResponse.content),
|
|
).not.toBe(true);
|
|
const aliasEvidence = parseEvidence(aliasResponse.structuredContent);
|
|
const physicalResponse = await call("analyze_javascript_application", {
|
|
input_path: physicalPath,
|
|
format: "directory",
|
|
integrity_policy: "fail",
|
|
});
|
|
expect(
|
|
physicalResponse.isError,
|
|
JSON.stringify(physicalResponse.content),
|
|
).not.toBe(true);
|
|
const physicalEvidence = parseEvidence(physicalResponse.structuredContent);
|
|
|
|
for (const evidence of [cliEvidence, aliasEvidence]) {
|
|
expect(evidence.parameters.input_path).toBe(selectedPath);
|
|
expect(evidence.subject?.local_path).toBe(resolvedPath);
|
|
expect(evidence.normalized_result).toMatchObject({
|
|
input_path: resolvedPath,
|
|
});
|
|
expect(parseApplicationGraphEvidence(evidence).ok).toBe(true);
|
|
}
|
|
expect(cliEvidence.normalized_result).toEqual(
|
|
aliasEvidence.normalized_result,
|
|
);
|
|
expect(physicalEvidence.parameters.input_path).toBe(physicalPath);
|
|
expect(physicalEvidence.subject?.local_path).toBe(resolvedPath);
|
|
expect(physicalEvidence.normalized_result).toEqual(
|
|
aliasEvidence.normalized_result,
|
|
);
|
|
expect(physicalEvidence.subject?.digest.sha256).toBe(
|
|
aliasEvidence.subject?.digest.sha256,
|
|
);
|
|
expect(physicalEvidence.evidence_id).not.toBe(aliasEvidence.evidence_id);
|
|
const bundlePath = join(root, "retained-evidence.json");
|
|
expect(
|
|
(await call("export_evidence_bundle", { path: bundlePath })).isError,
|
|
).not.toBe(true);
|
|
await rm(selectedPath);
|
|
await rm(physicalPath, { recursive: true });
|
|
const bundle = parseEvidenceBundle(
|
|
JSON.parse(await readFile(bundlePath, "utf8")),
|
|
);
|
|
const retained = bundle.records.find(
|
|
(record) => record.evidence_id === aliasEvidence.evidence_id,
|
|
);
|
|
expect(retained?.parameters.input_path).toBe(selectedPath);
|
|
for (const evidence of [
|
|
cliEvidence,
|
|
aliasEvidence,
|
|
physicalEvidence,
|
|
retained,
|
|
])
|
|
expect(
|
|
parseApplicationGraphEvidence(JSON.parse(JSON.stringify(evidence))).ok,
|
|
).toBe(true);
|
|
expect((await call("close_binary", {})).isError).not.toBe(true);
|
|
expect(
|
|
(await call("import_evidence_bundle", { path: bundlePath })).isError,
|
|
).not.toBe(true);
|
|
|
|
expect(
|
|
JAVASCRIPT_APPLICATION_EVIDENCE_EXAMPLE.parameters,
|
|
).not.toHaveProperty("input_path");
|
|
expect(
|
|
parseApplicationGraphEvidence(JAVASCRIPT_APPLICATION_EVIDENCE_EXAMPLE).ok,
|
|
).toBe(true);
|
|
},
|
|
);
|