// Seam tests for the API-layer logger. Pins: // - level gating at the default `info` level (info+ fires, debug doesn't) // - the `[ts] [LEVEL] [namespace]` prefix shape, with payload args // passed through verbatim as additional console.* arguments // - that child(ns) returns an independent logger with the right namespace // // VITE_LOG_LEVEL is intentionally NOT stubbed — the production default // (info) is the level this test pins. Other levels are the user's config // concern, not the contract. import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' import logger, { child } from '@/api/logger.js' beforeEach(() => { vi.spyOn(console, 'debug').mockImplementation(() => {}) vi.spyOn(console, 'info').mockImplementation(() => {}) vi.spyOn(console, 'warn').mockImplementation(() => {}) vi.spyOn(console, 'error').mockImplementation(() => {}) }) afterEach(() => { vi.restoreAllMocks() }) describe('root logger (default level: info)', () => { it('emits info/warn/error and suppresses debug', () => { logger.debug('d') logger.info('i') logger.warn('w') logger.error('e') expect(console.debug).not.toHaveBeenCalled() expect(console.info).toHaveBeenCalledTimes(1) expect(console.warn).toHaveBeenCalledTimes(1) expect(console.error).toHaveBeenCalledTimes(1) }) it('prefixes the first arg with [ts] [LEVEL] [namespace]; payload is a separate arg', () => { logger.info('hi') const call = console.info.mock.calls[0] expect(call[0]).toMatch(/^\[\d{2}:\d{2}:\d{2}\.\d{3}\] \[INFO\] \[api\]$/) expect(call[1]).toBe('hi') }) it('passes multiple payload args through verbatim', () => { logger.info('Loaded', { count: 5 }, [1, 2]) expect(console.info).toHaveBeenCalledWith( expect.stringMatching(/^\[.*\] \[INFO\] \[api\]$/), 'Loaded', { count: 5 }, [1, 2], ) }) }) describe('child(ns)', () => { it("returns a logger whose prefix namespace is the supplied ns", () => { const log = child('studentService') log.warn('hello') expect(console.warn).toHaveBeenCalledTimes(1) const call = console.warn.mock.calls[0] expect(call[0]).toMatch(/^\[.*\] \[WARN\] \[studentService\]$/) expect(call[1]).toBe('hello') }) it('child and root loggers keep their namespaces independent', () => { const log = child('taskService') log.info('a') logger.info('b') expect(console.info.mock.calls[0][0]).toMatch(/\[taskService\]$/) expect(console.info.mock.calls[1][0]).toMatch(/\[api\]$/) }) })