import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import { TextReader, Uint8ArrayWriter, ZipWriter } from "@zip.js/zip.js";
import { expect } from "vitest";
import { appleApplicationProjectionResultSchema } from "../../../src/domain/apple/appleApplication.js";
import { compareUnicodeCodePoints } from "../../../src/domain/unicodeCodePointOrder.js";
import { parseEvidence } from "../../../src/domain/evidence.js";
import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { cliTest } from "../../support/cli/cliFixture.js";
cliTest(
"Apple launchd domains follow their owning directory through CLI and MCP",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-apple-launchd-cli-");
const environment = { HOME: root, USERPROFILE: root };
const expected = [
[
"LaunchDaemons/Sample.app/Contents/Library/LaunchAgents/agent.plist",
"agent",
],
[
"LaunchDaemons/Sample.app/Contents/Resources/LaunchDaemons/Nested.app/Contents/Library/LaunchAgents/nested.plist",
"agent",
],
[
"LaunchAgents/Other.app/Contents/Library/LaunchDaemons/daemon.plist",
"daemon",
],
[
"launchdaemons/Case.app/contents/library/launchagents/mixed.plist",
"agent",
],
] as const;
const writer = new ZipWriter(new Uint8ArrayWriter());
for (const [path] of expected)
await writer.add(path, new TextReader(""));
for (const path of [
"LaunchDaemons/Sample.app/Contents/Library/LaunchAgents/nested/ignored.plist",
"LaunchDaemons/Sample.app/Contents/Library/LaunchAgents/not-a-plist",
"Contents/Library/LaunchAgents/unrooted.plist",
"__MACOSX/Sample.app/Contents/Library/LaunchAgents/sidecar.plist",
])
await writer.add(path, new TextReader(""));
await writer.add(
"LaunchDaemons/Sample.app/Contents/Library/LaunchAgents/directory.plist/",
undefined,
{ directory: true },
);
const path = join(root, "Launchd.zip");
await writeFile(path, await writer.close());
const inspected = await cli.run({
arguments: ["inspect-artifact", path, "--json"],
environment,
});
expect(inspected.exitCode, inspected.stderr).toBe(0);
const source = parseEvidence(inspected.json);
const input = { inventory_evidence: [source] };
const projected = await cli.run({
arguments: [
"project-apple-application-graph",
JSON.stringify(input),
"--json",
],
environment,
});
expect(projected.exitCode, projected.stderr).toBe(0);
const evidence = parseEvidence(projected.json);
const result = appleApplicationProjectionResultSchema.parse(
evidence.normalized_result,
);
expect(
result.components.launchd_plists.map(({ path, domain }) => [
path,
domain,
]),
).toEqual(
[...expected].sort(([left], [right]) =>
compareUnicodeCodePoints(left, right),
),
);
expect(result.coverage).toEqual({
status: "complete-within-inventory",
inventory_complete: true,
});
const { call } = await connectLocalToolsMcp(environment);
const mcp = await call("project_apple_application_graph", input);
expect(mcp.isError).not.toBe(true);
expect(parseEvidence(mcp.structuredContent)).toEqual(evidence);
},
);