UNPKG

@axiomhq/logging

Version:
286 lines (231 loc) 8.06 kB
import { describe, beforeEach, afterEach, it, expect, vi, beforeAll, afterAll } from 'vitest'; import { SimpleFetchTransport } from '../../../src/transports/fetch'; import { http, HttpResponse, HttpHandler } from 'msw'; import { setupServer } from 'msw/node'; import { createLogEvent } from '../../lib/mock'; import { LogLevel } from 'src/logger'; describe('SimpleFetchTransport', () => { let transport: SimpleFetchTransport; const API_URL = 'https://api.example.com/logs'; const handlers: HttpHandler[] = [ http.post(API_URL, async ({ request }) => { const body = await request.json(); return HttpResponse.json({ success: true, receivedLogs: body }); }), ]; const server = setupServer(...handlers); beforeEach(() => { vi.useFakeTimers(); }); beforeAll(() => { server.listen(); }); afterAll(() => { server.close(); }); afterEach(() => { server.resetHandlers(); vi.clearAllTimers(); vi.useRealTimers(); }); describe('basic functionality', () => { beforeEach(() => { transport = new SimpleFetchTransport({ input: API_URL, autoFlush: false, }); }); it('should not flush automatically when autoFlush is false', async () => { const requestSpy = vi.fn(); server.use( http.post(API_URL, async () => { requestSpy(); return HttpResponse.json({ success: true }); }), ); transport.log([createLogEvent()]); await vi.runAllTimersAsync(); expect(requestSpy).not.toHaveBeenCalled(); }); it('should flush logs when manually called', async () => { let receivedBody: any; server.use( http.post(API_URL, async ({ request }) => { receivedBody = await request.json(); return HttpResponse.json({ success: true }); }), ); const logEvent = createLogEvent(); transport.log([logEvent]); await transport.flush(); expect(receivedBody).toEqual([logEvent]); }); it('should not make request when there are no events to flush', async () => { const requestSpy = vi.fn(); server.use( http.post(API_URL, async () => { requestSpy(); return HttpResponse.json({ success: true }); }), ); await transport.flush(); expect(requestSpy).not.toHaveBeenCalled(); }); it('should handle request errors gracefully', async () => { const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); server.use( http.post(API_URL, () => { return HttpResponse.error(); }), ); transport.log([createLogEvent()]); await transport.flush(); expect(consoleErrorSpy).toHaveBeenCalled(); consoleErrorSpy.mockRestore(); }); }); describe('auto-flush behavior', () => { it('should auto-flush after default delay when autoFlush is true', async () => { const requestSpy = vi.fn(); server.use( http.post(API_URL, async () => { requestSpy(); return HttpResponse.json({ success: true }); }), ); transport = new SimpleFetchTransport({ input: API_URL, autoFlush: true, }); transport.log([createLogEvent()]); // Default delay is 2000ms await vi.advanceTimersByTimeAsync(2000); expect(requestSpy).toHaveBeenCalledTimes(1); }); it('should auto-flush after custom delay', async () => { const requestSpy = vi.fn(); server.use( http.post(API_URL, async () => { requestSpy(); return HttpResponse.json({ success: true }); }), ); transport = new SimpleFetchTransport({ input: API_URL, autoFlush: { durationMs: 1000 }, }); transport.log([createLogEvent()]); await vi.advanceTimersByTimeAsync(999); expect(requestSpy).not.toHaveBeenCalled(); await vi.advanceTimersByTimeAsync(1); expect(requestSpy).toHaveBeenCalledTimes(1); }); it('should reset auto-flush timer when new logs are added', async () => { let receivedBody: any; server.use( http.post(API_URL, async ({ request }) => { receivedBody = await request.json(); return HttpResponse.json({ success: true }); }), ); transport = new SimpleFetchTransport({ input: API_URL, autoFlush: { durationMs: 1000 }, }); transport.log([createLogEvent(LogLevel.info, 'first')]); await vi.advanceTimersByTimeAsync(500); transport.log([createLogEvent(LogLevel.info, 'second')]); await vi.advanceTimersByTimeAsync(500); expect(receivedBody).toBeUndefined(); await vi.advanceTimersByTimeAsync(500); expect(receivedBody).toBeDefined(); expect(receivedBody[0].message).toBe('first'); expect(receivedBody[1].message).toBe('second'); }); it('should auto-flush with custom duration from config object', async () => { const requestSpy = vi.fn(); server.use( http.post(API_URL, async () => { requestSpy(); return HttpResponse.json({ success: true }); }), ); transport = new SimpleFetchTransport({ input: API_URL, autoFlush: { durationMs: 500 }, }); transport.log([createLogEvent()]); await vi.advanceTimersByTimeAsync(499); expect(requestSpy).not.toHaveBeenCalled(); await vi.advanceTimersByTimeAsync(1); expect(requestSpy).toHaveBeenCalledTimes(1); }); }); describe('custom fetch configuration', () => { it('should respect custom fetch init options', async () => { let receivedHeaders: Headers = new Headers(); server.use( http.post(API_URL, async ({ request }) => { receivedHeaders = request.headers; return HttpResponse.json({ success: true }); }), ); transport = new SimpleFetchTransport({ input: API_URL, init: { headers: { 'Content-Type': 'application/json', 'X-Custom-Header': 'test', }, credentials: 'include', }, }); transport.log([createLogEvent()]); await transport.flush(); expect(receivedHeaders.get('X-Custom-Header')).toBe('test'); expect(receivedHeaders.get('Content-Type')).toBe('application/json'); }); }); describe('log level filtering', () => { it('should filter logs based on logLevel', async () => { let receivedLogs: any[] = []; server.use( http.post(API_URL, async ({ request }) => { receivedLogs = (await request.json()) as any[]; return HttpResponse.json({ success: true }); }), ); transport = new SimpleFetchTransport({ input: API_URL, logLevel: LogLevel.warn, }); transport.log([ createLogEvent(LogLevel.debug, 'debug message'), createLogEvent(LogLevel.info, 'info message'), createLogEvent(LogLevel.warn, 'warn message'), createLogEvent(LogLevel.error, 'error message'), ]); await transport.flush(); expect(receivedLogs).toHaveLength(2); expect(receivedLogs.map((log) => log.level)).toEqual(['warn', 'error']); }); it('should use info as default logLevel', async () => { let receivedLogs: any[] = []; server.use( http.post(API_URL, async ({ request }) => { receivedLogs = (await request.json()) as any[]; return HttpResponse.json({ success: true }); }), ); transport = new SimpleFetchTransport({ input: API_URL }); transport.log([ createLogEvent(LogLevel.debug, 'debug message'), createLogEvent(LogLevel.info, 'info message'), createLogEvent(LogLevel.warn, 'warn message'), ]); await transport.flush(); expect(receivedLogs).toHaveLength(2); expect(receivedLogs.map((log) => log.level)).toEqual(['info', 'warn']); }); }); });