Files
rea/src/native/NativeCommandDeadline.test.ts
T

133 lines
4.4 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from "vitest";
import { projectAnalysisError } from "../domain/analysisErrorProjection.js";
import type { JsonValue } from "../domain/jsonValue.js";
import {
NativeFixtureRunner,
nativeMachoTarget,
} from "../../tests/fixtures/nativeCommands.js";
import {
OwnedCommandFailure,
runOwnedCommand,
} from "../process/OwnedCommand.js";
import type {
NativeCommandOptions,
NativeCommandRunner,
} from "./CommandRunner.js";
import { XcrunCommandRunner } from "./CommandRunner.js";
import { DEFAULT_NATIVE_COMMAND_TIMEOUT_MS } from "../domain/native/nativeDeadlines.js";
import { NativeMacOSProvider } from "./NativeMacOSProvider.js";
vi.mock("../process/OwnedCommand.js", async (original) => ({
...(await original<typeof import("../process/OwnedCommand.js")>()),
runOwnedCommand: vi.fn(),
}));
afterEach(() => {
vi.mocked(runOwnedCommand).mockReset();
});
class RecordingRunner implements NativeCommandRunner {
readonly timeouts: number[] = [];
readonly #inner = new NativeFixtureRunner();
run(
tool: string,
arguments_: readonly string[],
options: NativeCommandOptions,
) {
if (options.timeoutMs !== undefined) this.timeouts.push(options.timeoutMs);
return this.#inner.run(tool, arguments_, options);
}
}
const execute = async (
runner: NativeCommandRunner,
parameters: Readonly<Record<string, JsonValue>>,
) => {
const client = new NativeMacOSProvider({}, runner, "darwin").createClient(
nativeMachoTarget("/fixture/app"),
);
return client.execute("list_architectures", parameters);
};
describe("native_command_timeout_ms", () => {
it("uses 60000 ms when the caller omits the setting", async () => {
const runner = new RecordingRunner();
const result = await execute(runner, {});
expect(result.ok, JSON.stringify(result)).toBe(true);
expect(runner.timeouts.every((timeout) => timeout === 60_000)).toBe(true);
expect(DEFAULT_NATIVE_COMMAND_TIMEOUT_MS).toBe(60_000);
expect(runner.timeouts.length).toBeGreaterThan(0);
});
it("passes a selected deadline through to each native command", async () => {
const runner = new RecordingRunner();
const result = await execute(runner, { native_command_timeout_ms: 1_500 });
expect(result.ok, JSON.stringify(result)).toBe(true);
expect(runner.timeouts.every((timeout) => timeout === 1_500)).toBe(true);
});
it("passes the selected deadline through production xcrun resolution", async () => {
const calls: {
command: string;
arguments: readonly string[];
timeoutMs: number;
}[] = [];
vi.mocked(runOwnedCommand).mockImplementation(async (spawn, limits) => {
calls.push({
command: spawn.command,
arguments: spawn.arguments,
timeoutMs: limits.timeoutMs,
});
throw new OwnedCommandFailure("timeout", "Command deadline elapsed.");
});
const runner = new XcrunCommandRunner({});
const result = await runner.run("lipo", ["-info", "/fixture/app"], {
timeoutMs: 120_000,
});
expect(result.ok).toBe(false);
if (result.ok) throw new Error("expected resolution timeout");
expect(result.error.reason).toBe("timeout");
expect(calls).toEqual([
{
command: "/usr/bin/xcrun",
arguments: ["--find", "lipo"],
timeoutMs: 120_000,
},
]);
});
it("uses 60000 ms for xcrun resolution when the caller omits the deadline", async () => {
const timeouts: number[] = [];
vi.mocked(runOwnedCommand).mockImplementation(async (_spawn, limits) => {
timeouts.push(limits.timeoutMs);
throw new OwnedCommandFailure("timeout", "Command deadline elapsed.");
});
const runner = new XcrunCommandRunner({});
const result = await runner.run("file", ["--brief", "/fixture/app"], {});
expect(result.ok).toBe(false);
expect(timeouts).toEqual([60_000]);
});
it("rejects an out-of-range deadline before launching a command", async () => {
const runner = new RecordingRunner();
const result = await execute(runner, { native_command_timeout_ms: 0 });
if (result.ok) throw new Error("expected an invalid deadline");
expect(runner.timeouts).toEqual([]);
expect(projectAnalysisError(result.error)).toMatchObject({
code: "invalid_request",
details: {
issues: [
{
path: ["native_command_timeout_ms"],
reason: "out_of_range",
minimum: 1,
maximum: 2_147_483_647,
},
],
},
});
});
});