diff --git a/.env.example b/.env.example index 876535b345..4190dbd5a4 100644 --- a/.env.example +++ b/.env.example @@ -15,6 +15,7 @@ HOST=localhost PORT=3080 MONGO_URI=mongodb://127.0.0.1:27017/LibreChat +# MONGO_DB_NAME=LibreChat DOMAIN_CLIENT=http://localhost:3080 DOMAIN_SERVER=http://localhost:3080 diff --git a/api/db/connect.js b/api/db/connect.js index 706f67cc07..a4ec2c7b76 100644 --- a/api/db/connect.js +++ b/api/db/connect.js @@ -48,6 +48,4 @@ async function connectDb() { return cached.conn; } -module.exports = { - connectDb, -}; +module.exports = { connectDb }; diff --git a/api/db/connect.spec.js b/api/db/connect.spec.js new file mode 100644 index 0000000000..566629315d --- /dev/null +++ b/api/db/connect.spec.js @@ -0,0 +1,102 @@ +jest.mock('dotenv'); +require('dotenv').config(); +const mongoose = require('mongoose'); +const { MongoMemoryServer } = require('mongodb-memory-server'); + +// Helper to clear the connect module from require cache +function clearConnectModule() { + delete require.cache[require.resolve('./connect')]; +} + +beforeEach(() => { + // Reset environment and global state + clearConnectModule(); + delete process.env.MONGO_URI; + delete process.env.MONGO_DB_NAME; + global.mongoose = undefined; +}); + +describe('connectDb Module', () => { + it('throws if MONGO_URI is not defined', () => { + expect(() => require('./connect')).toThrow('Please define the MONGO_URI environment variable'); + }); + + describe('with in-memory MongoDB', () => { + let mongoServer; + let uri; + + beforeAll(async () => { + mongoServer = await MongoMemoryServer.create(); + uri = mongoServer.getUri(); + }); + + afterAll(async () => { + // Ensure real mongoose connection is torn down + await mongoose.disconnect(); + await mongoServer.stop(); + }); + + it('connects using only MONGO_URI (no dbName override)', async () => { + process.env.MONGO_URI = uri; + clearConnectModule(); + const { connectDb } = require('./connect'); + + const mongooseInstance = await connectDb(); + expect(mongooseInstance).toBe(mongoose); + + // The default DB is whatever follows the last slash in the URI + const defaultDbNameMatch = uri.match(/\/([\w-]+)(\?.*)?$/); + const defaultDbName = defaultDbNameMatch ? defaultDbNameMatch[1] : mongoose.connection.name; + expect(mongooseInstance.connection.name).toBe(defaultDbName); + }); + + it('strips any DB name in URI when MONGO_DB_NAME is set', async () => { + process.env.MONGO_URI = uri; + process.env.MONGO_DB_NAME = 'ignoredDbName'; + clearConnectModule(); + + const connectSpy = jest.spyOn(mongoose, 'connect'); + const { connectDb } = require('./connect'); + await connectDb(); + + const [calledUri, calledOpts] = connectSpy.mock.calls[0]; + // Should have removed the trailing "/" + expect(calledUri).not.toMatch(/\/[\w-]+$/); + // And since we strip it rather than passing dbName, opts.dbName remains unset + expect(calledOpts).not.toHaveProperty('dbName'); + + connectSpy.mockRestore(); + }); + + it('caches the connection between calls', async () => { + process.env.MONGO_URI = uri; + clearConnectModule(); + const { connectDb } = require('./connect'); + + const first = await connectDb(); + // simulate still-connected + mongoose.connection.readyState = 1; + const second = await connectDb(); + + expect(second).toBe(first); + }); + + it('reconnects if cached conn is disconnected', async () => { + process.env.MONGO_URI = uri; + clearConnectModule(); + const { connectDb } = require('./connect'); + + // First, establish a connection + await connectDb(); + expect(mongoose.connection.readyState).toBe(1); + + // Now actually tear it down + await mongoose.disconnect(); + expect(mongoose.connection.readyState).toBe(0); + + // Calling again should reconnect + await connectDb(); + expect(mongoose.connection.readyState).toBe(1); + }); + }); +});