stock-bot/libs/core/shutdown/test/shutdown.test.ts
2025-06-25 08:29:53 -04:00

180 lines
5.4 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, mock } from 'bun:test';
import { Shutdown } from '../src/shutdown';
describe('Shutdown', () => {
let shutdown: Shutdown;
beforeEach(() => {
// Reset singleton instance for each test
(Shutdown as any).instance = null;
shutdown = Shutdown.getInstance({ timeout: 1000 });
});
afterEach(() => {
// Clean up
(Shutdown as any).instance = null;
});
describe('getInstance', () => {
it('should return singleton instance', () => {
const instance1 = Shutdown.getInstance();
const instance2 = Shutdown.getInstance();
expect(instance1).toBe(instance2);
});
it('should use provided config on first call', () => {
const instance = Shutdown.getInstance({ timeout: 5000 });
expect(instance).toBeDefined();
});
});
describe('handler registration', () => {
it('should register high priority handler', () => {
const handler = mock(async () => {});
shutdown.onShutdownHigh(handler, 'High Priority Task');
expect(shutdown['callbacks']).toHaveLength(1);
expect(shutdown['callbacks'][0].name).toBe('High Priority Task');
expect(shutdown['callbacks'][0].priority).toBe(10);
});
it('should register medium priority handler', () => {
const handler = mock(async () => {});
shutdown.onShutdownMedium(handler, 'Medium Priority Task');
expect(shutdown['callbacks']).toHaveLength(1);
expect(shutdown['callbacks'][0].priority).toBe(50);
});
it('should register low priority handler', () => {
const handler = mock(async () => {});
shutdown.onShutdownLow(handler, 'Low Priority Task');
expect(shutdown['callbacks']).toHaveLength(1);
expect(shutdown['callbacks'][0].priority).toBe(90);
});
it('should register multiple handlers in order', () => {
const handler1 = mock(async () => {});
const handler2 = mock(async () => {});
const handler3 = mock(async () => {});
shutdown.onShutdownHigh(handler1, 'First');
shutdown.onShutdownHigh(handler2, 'Second');
shutdown.onShutdownHigh(handler3, 'Third');
expect(shutdown['callbacks']).toHaveLength(3);
expect(shutdown['callbacks'][0].name).toBe('First');
expect(shutdown['callbacks'][2].name).toBe('Third');
});
});
describe('shutdown process', () => {
it('should execute handlers in priority order', async () => {
const executionOrder: string[] = [];
const highHandler = mock(async () => {
executionOrder.push('high');
});
const mediumHandler = mock(async () => {
executionOrder.push('medium');
});
const lowHandler = mock(async () => {
executionOrder.push('low');
});
shutdown.onShutdownLow(lowHandler, 'Low');
shutdown.onShutdownMedium(mediumHandler, 'Medium');
shutdown.onShutdownHigh(highHandler, 'High');
await shutdown.shutdown();
expect(executionOrder).toEqual(['high', 'medium', 'low']);
});
it('should only shutdown once', async () => {
const handler = mock(async () => {});
shutdown.onShutdownHigh(handler, 'Handler');
await shutdown.shutdown();
await shutdown.shutdown(); // Second call should be ignored
expect(handler).toHaveBeenCalledTimes(1);
});
it('should handle errors in handlers', async () => {
const errorHandler = mock(async () => {
throw new Error('Handler error');
});
const successHandler = mock(async () => {});
shutdown.onShutdownHigh(errorHandler, 'Error Handler');
shutdown.onShutdownHigh(successHandler, 'Success Handler');
await shutdown.shutdown();
expect(errorHandler).toHaveBeenCalled();
expect(successHandler).toHaveBeenCalled();
});
it('should respect timeout', async () => {
const slowHandler = mock(async () => {
await new Promise(resolve => setTimeout(resolve, 2000));
});
shutdown = Shutdown.getInstance({ timeout: 100 });
shutdown.onShutdownHigh(slowHandler, 'Slow Handler');
const start = Date.now();
await shutdown.shutdown();
const duration = Date.now() - start;
// Shutdown waits for timeout (100ms) plus some processing time
expect(duration).toBeGreaterThan(90);
expect(duration).toBeLessThan(1500); // But not the full 2000ms
});
});
describe('reset', () => {
it('should clear all handlers', () => {
shutdown.onShutdownHigh(async () => {}, 'Handler 1');
shutdown.onShutdownMedium(async () => {}, 'Handler 2');
shutdown.onShutdownLow(async () => {}, 'Handler 3');
// Manually clear callbacks to simulate reset
shutdown['callbacks'] = [];
expect(shutdown['callbacks']).toHaveLength(0);
});
it('should reset shutdown state', async () => {
const handler = mock(async () => {});
shutdown.onShutdownHigh(handler, 'Handler');
await shutdown.shutdown();
// Reset by creating new instance
(Shutdown as any).instance = null;
shutdown = Shutdown.getInstance({ timeout: 1000 });
shutdown.onShutdownHigh(handler, 'Handler');
await shutdown.shutdown();
expect(handler).toHaveBeenCalledTimes(2);
});
});
// Skip forceShutdown test as it's not implemented in current shutdown
describe.skip('forceShutdown', () => {
it('should exit process after timeout', async () => {
// Skipped
});
});
});