Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions tests/unit/middleware/legacy/ssh-timeout.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
jest.mock('node-ssh', () => {
const mockSSH = {
isConnected: jest.fn().mockReturnValue(false),
connect: jest.fn().mockResolvedValue(undefined),
dispose: jest.fn(),
};
return { NodeSSH: jest.fn(() => mockSSH) };
});

import { MirrorSQL } from '../../../../shared/database/src/legacy/sql.mirror';

describe('MirrorSQL SSH timeout stacking', () => {
beforeEach(() => {
jest.useFakeTimers();
// Reset the singleton between tests
(MirrorSQL as any)._instance = null;
(MirrorSQL as any)._ssh = null;
(MirrorSQL as any)._disposeTimer = null;
});

afterEach(() => {
jest.useRealTimers();
});

it('should only have one active timeout after multiple sshInstance() calls', async () => {
const setTimeoutSpy = jest.spyOn(global, 'setTimeout');
const clearTimeoutSpy = jest.spyOn(global, 'clearTimeout');

await MirrorSQL.sshInstance();
await MirrorSQL.sshInstance();
await MirrorSQL.sshInstance();

const timeoutCalls = setTimeoutSpy.mock.calls.filter(([, ms]) => ms === 5 * 60 * 1000);

expect(timeoutCalls).toHaveLength(3);
// After 3 calls, the first 2 timeouts should have been cleared
expect(clearTimeoutSpy).toHaveBeenCalledTimes(2);

setTimeoutSpy.mockRestore();
clearTimeoutSpy.mockRestore();
});

it('should not dispose SSH if timeout was superseded by a newer call', async () => {
const closeSpy = jest.spyOn(MirrorSQL.instance(), 'close');

await MirrorSQL.sshInstance();
// Advance partway — not enough to trigger
jest.advanceTimersByTime(4 * 60 * 1000);

await MirrorSQL.sshInstance();
// Advance past original 5 min mark — old timeout should have been cleared
jest.advanceTimersByTime(2 * 60 * 1000);

expect(closeSpy).not.toHaveBeenCalled();

// Advance to trigger the second (active) timeout
jest.advanceTimersByTime(3 * 60 * 1000);
expect(closeSpy).toHaveBeenCalledTimes(1);

closeSpy.mockRestore();
});
});
Loading