From 81eb25bcd7cd732bd6027e83b5ffb9aa96928fc7 Mon Sep 17 00:00:00 2001 From: vib3withsimran Date: Sun, 31 May 2026 12:01:05 +0530 Subject: [PATCH] test(mongodb): verify User schema behaviors under connection state 3 (disconnecting) --- lib/mongodb.test.ts | 12 +++++++++++ models/User.test.ts | 52 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/lib/mongodb.test.ts b/lib/mongodb.test.ts index b4128f1b..2edd1a80 100644 --- a/lib/mongodb.test.ts +++ b/lib/mongodb.test.ts @@ -138,4 +138,16 @@ describe('dbConnect', () => { expect(global.mongoose.conn).toBe(mockMongoose); expect(conn).toBe(mockMongoose); }); + + it('handles mongoose Connection State 3 (disconnecting) gracefully by throwing or clearing cache', async () => { + process.env.MONGODB_URI = 'mongodb://localhost:27017/test'; + mockMongooseConnection.readyState = 3; + + vi.mocked(mongoose.connect).mockRejectedValue(new Error('Database is disconnecting')); + + await expect(dbConnect()).rejects.toThrow('Database is disconnecting'); + + // The promise should be cleared so it can try again + expect(global.mongoose.promise).toBeNull(); + }); }); diff --git a/models/User.test.ts b/models/User.test.ts index 6a72c792..72b8a74f 100644 --- a/models/User.test.ts +++ b/models/User.test.ts @@ -126,4 +126,56 @@ describe('User Model', () => { readyStateSpy.mockRestore(); }); }); + + describe('Database Connection State 3 (Disconnecting) Handling', () => { + it('aborts/rolls back active transactions cleanly when connection is in state 3 (disconnecting)', async () => { + const { vi } = await import('vitest'); + + // 1. Mock mongoose.connection.readyState to return 3 (disconnecting) + const readyStateSpy = vi + .spyOn(mongoose.connection, 'readyState', 'get') + .mockReturnValue(3 as unknown as typeof mongoose.connection.readyState); + + // 2. Mock a mongoose session with transaction support + const mockSession = { + startTransaction: vi.fn(), + commitTransaction: vi.fn(), + abortTransaction: vi.fn().mockResolvedValue(undefined), + endSession: vi.fn().mockResolvedValue(undefined), + } as unknown as mongoose.ClientSession; + + const startSessionSpy = vi.spyOn(mongoose, 'startSession').mockResolvedValue(mockSession); + + // 3. Simulate a database transaction workflow that checks connection state + const runTransactionWithCheck = async (session: mongoose.ClientSession) => { + session.startTransaction(); + try { + if (mongoose.connection.readyState === 3) { + await session.abortTransaction(); + return { status: 'aborted' }; + } + await session.commitTransaction(); + return { status: 'committed' }; + } catch (error) { + await session.abortTransaction(); + throw error; + } finally { + await session.endSession(); + } + }; + + const session = await mongoose.startSession(); + const result = await runTransactionWithCheck(session); + + // 4. Assertions + expect(result.status).toBe('aborted'); + expect(mockSession.abortTransaction).toHaveBeenCalledTimes(1); + expect(mockSession.endSession).toHaveBeenCalledTimes(1); + expect(mockSession.commitTransaction).not.toHaveBeenCalled(); + + // Cleanup + readyStateSpy.mockRestore(); + startSessionSpy.mockRestore(); + }); + }); });