| import { OAuthReconnectionTracker } from './OAuthReconnectionTracker'; |
|
|
| describe('OAuthReconnectTracker', () => { |
| let tracker: OAuthReconnectionTracker; |
| const userId = 'user123'; |
| const serverName = 'test-server'; |
| const anotherServer = 'another-server'; |
|
|
| beforeEach(() => { |
| tracker = new OAuthReconnectionTracker(); |
| }); |
|
|
| describe('setFailed', () => { |
| it('should record a failed reconnection attempt', () => { |
| tracker.setFailed(userId, serverName); |
| expect(tracker.isFailed(userId, serverName)).toBe(true); |
| }); |
|
|
| it('should track multiple servers for the same user', () => { |
| tracker.setFailed(userId, serverName); |
| tracker.setFailed(userId, anotherServer); |
|
|
| expect(tracker.isFailed(userId, serverName)).toBe(true); |
| expect(tracker.isFailed(userId, anotherServer)).toBe(true); |
| }); |
|
|
| it('should track different users independently', () => { |
| const anotherUserId = 'user456'; |
|
|
| tracker.setFailed(userId, serverName); |
| tracker.setFailed(anotherUserId, serverName); |
|
|
| expect(tracker.isFailed(userId, serverName)).toBe(true); |
| expect(tracker.isFailed(anotherUserId, serverName)).toBe(true); |
| }); |
| }); |
|
|
| describe('isFailed', () => { |
| it('should return false when no failed attempt is recorded', () => { |
| expect(tracker.isFailed(userId, serverName)).toBe(false); |
| }); |
|
|
| it('should return true after a failed attempt is recorded', () => { |
| tracker.setFailed(userId, serverName); |
| expect(tracker.isFailed(userId, serverName)).toBe(true); |
| }); |
|
|
| it('should return false for a different server even after another server failed', () => { |
| tracker.setFailed(userId, serverName); |
| expect(tracker.isFailed(userId, anotherServer)).toBe(false); |
| }); |
| }); |
|
|
| describe('removeFailed', () => { |
| it('should clear a failed reconnect record', () => { |
| tracker.setFailed(userId, serverName); |
| expect(tracker.isFailed(userId, serverName)).toBe(true); |
|
|
| tracker.removeFailed(userId, serverName); |
| expect(tracker.isFailed(userId, serverName)).toBe(false); |
| }); |
|
|
| it('should only clear the specific server for the user', () => { |
| tracker.setFailed(userId, serverName); |
| tracker.setFailed(userId, anotherServer); |
|
|
| tracker.removeFailed(userId, serverName); |
|
|
| expect(tracker.isFailed(userId, serverName)).toBe(false); |
| expect(tracker.isFailed(userId, anotherServer)).toBe(true); |
| }); |
|
|
| it('should handle clearing non-existent records gracefully', () => { |
| expect(() => tracker.removeFailed(userId, serverName)).not.toThrow(); |
| }); |
| }); |
|
|
| describe('setActive', () => { |
| it('should mark a server as reconnecting', () => { |
| tracker.setActive(userId, serverName); |
| expect(tracker.isActive(userId, serverName)).toBe(true); |
| }); |
|
|
| it('should track multiple reconnecting servers', () => { |
| tracker.setActive(userId, serverName); |
| tracker.setActive(userId, anotherServer); |
|
|
| expect(tracker.isActive(userId, serverName)).toBe(true); |
| expect(tracker.isActive(userId, anotherServer)).toBe(true); |
| }); |
| }); |
|
|
| describe('isActive', () => { |
| it('should return false when server is not reconnecting', () => { |
| expect(tracker.isActive(userId, serverName)).toBe(false); |
| }); |
|
|
| it('should return true when server is marked as reconnecting', () => { |
| tracker.setActive(userId, serverName); |
| expect(tracker.isActive(userId, serverName)).toBe(true); |
| }); |
|
|
| it('should handle non-existent user gracefully', () => { |
| expect(tracker.isActive('non-existent-user', serverName)).toBe(false); |
| }); |
| }); |
|
|
| describe('removeActive', () => { |
| it('should clear reconnecting state for a server', () => { |
| tracker.setActive(userId, serverName); |
| expect(tracker.isActive(userId, serverName)).toBe(true); |
|
|
| tracker.removeActive(userId, serverName); |
| expect(tracker.isActive(userId, serverName)).toBe(false); |
| }); |
|
|
| it('should only clear specific server state', () => { |
| tracker.setActive(userId, serverName); |
| tracker.setActive(userId, anotherServer); |
|
|
| tracker.removeActive(userId, serverName); |
|
|
| expect(tracker.isActive(userId, serverName)).toBe(false); |
| expect(tracker.isActive(userId, anotherServer)).toBe(true); |
| }); |
|
|
| it('should handle clearing non-existent state gracefully', () => { |
| expect(() => tracker.removeActive(userId, serverName)).not.toThrow(); |
| }); |
| }); |
|
|
| describe('cleanup behavior', () => { |
| it('should clean up empty user sets for failed reconnects', () => { |
| tracker.setFailed(userId, serverName); |
| tracker.removeFailed(userId, serverName); |
|
|
| |
| const anotherUserId = 'user456'; |
| tracker.setFailed(anotherUserId, serverName); |
|
|
| |
| expect(tracker.isFailed(userId, serverName)).toBe(false); |
| }); |
|
|
| it('should clean up empty user sets for active reconnections', () => { |
| tracker.setActive(userId, serverName); |
| tracker.removeActive(userId, serverName); |
|
|
| |
| const anotherUserId = 'user456'; |
| tracker.setActive(anotherUserId, serverName); |
|
|
| |
| expect(tracker.isActive(userId, serverName)).toBe(false); |
| }); |
| }); |
|
|
| describe('combined state management', () => { |
| it('should handle both failed and reconnecting states independently', () => { |
| |
| tracker.setActive(userId, serverName); |
| expect(tracker.isActive(userId, serverName)).toBe(true); |
| expect(tracker.isFailed(userId, serverName)).toBe(false); |
|
|
| |
| tracker.setFailed(userId, serverName); |
| expect(tracker.isActive(userId, serverName)).toBe(true); |
| expect(tracker.isFailed(userId, serverName)).toBe(true); |
|
|
| |
| tracker.removeActive(userId, serverName); |
| expect(tracker.isActive(userId, serverName)).toBe(false); |
| expect(tracker.isFailed(userId, serverName)).toBe(true); |
|
|
| |
| tracker.removeFailed(userId, serverName); |
| expect(tracker.isActive(userId, serverName)).toBe(false); |
| expect(tracker.isFailed(userId, serverName)).toBe(false); |
| }); |
| }); |
|
|
| describe('timeout behavior', () => { |
| beforeEach(() => { |
| jest.useFakeTimers(); |
| }); |
|
|
| afterEach(() => { |
| jest.useRealTimers(); |
| }); |
|
|
| it('should track timestamp when setting active state', () => { |
| const now = Date.now(); |
| jest.setSystemTime(now); |
|
|
| tracker.setActive(userId, serverName); |
| expect(tracker.isActive(userId, serverName)).toBe(true); |
|
|
| |
| jest.advanceTimersByTime(1000); |
| expect(tracker.isActive(userId, serverName)).toBe(true); |
| }); |
|
|
| it('should handle timeout checking with isStillReconnecting', () => { |
| const now = Date.now(); |
| jest.setSystemTime(now); |
|
|
| tracker.setActive(userId, serverName); |
| expect(tracker.isStillReconnecting(userId, serverName)).toBe(true); |
|
|
| |
| jest.advanceTimersByTime(2 * 60 * 1000 + 59 * 1000); |
| expect(tracker.isStillReconnecting(userId, serverName)).toBe(true); |
|
|
| |
| jest.advanceTimersByTime(2000); |
| expect(tracker.isStillReconnecting(userId, serverName)).toBe(false); |
|
|
| |
| expect(tracker.isActive(userId, serverName)).toBe(true); |
| }); |
|
|
| it('should handle multiple servers with different timeout periods', () => { |
| const now = Date.now(); |
| jest.setSystemTime(now); |
|
|
| |
| tracker.setActive(userId, serverName); |
| expect(tracker.isActive(userId, serverName)).toBe(true); |
|
|
| |
| jest.advanceTimersByTime(3 * 60 * 1000); |
|
|
| |
| tracker.setActive(userId, anotherServer); |
| expect(tracker.isActive(userId, anotherServer)).toBe(true); |
| expect(tracker.isActive(userId, serverName)).toBe(true); |
|
|
| |
| jest.advanceTimersByTime(2 * 60 * 1000 + 1); |
| expect(tracker.isStillReconnecting(userId, serverName)).toBe(false); |
| expect(tracker.isStillReconnecting(userId, anotherServer)).toBe(true); |
|
|
| |
| jest.advanceTimersByTime(3 * 60 * 1000); |
| expect(tracker.isStillReconnecting(userId, anotherServer)).toBe(false); |
| }); |
|
|
| it('should clear timestamp when removing active state', () => { |
| const now = Date.now(); |
| jest.setSystemTime(now); |
|
|
| tracker.setActive(userId, serverName); |
| expect(tracker.isActive(userId, serverName)).toBe(true); |
|
|
| tracker.removeActive(userId, serverName); |
| expect(tracker.isActive(userId, serverName)).toBe(false); |
|
|
| |
| jest.advanceTimersByTime(3 * 60 * 1000); |
| tracker.setActive(userId, serverName); |
| expect(tracker.isActive(userId, serverName)).toBe(true); |
|
|
| |
| jest.advanceTimersByTime(4 * 60 * 1000); |
| expect(tracker.isActive(userId, serverName)).toBe(true); |
| }); |
|
|
| it('should properly cleanup after timeout occurs', () => { |
| const now = Date.now(); |
| jest.setSystemTime(now); |
|
|
| tracker.setActive(userId, serverName); |
| tracker.setActive(userId, anotherServer); |
| expect(tracker.isActive(userId, serverName)).toBe(true); |
| expect(tracker.isActive(userId, anotherServer)).toBe(true); |
|
|
| |
| jest.advanceTimersByTime(6 * 60 * 1000); |
|
|
| |
| expect(tracker.isActive(userId, serverName)).toBe(true); |
| expect(tracker.isActive(userId, anotherServer)).toBe(true); |
| expect(tracker.isStillReconnecting(userId, serverName)).toBe(false); |
| expect(tracker.isStillReconnecting(userId, anotherServer)).toBe(false); |
|
|
| |
| expect(tracker.cleanupIfTimedOut(userId, serverName)).toBe(true); |
| expect(tracker.cleanupIfTimedOut(userId, anotherServer)).toBe(true); |
|
|
| |
| expect(tracker.isActive(userId, serverName)).toBe(false); |
| expect(tracker.isActive(userId, anotherServer)).toBe(false); |
| }); |
|
|
| it('should handle timeout check for non-existent entries gracefully', () => { |
| const now = Date.now(); |
| jest.setSystemTime(now); |
|
|
| |
| expect(tracker.isActive('non-existent', 'non-existent')).toBe(false); |
| expect(tracker.isStillReconnecting('non-existent', 'non-existent')).toBe(false); |
|
|
| |
| tracker.setActive(userId, serverName); |
| tracker.removeActive(userId, serverName); |
|
|
| |
| jest.advanceTimersByTime(6 * 60 * 1000); |
| expect(tracker.isActive(userId, serverName)).toBe(false); |
| expect(tracker.isStillReconnecting(userId, serverName)).toBe(false); |
| }); |
| }); |
|
|
| describe('isStillReconnecting', () => { |
| it('should return true for active entries within timeout', () => { |
| jest.useFakeTimers(); |
| const now = Date.now(); |
| jest.setSystemTime(now); |
|
|
| tracker.setActive(userId, serverName); |
| expect(tracker.isStillReconnecting(userId, serverName)).toBe(true); |
|
|
| |
| jest.advanceTimersByTime(3 * 60 * 1000); |
| expect(tracker.isStillReconnecting(userId, serverName)).toBe(true); |
|
|
| jest.useRealTimers(); |
| }); |
|
|
| it('should return false for timed out entries', () => { |
| jest.useFakeTimers(); |
| const now = Date.now(); |
| jest.setSystemTime(now); |
|
|
| tracker.setActive(userId, serverName); |
|
|
| |
| jest.advanceTimersByTime(6 * 60 * 1000); |
|
|
| |
| expect(tracker.isStillReconnecting(userId, serverName)).toBe(false); |
|
|
| |
| expect(tracker.isActive(userId, serverName)).toBe(true); |
|
|
| jest.useRealTimers(); |
| }); |
|
|
| it('should return false for non-existent entries', () => { |
| expect(tracker.isStillReconnecting('non-existent', 'non-existent')).toBe(false); |
| expect(tracker.isStillReconnecting(userId, serverName)).toBe(false); |
| }); |
| }); |
|
|
| describe('cleanupIfTimedOut', () => { |
| beforeEach(() => { |
| jest.useFakeTimers(); |
| }); |
|
|
| afterEach(() => { |
| jest.useRealTimers(); |
| }); |
|
|
| it('should cleanup timed out entries and return true', () => { |
| const now = Date.now(); |
| jest.setSystemTime(now); |
|
|
| tracker.setActive(userId, serverName); |
| expect(tracker.isActive(userId, serverName)).toBe(true); |
|
|
| |
| jest.advanceTimersByTime(6 * 60 * 1000); |
|
|
| |
| const wasCleanedUp = tracker.cleanupIfTimedOut(userId, serverName); |
| expect(wasCleanedUp).toBe(true); |
| expect(tracker.isActive(userId, serverName)).toBe(false); |
| }); |
|
|
| it('should not cleanup active entries and return false', () => { |
| const now = Date.now(); |
| jest.setSystemTime(now); |
|
|
| tracker.setActive(userId, serverName); |
|
|
| |
| jest.advanceTimersByTime(3 * 60 * 1000); |
|
|
| const wasCleanedUp = tracker.cleanupIfTimedOut(userId, serverName); |
| expect(wasCleanedUp).toBe(false); |
| expect(tracker.isActive(userId, serverName)).toBe(true); |
| }); |
|
|
| it('should return false for non-existent entries', () => { |
| const wasCleanedUp = tracker.cleanupIfTimedOut('non-existent', 'non-existent'); |
| expect(wasCleanedUp).toBe(false); |
| }); |
| }); |
|
|
| describe('timestamp tracking edge cases', () => { |
| beforeEach(() => { |
| jest.useFakeTimers(); |
| }); |
|
|
| afterEach(() => { |
| jest.useRealTimers(); |
| }); |
|
|
| it('should update timestamp when setting active on already active server', () => { |
| const now = Date.now(); |
| jest.setSystemTime(now); |
|
|
| tracker.setActive(userId, serverName); |
| expect(tracker.isActive(userId, serverName)).toBe(true); |
|
|
| |
| jest.advanceTimersByTime(3 * 60 * 1000); |
| expect(tracker.isActive(userId, serverName)).toBe(true); |
|
|
| |
| tracker.setActive(userId, serverName); |
|
|
| |
| jest.advanceTimersByTime(4 * 60 * 1000); |
| |
| expect(tracker.isActive(userId, serverName)).toBe(true); |
|
|
| |
| jest.advanceTimersByTime(2 * 60 * 1000); |
| |
| expect(tracker.isStillReconnecting(userId, serverName)).toBe(false); |
| }); |
|
|
| it('should handle same server for different users independently', () => { |
| const anotherUserId = 'user456'; |
| const now = Date.now(); |
| jest.setSystemTime(now); |
|
|
| tracker.setActive(userId, serverName); |
|
|
| |
| jest.advanceTimersByTime(3 * 60 * 1000); |
|
|
| tracker.setActive(anotherUserId, serverName); |
|
|
| |
| jest.advanceTimersByTime(3 * 60 * 1000); |
|
|
| |
| expect(tracker.isStillReconnecting(userId, serverName)).toBe(false); |
| |
| expect(tracker.isStillReconnecting(anotherUserId, serverName)).toBe(true); |
| }); |
| }); |
| }); |
|
|