* adds complete tests for database
* adds healtCheck and connection status related methods DatabaseConnection class
This commit is contained in:
@ -1,6 +1,220 @@
|
|||||||
// test temporal para probar que jest funciona
|
import mongoose from 'mongoose';
|
||||||
|
import { DatabaseConnection } from '../config/database.js';
|
||||||
|
import { Logger } from '../utils/logger.js';
|
||||||
|
|
||||||
|
// Mock mongoose
|
||||||
|
jest.mock('mongoose', () => {
|
||||||
|
const mockConnection = {
|
||||||
|
readyState: 0,
|
||||||
|
on: jest.fn(),
|
||||||
|
once: jest.fn()
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
connect: jest.fn(),
|
||||||
|
disconnect: jest.fn(),
|
||||||
|
connection: mockConnection
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mock Logger
|
||||||
|
jest.mock('../utils/logger.js', () => ({
|
||||||
|
Logger: {
|
||||||
|
database: {
|
||||||
|
connected: jest.fn(),
|
||||||
|
disconnected: jest.fn(),
|
||||||
|
reconnected: jest.fn(),
|
||||||
|
alreadyConnected: jest.fn(),
|
||||||
|
notConnected: jest.fn()
|
||||||
|
},
|
||||||
|
error: jest.fn()
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Mock config
|
||||||
|
jest.mock('../config/config.js', () => ({
|
||||||
|
config: {
|
||||||
|
mongodbUri: 'mongodb://localhost:27017/test'
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
describe('DatabaseConnection', () => {
|
describe('DatabaseConnection', () => {
|
||||||
|
let mockMongoose: jest.Mocked<typeof mongoose>;
|
||||||
|
let mockLogger: jest.Mocked<typeof Logger>;
|
||||||
|
let databaseConnection: DatabaseConnection;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
mockMongoose = mongoose as jest.Mocked<typeof mongoose>;
|
||||||
|
mockLogger = Logger as jest.Mocked<typeof Logger>;
|
||||||
|
|
||||||
|
// Reset singleton instance
|
||||||
|
(DatabaseConnection as any).instance = null;
|
||||||
|
databaseConnection = DatabaseConnection.getInstance();
|
||||||
|
});
|
||||||
|
|
||||||
describe('Singleton Pattern', () => {
|
describe('Singleton Pattern', () => {
|
||||||
test('should return the same instance', () => {})
|
test('should return the same instance', () => {
|
||||||
})
|
const instance1 = DatabaseConnection.getInstance();
|
||||||
})
|
const instance2 = DatabaseConnection.getInstance();
|
||||||
|
expect(instance1).toBe(instance2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('connect', () => {
|
||||||
|
test('should connect to MongoDB successfully', async () => {
|
||||||
|
mockMongoose.connect.mockResolvedValueOnce(mockMongoose);
|
||||||
|
Object.defineProperty(mockMongoose.connection, 'readyState', { value: 1, writable: true });
|
||||||
|
|
||||||
|
await databaseConnection.connect();
|
||||||
|
|
||||||
|
expect(mockMongoose.connect).toHaveBeenCalledWith('mongodb://localhost:27017/test', {
|
||||||
|
maxPoolSize: 10,
|
||||||
|
serverSelectionTimeoutMS: 5000,
|
||||||
|
socketTimeoutMS: 45000,
|
||||||
|
bufferCommands: false
|
||||||
|
});
|
||||||
|
expect(mockLogger.database.connected).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not connect if already connected', async () => {
|
||||||
|
// Set as already connected
|
||||||
|
(databaseConnection as any).isConnected = true;
|
||||||
|
|
||||||
|
await databaseConnection.connect();
|
||||||
|
|
||||||
|
expect(mockMongoose.connect).not.toHaveBeenCalled();
|
||||||
|
expect(mockLogger.database.alreadyConnected).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle connection errors', async () => {
|
||||||
|
const error = new Error('Connection failed');
|
||||||
|
mockMongoose.connect.mockRejectedValueOnce(error);
|
||||||
|
|
||||||
|
await expect(databaseConnection.connect()).rejects.toThrow('Connection failed');
|
||||||
|
expect(mockLogger.error).toHaveBeenCalledWith('Failed to connect to MongoDB', { error });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should set up connection event listeners', async () => {
|
||||||
|
mockMongoose.connect.mockResolvedValueOnce(mockMongoose);
|
||||||
|
Object.defineProperty(mockMongoose.connection, 'readyState', { value: 1, writable: true });
|
||||||
|
|
||||||
|
await databaseConnection.connect();
|
||||||
|
|
||||||
|
expect(mockMongoose.connection.on).toHaveBeenCalledWith('error', expect.any(Function));
|
||||||
|
expect(mockMongoose.connection.on).toHaveBeenCalledWith('disconnected', expect.any(Function));
|
||||||
|
expect(mockMongoose.connection.on).toHaveBeenCalledWith('reconnected', expect.any(Function));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('disconnect', () => {
|
||||||
|
test('should disconnect from MongoDB successfully', async () => {
|
||||||
|
// Set as connected
|
||||||
|
(databaseConnection as any).isConnected = true;
|
||||||
|
mockMongoose.disconnect.mockResolvedValueOnce(undefined);
|
||||||
|
|
||||||
|
await databaseConnection.disconnect();
|
||||||
|
|
||||||
|
expect(mockMongoose.disconnect).toHaveBeenCalled();
|
||||||
|
expect(mockLogger.database.disconnected).toHaveBeenCalled();
|
||||||
|
expect((databaseConnection as any).isConnected).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not disconnect if not connected', async () => {
|
||||||
|
// Set as not connected
|
||||||
|
(databaseConnection as any).isConnected = false;
|
||||||
|
|
||||||
|
await databaseConnection.disconnect();
|
||||||
|
|
||||||
|
expect(mockMongoose.disconnect).not.toHaveBeenCalled();
|
||||||
|
expect(mockLogger.database.notConnected).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle disconnection errors', async () => {
|
||||||
|
const error = new Error('Disconnection failed');
|
||||||
|
(databaseConnection as any).isConnected = true;
|
||||||
|
mockMongoose.disconnect.mockRejectedValueOnce(error);
|
||||||
|
|
||||||
|
await expect(databaseConnection.disconnect()).rejects.toThrow('Disconnection failed');
|
||||||
|
expect(mockLogger.error).toHaveBeenCalledWith('Error disconnecting from MongoDB', { error });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isConnected', () => {
|
||||||
|
test('should return true when connected', () => {
|
||||||
|
(databaseConnection as any).isConnected = true;
|
||||||
|
expect(databaseConnection.getConnectionStatus()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return false when not connected', () => {
|
||||||
|
(databaseConnection as any).isConnected = false;
|
||||||
|
expect(databaseConnection.getConnectionStatus()).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Event Handlers', () => {
|
||||||
|
test('should handle connection error event', async () => {
|
||||||
|
mockMongoose.connect.mockResolvedValueOnce(mockMongoose);
|
||||||
|
Object.defineProperty(mockMongoose.connection, 'readyState', { value: 1, writable: true });
|
||||||
|
|
||||||
|
let errorHandler: Function;
|
||||||
|
(mockMongoose.connection.on as jest.Mock).mockImplementation((event, handler) => {
|
||||||
|
if (event === 'error') {
|
||||||
|
errorHandler = handler;
|
||||||
|
}
|
||||||
|
return mockMongoose.connection;
|
||||||
|
});
|
||||||
|
|
||||||
|
await databaseConnection.connect();
|
||||||
|
|
||||||
|
// Simulate error event
|
||||||
|
const testError = new Error('Connection error');
|
||||||
|
errorHandler!(testError);
|
||||||
|
|
||||||
|
expect(mockLogger.error).toHaveBeenCalledWith('MongoDB connection error', { error: testError });
|
||||||
|
expect((databaseConnection as any).isConnected).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle disconnected event', async () => {
|
||||||
|
mockMongoose.connect.mockResolvedValueOnce(mockMongoose);
|
||||||
|
Object.defineProperty(mockMongoose.connection, 'readyState', { value: 1, writable: true });
|
||||||
|
|
||||||
|
let disconnectedHandler: Function;
|
||||||
|
(mockMongoose.connection.on as jest.Mock).mockImplementation((event, handler) => {
|
||||||
|
if (event === 'disconnected') {
|
||||||
|
disconnectedHandler = handler;
|
||||||
|
}
|
||||||
|
return mockMongoose.connection;
|
||||||
|
});
|
||||||
|
|
||||||
|
await databaseConnection.connect();
|
||||||
|
|
||||||
|
// Simulate disconnected event
|
||||||
|
disconnectedHandler!();
|
||||||
|
|
||||||
|
expect(mockLogger.database.disconnected).toHaveBeenCalled();
|
||||||
|
expect((databaseConnection as any).isConnected).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle reconnected event', async () => {
|
||||||
|
mockMongoose.connect.mockResolvedValueOnce(mockMongoose);
|
||||||
|
Object.defineProperty(mockMongoose.connection, 'readyState', { value: 1, writable: true });
|
||||||
|
|
||||||
|
let reconnectedHandler: Function;
|
||||||
|
(mockMongoose.connection.on as jest.Mock).mockImplementation((event, handler) => {
|
||||||
|
if (event === 'reconnected') {
|
||||||
|
reconnectedHandler = handler;
|
||||||
|
}
|
||||||
|
return mockMongoose.connection;
|
||||||
|
});
|
||||||
|
|
||||||
|
await databaseConnection.connect();
|
||||||
|
|
||||||
|
// Simulate reconnected event
|
||||||
|
reconnectedHandler!();
|
||||||
|
|
||||||
|
expect(mockLogger.database.reconnected).toHaveBeenCalled();
|
||||||
|
expect((databaseConnection as any).isConnected).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -1,9 +1,10 @@
|
|||||||
import mongoose from 'mongoose';
|
import mongoose from 'mongoose';
|
||||||
import config from './config'
|
import { config } from '../config/config.js';
|
||||||
|
import { Logger } from '../utils/logger.js';
|
||||||
|
|
||||||
export class DatabaseConnection {
|
export class DatabaseConnection {
|
||||||
private static instance: DatabaseConnection;
|
private static instance: DatabaseConnection;
|
||||||
private isConnected: boolean = false; // a implementar
|
private isConnected: boolean = false;
|
||||||
|
|
||||||
private constructor() {}
|
private constructor() {}
|
||||||
|
|
||||||
@ -16,7 +17,7 @@ export class DatabaseConnection {
|
|||||||
|
|
||||||
public async connect(): Promise<void> {
|
public async connect(): Promise<void> {
|
||||||
if (this.isConnected) {
|
if (this.isConnected) {
|
||||||
console.log("database is already connected")
|
Logger.database.alreadyConnected();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,27 +30,66 @@ export class DatabaseConnection {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.isConnected = true;
|
this.isConnected = true;
|
||||||
console.log("database connected")
|
Logger.database.connected();
|
||||||
|
|
||||||
mongoose.connection.on('error', (error) => {
|
mongoose.connection.on('error', (error) => {
|
||||||
console.log('MongoDB connection error', { error });
|
Logger.error('MongoDB connection error', { error });
|
||||||
this.isConnected = false;
|
this.isConnected = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
mongoose.connection.on('disconnected', () => {
|
mongoose.connection.on('disconnected', () => {
|
||||||
console.log('MongoDB connection disconnected');
|
Logger.database.disconnected();
|
||||||
this.isConnected = false;
|
this.isConnected = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
mongoose.connection.on('reconnected', () => {
|
mongoose.connection.on('reconnected', () => {
|
||||||
console.log('MongoDB connection reconnected');
|
Logger.database.reconnected();
|
||||||
this.isConnected = true;
|
this.isConnected = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('MongoDB Failed to connect', { error });
|
Logger.error('Failed to connect to MongoDB', { error });
|
||||||
this.isConnected = false;
|
this.isConnected = false;
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async disconnect(): Promise<void> {
|
||||||
|
if (!this.isConnected) {
|
||||||
|
Logger.database.notConnected();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await mongoose.disconnect();
|
||||||
|
this.isConnected = false;
|
||||||
|
Logger.database.disconnected();
|
||||||
|
} catch (error) {
|
||||||
|
Logger.error('Error disconnecting from MongoDB', { error });
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getConnectionStatus(): boolean {
|
||||||
|
return this.isConnected && mongoose.connection.readyState === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async healthCheck(): Promise<{ status: string; message: string }> {
|
||||||
|
try {
|
||||||
|
if (!this.isConnected) {
|
||||||
|
return { status: 'error', message: 'Database not connected' };
|
||||||
|
}
|
||||||
|
|
||||||
|
await mongoose.connection.db?.admin().ping();
|
||||||
|
return { status: 'ok', message: 'Database connection is healthy' };
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
status: 'error',
|
||||||
|
message: `Database health check failed: ${error instanceof Error ? error.message : 'Unknown error'}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const database = DatabaseConnection.getInstance();
|
||||||
|
export default database;
|
Reference in New Issue
Block a user