added cli-covarage tool and fixed more tests

This commit is contained in:
Boki 2025-06-26 14:23:01 -04:00
parent b63e58784c
commit b845a8eade
57 changed files with 11917 additions and 295 deletions

View file

@ -10,6 +10,7 @@ import {
onShutdownMedium,
resetShutdown,
setShutdownTimeout,
shutdownAndExit,
Shutdown,
} from '../src';
import type { ShutdownOptions, ShutdownResult } from '../src/types';
@ -103,12 +104,12 @@ describe('Shutdown Comprehensive Tests', () => {
it('should handle negative timeout values', () => {
// Should throw for negative values
expect(() => setShutdownTimeout(-1000)).toThrow('Shutdown timeout must be positive');
expect(() => setShutdownTimeout(-1000)).toThrow('Shutdown timeout must be a positive number');
});
it('should handle zero timeout', () => {
// Should throw for zero timeout
expect(() => setShutdownTimeout(0)).toThrow('Shutdown timeout must be positive');
expect(() => setShutdownTimeout(0)).toThrow('Shutdown timeout must be a positive number');
});
});
@ -388,7 +389,7 @@ describe('Shutdown Comprehensive Tests', () => {
for (let i = 0; i < errorCount; i++) {
onShutdown(async () => {
throw new Error(`Error ${i}`);
throw new Error('Expected error');
}, `error-${i}`);
}
@ -397,30 +398,158 @@ describe('Shutdown Comprehensive Tests', () => {
expect(result.callbacksExecuted).toBe(successCount + errorCount);
expect(result.callbacksFailed).toBe(errorCount);
expect(result.success).toBe(false);
expect(result.error).toContain(`${errorCount} callbacks failed`);
});
});
describe('Global State Management', () => {
it('should properly reset global state', () => {
// Add some callbacks
onShutdown(async () => {});
onShutdownHigh(async () => {});
onShutdownLow(async () => {});
describe('shutdownAndExit', () => {
it('should call process.exit after shutdown', async () => {
// Mock process.exit
const originalExit = process.exit;
const exitMock = mock(() => {
throw new Error('Process exit called');
});
process.exit = exitMock as any;
expect(getShutdownCallbackCount()).toBe(3);
try {
const callback = mock(async () => {});
onShutdown(callback);
resetShutdown();
expect(getShutdownCallbackCount()).toBe(0);
expect(isShuttingDown()).toBe(false);
await expect(shutdownAndExit('SIGTERM', 1)).rejects.toThrow('Process exit called');
expect(callback).toHaveBeenCalledTimes(1);
expect(exitMock).toHaveBeenCalledWith(1);
} finally {
// Restore process.exit
process.exit = originalExit;
}
});
it('should maintain singleton across imports', () => {
const instance1 = Shutdown.getInstance();
const instance2 = Shutdown.getInstance();
it('should use default exit code 0', async () => {
const originalExit = process.exit;
const exitMock = mock(() => {
throw new Error('Process exit called');
});
process.exit = exitMock as any;
expect(instance1).toBe(instance2);
try {
await expect(shutdownAndExit()).rejects.toThrow('Process exit called');
expect(exitMock).toHaveBeenCalledWith(0);
} finally {
process.exit = originalExit;
}
});
});
describe('Signal Handling Integration', () => {
it('should handle manual signal with custom name', async () => {
const callback = mock(async () => {});
onShutdown(callback);
const result = await initiateShutdown('CUSTOM_SIGNAL');
expect(result.success).toBe(true);
expect(callback).toHaveBeenCalled();
});
it('should handle shutdown from getInstance without options', () => {
const instance = Shutdown.getInstance();
expect(instance).toBeInstanceOf(Shutdown);
// Call again to test singleton
const instance2 = Shutdown.getInstance();
expect(instance2).toBe(instance);
});
it('should handle global instance state correctly', () => {
// Start fresh
resetShutdown();
expect(getShutdownCallbackCount()).toBe(0);
// Add callback - this creates global instance
onShutdown(async () => {});
expect(getShutdownCallbackCount()).toBe(1);
// Reset and verify
resetShutdown();
expect(getShutdownCallbackCount()).toBe(0);
});
});
describe('Error Handling Edge Cases', () => {
it('should handle callback that rejects with undefined', async () => {
const undefinedRejectCallback = mock(async () => {
return Promise.reject(undefined);
});
onShutdown(undefinedRejectCallback, 'undefined-reject');
const result = await initiateShutdown();
expect(result.callbacksFailed).toBe(1);
expect(result.success).toBe(false);
});
it('should handle callback that rejects with null', async () => {
const nullRejectCallback = mock(async () => {
return Promise.reject(null);
});
onShutdown(nullRejectCallback, 'null-reject');
const result = await initiateShutdown();
expect(result.callbacksFailed).toBe(1);
expect(result.success).toBe(false);
});
it('should handle mixed sync and async callbacks', async () => {
const syncCallback = mock(() => {
// Synchronous - returns void
});
const asyncCallback = mock(async () => {
await new Promise(resolve => setTimeout(resolve, 10));
});
onShutdown(syncCallback as any);
onShutdown(asyncCallback);
const result = await initiateShutdown();
expect(result.callbacksExecuted).toBe(2);
expect(syncCallback).toHaveBeenCalled();
expect(asyncCallback).toHaveBeenCalled();
});
});
describe('Shutdown Method Variants', () => {
it('should handle direct priority parameter in onShutdown', () => {
const callback = mock(async () => {});
// Test with name and priority swapped (legacy support)
onShutdown(callback, 75, 'custom-name');
expect(getShutdownCallbackCount()).toBe(1);
});
it('should handle callback without any parameters', () => {
const callback = mock(async () => {});
onShutdown(callback);
expect(getShutdownCallbackCount()).toBe(1);
});
it('should validate setTimeout input', () => {
const shutdown = new Shutdown();
// Valid timeout
expect(() => shutdown.setTimeout(5000)).not.toThrow();
// Invalid timeouts should throw
expect(() => shutdown.setTimeout(-1)).toThrow();
expect(() => shutdown.setTimeout(0)).toThrow();
expect(() => shutdown.setTimeout(NaN)).toThrow();
});
});
});