Files
jackwener__opencli/src/execution.test.ts
AstroHan ee59750ddb fix(execution): apply timeout to non-browser commands (#383)
Non-browser commands (`browser: false`) ran without any timeout
protection, even when `timeoutSeconds` was explicitly set. This wraps
the non-browser execution path with `runWithTimeout()` when the
adapter defines a positive `timeoutSeconds`.

Also adds an optional `hint` parameter to `TimeoutError` so the
non-browser path shows a relevant suggestion instead of the
browser-specific `OPENCLI_BROWSER_COMMAND_TIMEOUT` env var hint.
2026-03-27 14:54:28 +08:00

48 lines
1.6 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { executeCommand } from './execution.js';
import { TimeoutError } from './errors.js';
import { cli, Strategy } from './registry.js';
import { withTimeoutMs } from './runtime.js';
describe('executeCommand — non-browser timeout', () => {
it('applies timeoutSeconds to non-browser commands', async () => {
const cmd = cli({
site: 'test-execution',
name: 'non-browser-timeout',
description: 'test non-browser timeout',
browser: false,
strategy: Strategy.PUBLIC,
timeoutSeconds: 0.01,
func: () => new Promise(() => {}),
});
// Sentinel timeout at 200ms — if the inner 10ms timeout fires first,
// the error will be a TimeoutError with the command label, not 'sentinel'.
const error = await withTimeoutMs(executeCommand(cmd, {}), 200, 'sentinel timeout')
.catch((err) => err);
expect(error).toBeInstanceOf(TimeoutError);
expect(error).toMatchObject({
code: 'TIMEOUT',
message: 'test-execution/non-browser-timeout timed out after 0.01s',
});
});
it('skips timeout when timeoutSeconds is 0', async () => {
const cmd = cli({
site: 'test-execution',
name: 'non-browser-zero-timeout',
description: 'test zero timeout bypasses wrapping',
browser: false,
strategy: Strategy.PUBLIC,
timeoutSeconds: 0,
func: () => new Promise(() => {}),
});
// With timeout guard skipped, the sentinel fires instead.
await expect(
withTimeoutMs(executeCommand(cmd, {}), 50, 'sentinel timeout'),
).rejects.toThrow('sentinel timeout');
});
});