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 { 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() }) })