import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest' import { registerCleanup, graceful, clearCleanup, initShutdownHandlers } from '../../server/utils/shutdown.js' describe('shutdown', () => { let originalExit let exitCalls beforeEach(() => { clearCleanup() exitCalls = [] originalExit = process.exit process.exit = vi.fn((code) => { exitCalls.push(code) }) }) afterEach(() => { process.exit = originalExit clearCleanup() }) it('registers cleanup functions', () => { expect(() => { registerCleanup(async () => {}) }).not.toThrow() }) it('throws error for non-function cleanup', () => { expect(() => { registerCleanup('not a function') }).toThrow('Cleanup function must be a function') }) it('executes cleanup functions in reverse order', async () => { const calls = [] registerCleanup(async () => { calls.push('first') }) registerCleanup(async () => { calls.push('second') }) registerCleanup(async () => { calls.push('third') }) await graceful() expect(calls).toEqual(['third', 'second', 'first']) expect(exitCalls).toEqual([0]) }) it('handles cleanup function errors gracefully', async () => { registerCleanup(async () => { throw new Error('Cleanup error') }) registerCleanup(async () => { // This should still execute }) await graceful() expect(exitCalls).toEqual([0]) }) it('exits with code 1 on error', async () => { const error = new Error('Test error') await graceful(error) expect(exitCalls).toEqual([1]) }) it('prevents multiple shutdowns', async () => { let callCount = 0 registerCleanup(async () => { callCount++ }) await graceful() await graceful() expect(callCount).toBe(1) }) it('handles cleanup error during graceful shutdown', async () => { registerCleanup(async () => { throw new Error('Cleanup failed') }) await graceful() expect(exitCalls).toEqual([0]) }) it('handles error in executeCleanup catch block', async () => { registerCleanup(async () => { throw new Error('Test') }) await graceful() expect(exitCalls.length).toBeGreaterThan(0) }) it('handles error with stack trace', async () => { const error = new Error('Test error') error.stack = 'Error: Test error\n at test.js:1:1' await graceful(error) expect(exitCalls).toEqual([1]) }) it('handles error without stack trace', async () => { const error = { message: 'Test error' } await graceful(error) expect(exitCalls).toEqual([1]) }) it('handles timeout scenario', async () => { registerCleanup(async () => { await new Promise(resolve => setTimeout(resolve, 40000)) }) const timeout = setTimeout(() => { expect(exitCalls.length).toBeGreaterThan(0) }, 35000) graceful() await new Promise(resolve => setTimeout(resolve, 100)) clearTimeout(timeout) }) it('covers executeCleanup early return when already shutting down', async () => { registerCleanup(async () => {}) await graceful() await graceful() // Second call should return early expect(exitCalls.length).toBeGreaterThan(0) }) it('covers initShutdownHandlers', () => { const handlers = {} const originalOn = process.on process.on = vi.fn((signal, handler) => { handlers[signal] = handler }) initShutdownHandlers() expect(process.on).toHaveBeenCalledWith('SIGTERM', expect.any(Function)) expect(process.on).toHaveBeenCalledWith('SIGINT', expect.any(Function)) process.on = originalOn }) it('covers graceful catch block error path', async () => { // Force executeCleanup to throw by making cleanup throw synchronously registerCleanup(async () => { throw new Error('Force error in cleanup') }) await graceful() expect(exitCalls.length).toBeGreaterThan(0) }) })