Files
websitedev/proto/src/lib/mongodb.test.ts
T
Do Siki bd7287aa58 fix: address code review findings from 2026-08-17
- scope no-cache headers to non-static routes (restore immutable asset caching)
- reset cached rejected MongoDB promise so retries can succeed
- use last X-Forwarded-For entry in Content Editor rate limiter (anti-spoofing)
- remove weak Mongo defaults from compose files (fail loudly on missing env)
- move staging banner text to common.json content
- read APP_PORT from env file in deploy.sh healthcheck
- filter network noise from staging smoke console assertions

Closes MITHOME-48, MITHOME-49, MITHOME-50, MITHOME-51, MITHOME-52, MITHOME-53, MITHOME-54
2026-08-18 12:21:32 +02:00

162 lines
5.0 KiB
TypeScript
Executable File

// Mock the MongoDB module completely
const mockDb = {
collection: jest.fn().mockReturnValue({
findOne: jest.fn(),
insertOne: jest.fn(),
updateOne: jest.fn(),
deleteOne: jest.fn()
}),
admin: jest.fn().mockReturnValue({
ping: jest.fn().mockResolvedValue(true)
})
}
const mockClient = {
connect: jest.fn(),
close: jest.fn().mockResolvedValue(undefined),
db: jest.fn().mockReturnValue(mockDb)
}
// The connect method should resolve to the client itself
mockClient.connect.mockResolvedValue(mockClient)
const MockedMongoClient = jest.fn().mockImplementation(() => mockClient)
// Mock the mongodb module
jest.mock('mongodb', () => ({
MongoClient: MockedMongoClient
}))
// Restore environment before tests
const originalEnv = process.env
describe('MongoDB Connection (Unit Tests)', () => {
beforeEach(() => {
jest.resetModules()
process.env = {
...originalEnv,
MONGODB_URI: 'mongodb://localhost:27017/test',
MONGODB_DB: 'test'
}
jest.clearAllMocks()
// Reset the mock implementation
MockedMongoClient.mockImplementation(() => mockClient as any)
mockClient.db.mockReturnValue(mockDb)
})
afterEach(() => {
process.env = originalEnv
})
describe('MongoDB URI validation', () => {
it('should throw error when MONGODB_URI is not set', () => {
// This test is tricky because the module is cached
// We'll test this by clearing the module cache and mocking process.env
const originalMongodbUri = process.env.MONGODB_URI
delete process.env.MONGODB_URI
// Clear module cache to force re-import
jest.resetModules()
const { getClientPromise } = require('./mongodb')
expect(() => getClientPromise()).toThrow('Please add MONGODB_URI to your environment variables')
// Restore environment
process.env.MONGODB_URI = originalMongodbUri
})
})
describe('Database connection', () => {
it('should create MongoClient with correct URI and options', async () => {
// Import after setting up the mock
const { getClientPromise } = await import('./mongodb')
await getClientPromise()
expect(MockedMongoClient).toHaveBeenCalledWith(
process.env.MONGODB_URI,
expect.objectContaining({
maxPoolSize: 10,
serverSelectionTimeoutMS: 5000,
socketTimeoutMS: 45000
})
)
})
it('should return database instance', async () => {
const { getDb } = await import('./mongodb')
const result = await getDb()
expect(result).toBe(mockDb)
expect(typeof result.collection).toBe('function')
})
it('should return collection from database', async () => {
const { getCollection } = await import('./mongodb')
const collection = await getCollection('test_collection')
expect(collection).toBeDefined()
expect(mockDb.collection).toHaveBeenCalledWith('test_collection')
expect(typeof collection.findOne).toBe('function')
})
it('should check MongoDB connection successfully', async () => {
const { checkMongoConnection } = await import('./mongodb')
const result = await checkMongoConnection()
expect(result).toBe(true)
})
it('should return false on MongoDB connection failure', async () => {
// Create a failing client
const failingClient = {
connect: jest.fn().mockRejectedValue(new Error('Connection failed')),
db: jest.fn().mockRejectedValue(new Error('Connection failed')),
close: jest.fn()
}
MockedMongoClient.mockImplementation(() => failingClient as any)
// Clear the module cache and re-import
jest.resetModules()
const { checkMongoConnection } = await import('./mongodb')
const result = await checkMongoConnection()
expect(result).toBe(false)
})
})
describe('Connection reuse and caching', () => {
it('should reuse the same MongoClient instance for multiple calls', async () => {
const { getClientPromise } = await import('./mongodb')
const client1 = await getClientPromise()
const client2 = await getClientPromise()
// Should be the same client instance
expect(client1).toBe(client2)
})
it('should not cache a rejected connection promise and retry on next call', async () => {
// First attempt fails (e.g. transient DB outage at startup)
const failingClient = {
connect: jest.fn().mockRejectedValueOnce(new Error('transient failure')),
db: jest.fn(),
close: jest.fn()
}
MockedMongoClient.mockImplementation(() => failingClient)
const { getClientPromise } = await import('./mongodb')
await expect(getClientPromise()).rejects.toThrow('transient failure')
// Second call must retry instead of replaying the cached rejection
failingClient.connect.mockResolvedValueOnce(failingClient as any)
await expect(getClientPromise()).resolves.toBe(failingClient)
})
})
})