* dist-tag 検証をリトライ付きに変更(npm レジストリの結果整合性対策) * takt run 実行時に蓋閉じスリープを抑制 * takt: github-issue-237-fix-ctrl-c-provider-graceful-timeout-force
174 lines
4.9 KiB
TypeScript
174 lines
4.9 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
const {
|
|
mockWarn,
|
|
mockError,
|
|
mockBlankLine,
|
|
mockGetLabel,
|
|
} = vi.hoisted(() => ({
|
|
mockWarn: vi.fn(),
|
|
mockError: vi.fn(),
|
|
mockBlankLine: vi.fn(),
|
|
mockGetLabel: vi.fn((key: string) => key),
|
|
}));
|
|
|
|
vi.mock('../shared/ui/index.js', () => ({
|
|
warn: mockWarn,
|
|
error: mockError,
|
|
blankLine: mockBlankLine,
|
|
}));
|
|
|
|
vi.mock('../shared/i18n/index.js', () => ({
|
|
getLabel: mockGetLabel,
|
|
}));
|
|
|
|
import { ShutdownManager } from '../features/tasks/execute/shutdownManager.js';
|
|
|
|
describe('ShutdownManager', () => {
|
|
let savedSigintListeners: ((...args: unknown[]) => void)[];
|
|
let originalShutdownTimeoutEnv: string | undefined;
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
savedSigintListeners = process.rawListeners('SIGINT') as ((...args: unknown[]) => void)[];
|
|
originalShutdownTimeoutEnv = process.env.TAKT_SHUTDOWN_TIMEOUT_MS;
|
|
delete process.env.TAKT_SHUTDOWN_TIMEOUT_MS;
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.useRealTimers();
|
|
process.removeAllListeners('SIGINT');
|
|
for (const listener of savedSigintListeners) {
|
|
process.on('SIGINT', listener as NodeJS.SignalsListener);
|
|
}
|
|
if (originalShutdownTimeoutEnv === undefined) {
|
|
delete process.env.TAKT_SHUTDOWN_TIMEOUT_MS;
|
|
} else {
|
|
process.env.TAKT_SHUTDOWN_TIMEOUT_MS = originalShutdownTimeoutEnv;
|
|
}
|
|
});
|
|
|
|
it('1回目SIGINTでgracefulコールバックを呼ぶ', () => {
|
|
const onGraceful = vi.fn();
|
|
const onForceKill = vi.fn();
|
|
|
|
const manager = new ShutdownManager({
|
|
callbacks: { onGraceful, onForceKill },
|
|
gracefulTimeoutMs: 1_000,
|
|
});
|
|
manager.install();
|
|
|
|
const listeners = process.rawListeners('SIGINT') as Array<() => void>;
|
|
listeners[listeners.length - 1]!();
|
|
|
|
expect(onGraceful).toHaveBeenCalledTimes(1);
|
|
expect(onForceKill).not.toHaveBeenCalled();
|
|
expect(mockWarn).toHaveBeenCalledWith('piece.sigintGraceful');
|
|
|
|
manager.cleanup();
|
|
});
|
|
|
|
it('graceful timeoutでforceコールバックを呼ぶ', () => {
|
|
vi.useFakeTimers();
|
|
const onGraceful = vi.fn();
|
|
const onForceKill = vi.fn();
|
|
|
|
const manager = new ShutdownManager({
|
|
callbacks: { onGraceful, onForceKill },
|
|
gracefulTimeoutMs: 50,
|
|
});
|
|
manager.install();
|
|
|
|
const listeners = process.rawListeners('SIGINT') as Array<() => void>;
|
|
listeners[listeners.length - 1]!();
|
|
vi.advanceTimersByTime(50);
|
|
|
|
expect(onGraceful).toHaveBeenCalledTimes(1);
|
|
expect(onForceKill).toHaveBeenCalledTimes(1);
|
|
expect(mockError).toHaveBeenCalledWith('piece.sigintTimeout');
|
|
expect(mockError).toHaveBeenCalledWith('piece.sigintForce');
|
|
|
|
manager.cleanup();
|
|
});
|
|
|
|
it('2回目SIGINTで即時forceコールバックを呼び、timeoutを待たない', () => {
|
|
vi.useFakeTimers();
|
|
const onGraceful = vi.fn();
|
|
const onForceKill = vi.fn();
|
|
|
|
const manager = new ShutdownManager({
|
|
callbacks: { onGraceful, onForceKill },
|
|
gracefulTimeoutMs: 10_000,
|
|
});
|
|
manager.install();
|
|
|
|
const listeners = process.rawListeners('SIGINT') as Array<() => void>;
|
|
const handler = listeners[listeners.length - 1]!;
|
|
handler();
|
|
handler();
|
|
vi.advanceTimersByTime(10_000);
|
|
|
|
expect(onGraceful).toHaveBeenCalledTimes(1);
|
|
expect(onForceKill).toHaveBeenCalledTimes(1);
|
|
expect(mockError).toHaveBeenCalledWith('piece.sigintForce');
|
|
|
|
manager.cleanup();
|
|
});
|
|
|
|
it('環境変数未設定時はデフォルト10_000msを使う', () => {
|
|
vi.useFakeTimers();
|
|
const onGraceful = vi.fn();
|
|
const onForceKill = vi.fn();
|
|
|
|
const manager = new ShutdownManager({
|
|
callbacks: { onGraceful, onForceKill },
|
|
});
|
|
manager.install();
|
|
|
|
const listeners = process.rawListeners('SIGINT') as Array<() => void>;
|
|
listeners[listeners.length - 1]!();
|
|
|
|
vi.advanceTimersByTime(9_999);
|
|
expect(onForceKill).not.toHaveBeenCalled();
|
|
|
|
vi.advanceTimersByTime(1);
|
|
expect(onForceKill).toHaveBeenCalledTimes(1);
|
|
|
|
manager.cleanup();
|
|
});
|
|
|
|
it('環境変数設定時はその値をtimeoutとして使う', () => {
|
|
vi.useFakeTimers();
|
|
process.env.TAKT_SHUTDOWN_TIMEOUT_MS = '25';
|
|
const onGraceful = vi.fn();
|
|
const onForceKill = vi.fn();
|
|
|
|
const manager = new ShutdownManager({
|
|
callbacks: { onGraceful, onForceKill },
|
|
});
|
|
manager.install();
|
|
|
|
const listeners = process.rawListeners('SIGINT') as Array<() => void>;
|
|
listeners[listeners.length - 1]!();
|
|
|
|
vi.advanceTimersByTime(24);
|
|
expect(onForceKill).not.toHaveBeenCalled();
|
|
|
|
vi.advanceTimersByTime(1);
|
|
expect(onForceKill).toHaveBeenCalledTimes(1);
|
|
|
|
manager.cleanup();
|
|
});
|
|
|
|
it('不正な環境変数値ではエラーをthrowする', () => {
|
|
process.env.TAKT_SHUTDOWN_TIMEOUT_MS = '0';
|
|
|
|
expect(
|
|
() =>
|
|
new ShutdownManager({
|
|
callbacks: { onGraceful: vi.fn(), onForceKill: vi.fn() },
|
|
}),
|
|
).toThrowError('TAKT_SHUTDOWN_TIMEOUT_MS must be a positive integer');
|
|
});
|
|
});
|