Files
rea/tests/boundary/providers/native/nativePlistSelection.test.ts
T
morluto e6c487799b fix(native): preserve resolved plist selection intent
Resolve lookup identity and selected/default origin together in the plist
source owner. Physical-file and conversion failures consume that fact
rather than reconstructing intent from omission of the path argument.
Selected plist artifacts resolve independently of readiness; unresolved
scope admission does not acquire an invented file-selection context.
Remove the superseded resolver and path/requested representation.

Cover direct-target CLI/MCP refusals, physical file conditions, permission
denial, default controls, unresolved scope context and cleanup retry for
both explicit path and selected-target workflows.

Fixes #4923. Related: #4849.

Validation: public CLI/MCP baseline3 failed8 controls passed; final55
passed across7files, check:fast, docs5, discovery1566 and source review.
Actual macOS26.3 plutil CLI/MCP; no Linux/Windows native qualification.
2026-10-18 08:11:46 +08:00

149 lines
5.0 KiB
TypeScript

import { chmod, mkdir, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect, it, onTestFinished } from "vitest";
import { NativeMacOSProvider } from "../../../../src/native/NativeMacOSProvider.js";
import {
resolveNativePlistSelection,
nativePlistFailure,
} from "../../../../src/native/NativePlistSource.js";
import { projectAnalysisError } from "../../../../src/domain/analysisErrorProjection.js";
import {
NativeFixtureRunner,
nativeMachoTarget,
} from "../../../fixtures/nativeCommands.js";
import { createTestTempDirectory } from "../../../fixtures/temporaryDirectory.js";
it("resolves selected paths and defaults without conflating intent and file readiness", async () => {
const root = await createTestTempDirectory("rea-plist-origin-");
const program = join(root, "program");
await writeFile(program, "fixture program");
const target = nativeMachoTarget(program);
const chosen = join(root, "chosen.plist");
expect(await resolveNativePlistSelection(target, chosen)).toEqual({
ok: true,
value: { path: chosen, origin: "selected" },
});
expect(await resolveNativePlistSelection(target, "chosen.plist")).toEqual({
ok: true,
value: { path: chosen, origin: "selected" },
});
expect(await resolveNativePlistSelection(target, undefined)).toEqual({
ok: true,
value: { path: join(root, "Contents/Info.plist"), origin: "default" },
});
const recorded = join(root, "Info.plist");
expect(
await resolveNativePlistSelection(
{ ...target, bundleInfoPlist: recorded },
undefined,
),
).toEqual({ ok: true, value: { path: recorded, origin: "default" } });
const plistTarget = {
kind: "artifact",
format: "plist",
path: chosen,
sha256: "0".repeat(64),
} as const;
expect(await resolveNativePlistSelection(plistTarget, undefined)).toEqual({
ok: true,
value: { path: chosen, origin: "selected" },
});
});
it.each(["missing", "directory"] as const)(
"retains selected target intent for a %s plist",
async (kind) => {
const root = await createTestTempDirectory("rea-plist-origin-file-");
const path = join(root, "chosen.plist");
if (kind === "directory") await mkdir(path);
const client = new NativeMacOSProvider(
{},
new NativeFixtureRunner(),
"darwin",
).createClient({
kind: "artifact",
format: "plist",
path,
sha256: "0".repeat(64),
});
onTestFinished(async () => {
await client.close();
});
const result = await client.execute("inspect_plist", {});
if (result.ok) throw new Error("Expected selected file refusal");
expect(projectAnalysisError(result.error)).toMatchObject({
code: "invalid_request",
details: {
issues: [
{
path: ["path"],
message: expect.stringContaining(
kind === "missing"
? "no file exists there"
: "not a regular file",
),
},
],
},
});
},
);
it.skipIf(process.getuid?.() === 0 || process.platform === "win32")(
"keeps permission failure distinct from selection and format",
async () => {
const root = await createTestTempDirectory("rea-plist-origin-denied-");
const path = join(root, "chosen.plist");
await writeFile(path, "<plist><dict/></plist>");
const client = new NativeMacOSProvider(
{},
new NativeFixtureRunner(),
"darwin",
).createClient({
kind: "artifact",
format: "plist",
path,
sha256: "0".repeat(64),
});
onTestFinished(async () => {
await chmod(path, 0o600);
await client.close();
});
await chmod(path, 0);
const result = await client.execute("inspect_plist", {});
if (result.ok) throw new Error("Expected access denial");
expect(projectAnalysisError(result.error)).toMatchObject({
code: "access_denied",
details: { path },
});
},
);
it("does not invent selection facts for unresolved scope failures", () => {
const cause = Object.assign(new Error("scope failure"), { code: "ENOENT" });
const error = nativePlistFailure(cause, undefined);
expect(error.cause).toBe(cause);
expect(error.diagnostics).toEqual({
phase: "plist-scope",
reason: "scope failure",
});
expect(projectAnalysisError(error).code).not.toBe("invalid_request");
expect(projectAnalysisError(error).code).not.toBe("capability_unavailable");
});
it("refuses closed client admission before resolving a target or launching tools", async () => {
const root = await createTestTempDirectory("rea-plist-origin-closed-");
const client = new NativeMacOSProvider(
{},
new NativeFixtureRunner(),
"darwin",
).createClient(nativeMachoTarget(join(root, "missing-program")));
expect((await client.close()).ok).toBe(true);
const result = await client.execute("inspect_plist", {});
if (result.ok) throw new Error("Expected closed scope");
expect(projectAnalysisError(result.error)).toMatchObject({
code: "capability_unavailable",
details: { reason: "Native provider client is closed" },
});
});