Files
AeroFetch/test/jsonStore.test.ts
T
debont80 1376c2dee8 Harden audit findings: correctness, type-safety, Windows conventions & polish
Audit-pass over CODE-AUDIT.md (~48 items closed this pass; all verified —
typecheck + 234 tests + eslint + prettier green).

Correctness / bugs:
- B3: match the release checksum to the asset's filename line (no wrong-hash verify)
- B4: newline-safe metadata probe (one --print with a unit-separator delimiter)
- B5 / L88: guard the meta event against canceled items; progress no longer promotes
  a queued item outside pump()
- B7: cookie-login promise always resolves (handles destroy-without-close)
- L146: trim parser rejects >2 colon-group times; M36: Library selection counts only
  actionable rows
- L11 / L50 / L156 / L57 / L159 / L15 / L3: live queue count, empty-cookie message,
  schedule picker min, dead-code/comment cleanup

Type safety:
- Enable noUncheckedIndexedAccess + noFallthroughCasesInSwitch (15 real edge cases fixed)

Resilience / Windows / metadata:
- R5: settings write failure handled (no unhandled IPC rejection; reconciles to truth)
- W1 / W5 / W6: min window size, seeded folder picker, parented sign-in window;
  L147 dead macOS branches removed
- CL1: shared stdout markers; package/builder metadata (license, homepage, repository,
  copyright, tsbuildinfo glob)

Copy / docs / tests:
- M37 / SR9 dev-jargon cleanup in hints; M8 / M25 / M26 / L66 / L80 / L81 reconciled
- New unit tests for L35 (isValidMediaItem) and L36 (compareVersions)

This commit also checkpoints the previously-uncommitted feat/tray-background-clipboard
work it builds on: background running + auto-download, library clipboard detection,
tray, binary management & library scale, credential encryption at rest, the shared
jsonStore and ui/ primitives, and the eslint/prettier tooling.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 13:02:54 -04:00

61 lines
2.0 KiB
TypeScript

import { describe, it, expect, afterEach } from 'vitest'
import { mkdtempSync, rmSync, writeFileSync, readdirSync } from 'fs'
import { tmpdir } from 'os'
import { join } from 'path'
import { readJsonArraySafe, writeJsonAtomic } from '../src/main/jsonStore'
interface Row {
id: string
}
const isRow = (o: unknown): o is Row =>
typeof o === 'object' && o !== null && typeof (o as Row).id === 'string'
describe('jsonStore atomic write + safe read', () => {
const dirs: string[] = []
function tmp(): string {
const d = mkdtempSync(join(tmpdir(), 'aerofetch-jsonstore-'))
dirs.push(d)
return d
}
afterEach(() => {
for (const d of dirs.splice(0)) {
try {
rmSync(d, { recursive: true, force: true })
} catch {
/* ignore */
}
}
})
it('round-trips a valid array (R1 write → read)', () => {
const file = join(tmp(), 'data.json')
writeJsonAtomic(file, [{ id: 'a' }, { id: 'b' }])
expect(readJsonArraySafe(file, isRow)).toEqual([{ id: 'a' }, { id: 'b' }])
})
it('leaves no .tmp file behind (atomic temp+rename)', () => {
const d = tmp()
writeJsonAtomic(join(d, 'data.json'), [{ id: 'a' }])
expect(readdirSync(d)).toEqual(['data.json'])
})
it('returns [] for a missing file (first run)', () => {
expect(readJsonArraySafe(join(tmp(), 'nope.json'), isRow)).toEqual([])
})
it('drops rows that fail validation', () => {
const file = join(tmp(), 'data.json')
writeFileSync(file, JSON.stringify([{ id: 'a' }, { nope: 1 }, { id: 'c' }]))
expect(readJsonArraySafe(file, isRow)).toEqual([{ id: 'a' }, { id: 'c' }])
})
it('backs up a corrupt file instead of silently wiping it (R2)', () => {
const d = tmp()
const file = join(d, 'data.json')
writeFileSync(file, '{ truncated, not valid json')
expect(readJsonArraySafe(file, isRow)).toEqual([])
const backups = readdirSync(d).filter((f) => f.startsWith('data.json.corrupt-'))
expect(backups.length).toBe(1)
})
})