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
47 changes: 25 additions & 22 deletions models/User.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ describe('User Model', () => {
options: { default?: unknown };
};

// Assertion 1: the default is a function
expect(typeof createdAtPath.options.default).toBe('function');

// Assertion 2: calling the default returns a numeric timestamp
const result = (createdAtPath.options.default as () => number)();
expect(typeof result).toBe('number');
expect(Number.isFinite(result)).toBe(true);
Expand Down Expand Up @@ -94,14 +96,6 @@ describe('User Model', () => {
});

it('buffers operations when connection is in state 2 (connecting) by default', async (): Promise<void> => {
// In connection state 2 (connecting), Mongoose buffers operations by default rather than throwing errors immediately.
// This behavior occurs because Mongoose assumes the database connection will be established shortly (transitioning to state 1).
// Therefore, it queues all pending model commands inside the internal collection queue (User.collection.queue).
//
// In contrast, in connection state 0 (disconnected), Mongoose will either throw a ConnectionError immediately (if command buffering is disabled)
// or time out because there is no ongoing connection attempt that would eventually flush the command buffer.
// In state 2, the query remains in-flight (pending) waiting for connection recovery/open events.

let currentReadyState = 2;
readyStateSpy = vi
.spyOn(mongoose.connection, 'readyState', 'get')
Expand Down Expand Up @@ -129,9 +123,6 @@ describe('User Model', () => {
currentReadyState = 1;
expect(mongoose.connection.readyState).toBe(1);

// Drain the queued operations on the connection. The query is now executed against the underlying driver.
// Since there is no active physical database instance in this mock, it will fail during driver call dispatch,
// confirming that the query promise was successfully released from the buffer and attempted execution.
try {
(mongoose.connection as unknown as { onOpen: () => void }).onOpen();
await promise;
Expand All @@ -142,7 +133,6 @@ describe('User Model', () => {
});

it('rejects operations immediately when bufferCommands is disabled in state 2', async (): Promise<void> => {
// Disable command buffering on both mongoose and User schema
mongoose.set('bufferCommands', false);
User.schema.set('bufferCommands', false);

Expand All @@ -152,17 +142,37 @@ describe('User Model', () => {

expect(mongoose.connection.readyState).toBe(2);

// When buffering is disabled, the operation should fail immediately rather than waiting or queuing.
await expect(User.findOne({ username: 'testuser' }).exec()).rejects.toThrow(
/Cannot call.*if.*bufferCommands = false/
);

// Assert that nothing was added to the collection queue
const collectionWrapper = User.collection as unknown as { queue: unknown[] };
expect(collectionWrapper.queue.length).toBe(0);
});
});

describe('Database Connection State 99 Handling', () => {
it('triggers a lazy initialization fallback for database operations when uninitialized', async () => {
const { vi } = await import('vitest');

const readyStateSpy = vi
.spyOn(mongoose.connection, 'readyState', 'get')
.mockReturnValue(99 as unknown as typeof mongoose.connection.readyState);

expect(mongoose.connection.readyState).toBe(99);

const fallbackUser = { username: 'buffered_user' };
const findOneSpy = vi.spyOn(User, 'findOne').mockResolvedValue(fallbackUser as never);

const result = await User.findOne({ username: 'buffered_user' });
expect(result).toEqual(fallbackUser);
expect(findOneSpy).toHaveBeenCalledTimes(1);

readyStateSpy.mockRestore();
findOneSpy.mockRestore();
});
});

describe('Database Connection State 0 Handling', () => {
it('fails queries gracefully with a ConnectionError when disconnected', async (): Promise<void> => {
const readyStateSpy = vi
Expand Down Expand Up @@ -235,19 +245,15 @@ describe('User Model', () => {
});
});

describe('Database Connection State 99 Handling', () => {
describe('Database Connection State 99 Handling (Lazy Initialization Tracking)', () => {
it('triggers lazy initialization exactly once and uses the correct connection URI', async (): Promise<void> => {
// 1. Mock readyState to 99 (uninitialized — no connection ever attempted)
const readyStateSpy = vi
.spyOn(mongoose.connection, 'readyState', 'get')
.mockReturnValue(99 as unknown as typeof mongoose.connection.readyState);

// 2. Stub mongoose.connect to capture what URI it was called with
const connectSpy = vi.spyOn(mongoose, 'connect').mockResolvedValue(mongoose);

const MONGO_URI = 'mongodb://localhost:27017/commitpulse';

// 3. Simulate the lazy init fallback — connects exactly once with correct URI
const lazyInit = async (): Promise<void> => {
if (mongoose.connection.readyState === 99) {
await mongoose.connect(MONGO_URI);
Expand All @@ -256,12 +262,10 @@ describe('User Model', () => {

await lazyInit();

// 4. Assertions
expect(mongoose.connection.readyState).toBe(99);
expect(connectSpy).toHaveBeenCalledTimes(1);
expect(connectSpy).toHaveBeenCalledWith(MONGO_URI);

// 5. Cleanup
readyStateSpy.mockRestore();
connectSpy.mockRestore();
});
Expand All @@ -276,7 +280,6 @@ describe('User Schema Behaviors under Connection State 2 (Variation 3)', () => {
it('buffers user model database operations cleanly when connection state is 2 (connecting)', async () => {
const { vi } = await import('vitest');

// Mock the mongoose connection readyState to return 2 (connecting)
const readyStateSpy = vi
.spyOn(mongoose.connection, 'readyState', 'get')
.mockReturnValue(2 as unknown as typeof mongoose.connection.readyState);
Expand Down
Loading