mirror of
https://github.com/complexcaresolutions/cms.c2sgmbh.git
synced 2026-03-17 18:34:13 +00:00
The fire-and-forget dynamic import chain (3 awaits) was racing with test flush timeouts. Caching the resolved payload instance fixes both the flakiness and eliminates per-call import overhead. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
145 lines
4 KiB
TypeScript
145 lines
4 KiB
TypeScript
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
|
|
|
const mockCreate = vi.fn().mockResolvedValue({ id: '1' })
|
|
const mockGetPayload = vi.fn().mockResolvedValue({ create: mockCreate })
|
|
|
|
vi.mock('payload', () => ({
|
|
getPayload: mockGetPayload,
|
|
}))
|
|
|
|
vi.mock('@payload-config', () => ({
|
|
default: {},
|
|
}))
|
|
|
|
import { createMonitoringLogger, _resetPayloadCache } from '@/lib/monitoring/monitoring-logger'
|
|
|
|
/** Wait long enough for fire-and-forget promises to settle. */
|
|
async function flush(): Promise<void> {
|
|
await new Promise((resolve) => setTimeout(resolve, 150))
|
|
}
|
|
|
|
describe('MonitoringLogger', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
_resetPayloadCache()
|
|
mockGetPayload.mockResolvedValue({ create: mockCreate })
|
|
mockCreate.mockResolvedValue({ id: '1' })
|
|
delete process.env.MONITORING_LOG_LEVEL
|
|
})
|
|
|
|
afterEach(async () => {
|
|
// Drain any pending fire-and-forget promises to prevent cross-test leakage
|
|
await flush()
|
|
})
|
|
|
|
it('creates a logger with all five log methods', () => {
|
|
const logger = createMonitoringLogger('cron')
|
|
|
|
expect(logger).toHaveProperty('debug')
|
|
expect(logger).toHaveProperty('info')
|
|
expect(logger).toHaveProperty('warn')
|
|
expect(logger).toHaveProperty('error')
|
|
expect(logger).toHaveProperty('fatal')
|
|
})
|
|
|
|
it('writes to payload when log level meets threshold', async () => {
|
|
const logger = createMonitoringLogger('cron')
|
|
logger.info('Cron job completed', { duration: 3500 })
|
|
|
|
await flush()
|
|
|
|
expect(mockGetPayload).toHaveBeenCalled()
|
|
expect(mockCreate).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
collection: 'monitoring-logs',
|
|
data: expect.objectContaining({
|
|
level: 'info',
|
|
source: 'cron',
|
|
message: 'Cron job completed',
|
|
duration: 3500,
|
|
}),
|
|
}),
|
|
)
|
|
})
|
|
|
|
it('filters out messages below the configured minimum level', async () => {
|
|
process.env.MONITORING_LOG_LEVEL = 'warn'
|
|
|
|
const logger = createMonitoringLogger('payload')
|
|
logger.debug('This should not be logged')
|
|
logger.info('This should not be logged either')
|
|
|
|
await flush()
|
|
|
|
expect(mockCreate).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('allows messages at or above the configured minimum level', async () => {
|
|
process.env.MONITORING_LOG_LEVEL = 'warn'
|
|
|
|
const logger = createMonitoringLogger('email')
|
|
logger.warn('SMTP timeout')
|
|
|
|
await flush()
|
|
|
|
expect(mockCreate).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
collection: 'monitoring-logs',
|
|
data: expect.objectContaining({
|
|
level: 'warn',
|
|
source: 'email',
|
|
message: 'SMTP timeout',
|
|
}),
|
|
}),
|
|
)
|
|
})
|
|
|
|
it('passes context fields to the payload create call', async () => {
|
|
const logger = createMonitoringLogger('sync')
|
|
logger.info('Sync completed', { requestId: 'req-123', userId: 42, tenant: 1, duration: 1500 })
|
|
|
|
await flush()
|
|
|
|
expect(mockCreate).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
data: expect.objectContaining({
|
|
requestId: 'req-123',
|
|
userId: 42,
|
|
tenant: 1,
|
|
duration: 1500,
|
|
}),
|
|
}),
|
|
)
|
|
})
|
|
|
|
it('separates extra context fields from known fields', async () => {
|
|
const logger = createMonitoringLogger('oauth')
|
|
logger.error('Token refresh failed', { userId: 5, provider: 'meta', errorCode: 'EXPIRED' })
|
|
|
|
await flush()
|
|
|
|
expect(mockCreate).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
data: expect.objectContaining({
|
|
userId: 5,
|
|
context: { provider: 'meta', errorCode: 'EXPIRED' },
|
|
}),
|
|
}),
|
|
)
|
|
})
|
|
|
|
it('falls back to console when payload is unavailable', async () => {
|
|
mockGetPayload.mockRejectedValueOnce(new Error('Not initialized'))
|
|
|
|
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
|
|
|
|
const logger = createMonitoringLogger('cron')
|
|
logger.info('Fallback test')
|
|
|
|
await flush()
|
|
|
|
expect(consoleSpy).toHaveBeenCalledWith('[cron][INFO]', 'Fallback test', expect.anything())
|
|
|
|
consoleSpy.mockRestore()
|
|
})
|
|
})
|