519e522917
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>
32 lines
1.4 KiB
TypeScript
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()
|
|
})
|
|
})
|