Files
reverse-engineering-anything/tests/boundary/cli/environmentBundleDestinationCli.test.ts
T
5e1d504664 fix(environment): admit the output root before creating its parents
environment-build/export ran an unclassified recursive mkdir of the
destination's parent before SafeOutputTree validated anything. A
trailing `..` therefore created directories before it was refused, and
a regular file in the parent path surfaced as unreadable_output instead
of refusing the selection.

SafeOutputTree.create gains a createParents option that admits the
root's own syntax first, then creates the parents, mapping EEXIST and
ENOTDIR to a destination refusal. The environment bundle writer uses it
instead of its own mkdir.

Fixes #7688
Fixes #7571

The non-directory mapping comes from #7734 by HeadmasterEggy, and the
syntax-first check from #7588 by harshitethic; their tests are kept.

Co-Authored-By: Avocado <headmastereggy@gmail.com>
Co-Authored-By: Harshit Sharma <66710144+harshitethic@users.noreply.github.com>
Co-Authored-By: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
2026-11-14 14:59:49 +01:00

100 lines
3.7 KiB
TypeScript

import { mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect } from "vitest";
import { writeEnvironmentServeFiles } from "../../fixtures/environmentServe.js";
import { parseMcpToolError } from "../../fixtures/mcpToolError.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { cliTest } from "../../support/cli/cliFixture.js";
cliTest.for(["environment-export", "environment-build"] as const)(
"%s attributes non-directory parents to the selected destination",
async (command, { cli }) => {
const root = await createTestTempDirectory("rea-bundle-parent-");
const files = await writeEnvironmentServeFiles(root);
const occupied = join(root, "parentfile");
await writeFile(occupied, "keep\n");
const before = await stat(occupied);
const entries = await readdir(root);
const call = await cli.sharedMcpCall?.();
if (call === undefined) throw new Error("Expected stdio MCP session");
for (const directory of [
join(occupied, "bundle"),
join(occupied, "missing", "bundle"),
]) {
const arguments_ =
command === "environment-export"
? [command, files.manifestPath, "--evidence", files.capturePath]
: [command, files.capturePath];
const result = await cli.run({
arguments: [...arguments_, directory, "--json"],
});
expect(result.exitCode, result.stderr).toBe(1);
expect(result.json).toMatchObject({
code: "invalid_request",
category: "invalid_input",
details: {
issues: [{ path: ["directory"], reason: "invalid_value" }],
},
recovery: {
kind: "inspect_input_contract",
operation: "environment-export",
},
});
const mcp = await call("export_environment_bundle", {
source: { kind: "evidence-file", path: files.capturePath },
output_directory: directory,
});
expect(mcp.isError).toBe(true);
expect(parseMcpToolError(mcp).error).toMatchObject({
code: "invalid_request",
category: "invalid_input",
details: {
issues: [{ path: ["output_directory"], reason: "invalid_value" }],
},
recovery: {
kind: "inspect_input_contract",
operation: "export_environment_bundle",
},
});
expect(await readFile(occupied, "utf8")).toBe("keep\n");
const after = await stat(occupied);
expect({ dev: after.dev, ino: after.ino }).toEqual({
dev: before.dev,
ino: before.ino,
});
expect(await readdir(root)).toEqual(entries);
}
},
);
cliTest.for(["environment-export", "environment-build"] as const)(
"%s still creates missing parents and accepts an empty destination",
async (command, { cli }) => {
const root = await createTestTempDirectory("rea-bundle-parent-control-");
const files = await writeEnvironmentServeFiles(root);
const empty = join(root, "empty");
await mkdir(empty);
for (const directory of [
join(root, "missing", "nested", "bundle"),
empty,
]) {
const arguments_ =
command === "environment-export"
? [command, files.manifestPath, "--evidence", files.capturePath]
: [command, files.capturePath];
const result = await cli.run({
arguments: [...arguments_, directory, "--json"],
});
expect(result.exitCode, result.stderr).toBe(0);
expect(result.json).toMatchObject({
status: command === "environment-export" ? "exported" : "built",
});
expect(
(
await readFile(join(directory, "envs/grafana-example/evidence.json"))
).equals(await readFile(files.capturePath)),
).toBe(true);
}
},
);