* 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 constructor() {}
 | 
			
		||||
  
 | 
			
		||||
    public static getInstance(): DatabaseConnection {
 | 
			
		||||
      if (!DatabaseConnection.instance) {
 | 
			
		||||
        DatabaseConnection.instance = new DatabaseConnection();
 | 
			
		||||
      }
 | 
			
		||||
      return DatabaseConnection.instance;
 | 
			
		||||
  private static instance: DatabaseConnection;
 | 
			
		||||
  private isConnected: boolean = false;
 | 
			
		||||
 | 
			
		||||
  private constructor() {}
 | 
			
		||||
 | 
			
		||||
  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,
 | 
			
		||||
        });
 | 
			
		||||
  
 | 
			
		||||
        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 });
 | 
			
		||||
    try {
 | 
			
		||||
      await mongoose.connect(config.mongodbUri, {
 | 
			
		||||
        maxPoolSize: 10,
 | 
			
		||||
        serverSelectionTimeoutMS: 5000,
 | 
			
		||||
        socketTimeoutMS: 45000,
 | 
			
		||||
        bufferCommands: false,
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      this.isConnected = true;
 | 
			
		||||
      Logger.database.connected();
 | 
			
		||||
 | 
			
		||||
      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