const { test } = require('node:test') const assert = require('node:assert/strict') const fs = require('node:fs') const os = require('node:os') const path = require('node:path') const vm = require('node:vm') const ts = require('typescript') const exportsObject = {} const source = fs.readFileSync(path.resolve(__dirname, '../src/main/dailyLog.ts'), 'utf8') vm.runInNewContext(ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022 } }).outputText, { exports: exportsObject, require }) const { createDailyLog, redactLog } = exportsObject test('appends and rotates by local date, retaining unrelated files', (t) => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'lms-log-test-')) t.after(() => fs.rmSync(root, { recursive: true, force: true })) fs.writeFileSync(path.join(root, 'renderer.log'), 'existing log') let date = new Date(2026, 8, 29, 23, 59) const log = createDailyLog([root], () => date) log.write('test', 'first') log.write('test', 'second') const oldPath = log.path() assert.match(oldPath, /lms-2026-09-29\.log$/) assert.match(fs.readFileSync(oldPath, 'utf8'), /first[\s\S]*second/) assert.equal(fs.statSync(oldPath).mode & 0o777, 0o600) date = new Date(2026, 8, 30, 0, 1) log.write('test', 'next day') assert.match(log.path(), /lms-2026-09-30\.log$/) assert.equal(fs.existsSync(oldPath), false) assert.equal(fs.readFileSync(path.join(root, 'renderer.log'), 'utf8'), 'existing log') }) test('falls back when the primary directory is unusable', (t) => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'lms-log-test-')) t.after(() => fs.rmSync(root, { recursive: true, force: true })) const blocked = path.join(root, 'file') fs.writeFileSync(blocked, '') const log = createDailyLog([blocked, path.join(root, 'fallback')]) log.write('test', 'fallback') assert.equal(path.dirname(log.path()), path.join(root, 'fallback')) const unavailable = createDailyLog([blocked]) assert.doesNotThrow(() => unavailable.write('test', 'ignored')) assert.equal(unavailable.path(), '') }) test('redacts common authentication fields and credential URLs', () => { const result = redactLog('https://oauth2:secret123@example.com/repo.git Bearer secret456 --handoff-token=secret789 ?password=secret000 {"accessToken":"secret111"} password=secret222') for (const secret of ['secret123', 'secret456', 'secret789', 'secret000', 'secret111', 'secret222']) { assert.equal(result.includes(secret), false) } assert.match(result, /example.com\/repo.git/) }) test('bootstrap logs dependency loading failures before exiting', async () => { const events = [] const bootstrap = fs.readFileSync(path.resolve(__dirname, '../src/main/bootstrap.ts'), 'utf8') const code = ts.transpileModule(bootstrap, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022 } }).outputText vm.runInNewContext(code, { exports: {}, Error, require: (name) => { if (name === 'electron') return { app: { exit: (code) => events.push(['exit', code]) } } if (name === './appLogging') { events.push(['logger-ready']) return { appLog: { write: (...args) => events.push(args) } } } if (name === './index') throw new Error('test native module failure') throw new Error(`Unexpected dependency: ${name}`) } }) await new Promise((resolve) => setImmediate(resolve)) assert.equal(events[0][0], 'logger-ready') assert.equal(events[1][0], 'startup-error') assert.match(events[1][1], /test native module failure/) assert.deepEqual(events[2], ['exit', 1]) })