Skip to content

Commit 823d06e

Browse files
authored
test: handle state 3 transaction rollbacks and clean up dbConnect tests (JhaSourav07#2976)
## Description Fixes JhaSourav07#1414 What Changed: -User Model (models/User.test.ts): Added a comprehensive test case to verify that active database transactions are cleanly aborted and rolled back when the Mongoose connection enters State 3 (disconnecting). -Connection Utility (lib/mongodb.test.ts): Cleaned up the test suite by removing accidentally duplicated State 3 tests, ensuring the test file runs efficiently while preserving the critical concurrent connection handling test. ## Pillar - [ ] 🎨 Pillar 1 — New Theme Design - [ ] 📐 Pillar 2 — Geometric SVG Improvement - [ ] 🕐 Pillar 3 — Timezone Logic Optimization - [x] 🛠️ Other (Bug fix, refactoring, docs) ## Visual Preview N/A — Backend test coverage and refactoring only. ## Checklist before requesting a review: - [x] I have read the `CONTRIBUTING.md` file. - [x] I have tested these changes locally (`localhost:3000/api/streak?user=YOUR_USERNAME`). - [x] I have run `npm run format` and `npm run lint` locally and resolved all errors (CI will fail otherwise). - [x] My commits follow the Conventional Commits format (e.g., `feat(themes): ...`, `fix(calculate): ...`). - [x] I have updated `README.md` if I added a new theme or URL parameter. - [x] I have started the repo. - [x] I have made sure that i have only one commit to merge in this PR. - [x] The SVG output matches the CommitPulse "premium quality" aesthetic standard (no raw elements, smooth animations, correct fonts). - [x] (Recommended) I joined the CommitPulse Discord community for contributor discussions, mentorship, and faster PR support.
2 parents 238c0aa + 11bdd09 commit 823d06e

2 files changed

Lines changed: 55 additions & 20 deletions

File tree

lib/mongodb.test.ts

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ describe('dbConnect', () => {
105105
process.env.MONGODB_URI = 'mongodb://localhost:27017/test';
106106
global.mongoose.conn = null;
107107

108-
//const mockMongoose = { connection: 'mock' };
109108
vi.mocked(mongoose.connect).mockRejectedValue(new Error('Database is disconnected'));
110109

111110
await expect(dbConnect()).rejects.toThrow('Database is disconnected');
@@ -114,21 +113,6 @@ describe('dbConnect', () => {
114113
expect(global.mongoose.promise).toBeNull();
115114
});
116115

117-
it('handles mongoose Connection State 3 (disconnecting) gracefully', async () => {
118-
process.env.MONGODB_URI = 'mongodb://localhost:27017/test';
119-
global.mongoose.conn = null;
120-
mockMongooseConnection.readyState = 3;
121-
122-
const mockMongoose = { connection: 'mock' };
123-
setConnectedMongoose(mockMongoose as unknown as typeof mongoose);
124-
125-
const conn = await dbConnect();
126-
127-
expect(mongoose.connect).toHaveBeenCalledTimes(1);
128-
expect(conn).toBe(mockMongoose);
129-
expect(global.mongoose.conn).toBe(mockMongoose);
130-
});
131-
132116
it('returns the cached connection immediately when mongoose is already connected', async () => {
133117
process.env.MONGODB_URI = 'mongodb://localhost:27017/test';
134118

@@ -169,13 +153,17 @@ describe('dbConnect', () => {
169153

170154
it('handles mongoose Connection State 3 (disconnecting) gracefully', async () => {
171155
process.env.MONGODB_URI = 'mongodb://localhost:27017/test';
156+
global.mongoose.conn = null;
172157
mockMongooseConnection.readyState = 3;
173-
const mockMDB = { connection: 'mock' };
174-
setConnectedMongoose(mockMDB as unknown as typeof mongoose);
175158

176-
const res = await dbConnect();
159+
const mockMongoose = { connection: 'mock' };
160+
setConnectedMongoose(mockMongoose as unknown as typeof mongoose);
161+
162+
const conn = await dbConnect();
163+
177164
expect(mongoose.connect).toHaveBeenCalledTimes(1);
178-
expect(res).toBe(mockMDB);
165+
expect(conn).toBe(mockMongoose);
166+
expect(global.mongoose.conn).toBe(mockMongoose);
179167
});
180168

181169
it('reuses an in-flight promise when state 3 triggers concurrent dbConnect calls', async () => {

models/User.test.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,4 +406,51 @@ describe('User Schema Behaviors under Connection State 2 (Variation 3)', () => {
406406

407407
readyStateSpy.mockRestore();
408408
});
409+
410+
describe('Database Connection State 3 (Disconnecting) Handling', () => {
411+
it('aborts/rolls back active transactions cleanly when connection is in state 3 (disconnecting)', async () => {
412+
const { vi } = await import('vitest');
413+
414+
const readyStateSpy = vi
415+
.spyOn(mongoose.connection, 'readyState', 'get')
416+
.mockReturnValue(3 as unknown as typeof mongoose.connection.readyState);
417+
418+
const mockSession = {
419+
startTransaction: vi.fn(),
420+
commitTransaction: vi.fn(),
421+
abortTransaction: vi.fn().mockResolvedValue(undefined),
422+
endSession: vi.fn().mockResolvedValue(undefined),
423+
} as unknown as mongoose.ClientSession;
424+
425+
const startSessionSpy = vi.spyOn(mongoose, 'startSession').mockResolvedValue(mockSession);
426+
427+
const runTransactionWithCheck = async (session: mongoose.ClientSession) => {
428+
session.startTransaction();
429+
try {
430+
if (mongoose.connection.readyState === 3) {
431+
await session.abortTransaction();
432+
return { status: 'aborted' };
433+
}
434+
await session.commitTransaction();
435+
return { status: 'committed' };
436+
} catch (error) {
437+
await session.abortTransaction();
438+
throw error;
439+
} finally {
440+
await session.endSession();
441+
}
442+
};
443+
444+
const session = await mongoose.startSession();
445+
const result = await runTransactionWithCheck(session);
446+
447+
expect(result.status).toBe('aborted');
448+
expect(mockSession.abortTransaction).toHaveBeenCalledTimes(1);
449+
expect(mockSession.endSession).toHaveBeenCalledTimes(1);
450+
expect(mockSession.commitTransaction).not.toHaveBeenCalled();
451+
452+
readyStateSpy.mockRestore();
453+
startSessionSpy.mockRestore();
454+
});
455+
});
409456
});

0 commit comments

Comments
 (0)