Files
AeroFetch/test/logger.test.ts
debont80 519e522917 feat(main): CC8 — file-backed leveled logger + renderer log sink
Diagnostics were invisible in a packaged build: main catches did a bare
console.error (DevTools suppressed in prod, M31) and the renderer's logError
(M29) wrote to an unreachable console. Add a small leveled logger
(src/main/logger.ts) appending to <userData>/logs/aerofetch.log with size-capped
rotation; all app/fs access is lazy+guarded so it's a safe no-op under tests.

- Route the 5 main-process console.* catch sites (jsonStore write-fail, settings
  write-fail + plaintext-secret warn, cookie loadURL, yt-dlp auto-update)
  through the logger.
- Add a fire-and-forget log:write IPC channel + preload logError() so the
  renderer's logError forwards failures to the main file sink (closes the M29
  "sink" half the code comments deferred to CC8). mockApi + Api type updated.
- Unit-test pure formatLine/composeMessage + no-op safety; update the R6
  jsonStore test to assert against the logger.

The user-facing toast half of CC8 ties to the global status surface (UI25/UX9).
typecheck + 258 tests + lint + prettier green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 08:53:06 -04:00

32 lines
1.4 KiB
TypeScript

import { describe, it, expect } from 'vitest'
import { formatLine, composeMessage, logger } from '../src/main/logger'
// The logger's file sink needs Electron's `app` (userData path), which is absent in
// the node test env — so here we cover the pure formatting helpers and assert that
// the impure entry points degrade to a safe no-op rather than throwing (CC8).
describe('logger (CC8)', () => {
const now = new Date('2026-07-01T08:30:00.000Z')
it('formatLine renders ISO timestamp + upper-case level + message', () => {
expect(formatLine('error', 'boom', now)).toBe('2026-07-01T08:30:00.000Z [ERROR] boom')
expect(formatLine('info', 'hi', now)).toBe('2026-07-01T08:30:00.000Z [INFO] hi')
})
it('composeMessage passes the label through when there is no detail', () => {
expect(composeMessage('settings write failed')).toBe('settings write failed')
})
it('composeMessage appends an Error message', () => {
expect(composeMessage('write failed', new Error('ENOSPC'))).toBe('write failed: ENOSPC')
})
it('composeMessage stringifies a non-Error detail', () => {
expect(composeMessage('op', 42)).toBe('op: 42')
expect(composeMessage('op', 'bad input')).toBe('op: bad input')
})
it('logger.error is a safe no-op when app/userData is unavailable', () => {
expect(() => logger.error('some op', new Error('x'))).not.toThrow()
})
})