* 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('Singleton Pattern', () => {
|
||||
test('should return the same instance', () => {})
|
||||
})
|
||||
})
|
||||
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', () => {
|
||||
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,55 +1,95 @@
|
||||
import mongoose from 'mongoose';
|
||||
import config from './config'
|
||||
import { config } from '../config/config.js';
|
||||
import { Logger } from '../utils/logger.js';
|
||||
|
||||
export class DatabaseConnection {
|
||||
private static instance: DatabaseConnection;
|
||||
private isConnected: boolean = false; // a implementar
|
||||
private static instance: DatabaseConnection;
|
||||
private isConnected: boolean = false;
|
||||
|
||||
private constructor() {}
|
||||
private constructor() {}
|
||||
|
||||
public static getInstance(): DatabaseConnection {
|
||||
if (!DatabaseConnection.instance) {
|
||||
DatabaseConnection.instance = new DatabaseConnection();
|
||||
}
|
||||
return DatabaseConnection.instance;
|
||||
public static getInstance(): DatabaseConnection {
|
||||
if (!DatabaseConnection.instance) {
|
||||
DatabaseConnection.instance = new DatabaseConnection();
|
||||
}
|
||||
return DatabaseConnection.instance;
|
||||
}
|
||||
|
||||
public async connect(): Promise<void> {
|
||||
if (this.isConnected) {
|
||||
Logger.database.alreadyConnected();
|
||||
return;
|
||||
}
|
||||
|
||||
public async connect(): Promise<void> {
|
||||
if (this.isConnected) {
|
||||
console.log("database is already connected")
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await mongoose.connect(config.mongodbUri, {
|
||||
maxPoolSize: 10,
|
||||
serverSelectionTimeoutMS: 5000,
|
||||
socketTimeoutMS: 45000,
|
||||
bufferCommands: false,
|
||||
});
|
||||
|
||||
try {
|
||||
await mongoose.connect(config.mongodbUri, {
|
||||
maxPoolSize: 10,
|
||||
serverSelectionTimeoutMS: 5000,
|
||||
socketTimeoutMS: 45000,
|
||||
bufferCommands: false,
|
||||
});
|
||||
this.isConnected = true;
|
||||
Logger.database.connected();
|
||||
|
||||
this.isConnected = true;
|
||||
console.log("database connected")
|
||||
|
||||
mongoose.connection.on('error', (error) => {
|
||||
console.log('MongoDB connection error', { error });
|
||||
this.isConnected = false;
|
||||
});
|
||||
|
||||
mongoose.connection.on('disconnected', () => {
|
||||
console.log('MongoDB connection disconnected');
|
||||
this.isConnected = false;
|
||||
});
|
||||
|
||||
mongoose.connection.on('reconnected', () => {
|
||||
console.log('MongoDB connection reconnected');
|
||||
this.isConnected = true;
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.log('MongoDB Failed to connect', { error });
|
||||
mongoose.connection.on('error', (error) => {
|
||||
Logger.error('MongoDB connection error', { error });
|
||||
this.isConnected = false;
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
mongoose.connection.on('disconnected', () => {
|
||||
Logger.database.disconnected();
|
||||
this.isConnected = false;
|
||||
});
|
||||
|
||||
mongoose.connection.on('reconnected', () => {
|
||||
Logger.database.reconnected();
|
||||
this.isConnected = true;
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
Logger.error('Failed to connect to MongoDB', { error });
|
||||
this.isConnected = false;
|
||||
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