You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
52 lines
2.4 KiB
TypeScript
52 lines
2.4 KiB
TypeScript
// apps/hq/test/backup.test.ts — `npm run backup` (D33) must find the PRODUCTION database.
|
|
// It used to read process.env.DATABASE_URL directly and exit when it was unset — but on the
|
|
// prod box DATABASE_URL is not exported; the connection is RESOLVED (D19). So the backup
|
|
// command could never reach the real data. It now goes through the one shared resolver, and
|
|
// a SQLite outcome is a loud error rather than a pg_dump against a file it cannot read.
|
|
import { describe, it, expect } from 'vitest'
|
|
import { backupFileName, dumpsToRotate, resolveBackupTarget } from '../src/backup'
|
|
|
|
describe('resolveBackupTarget (same resolver as the server + every maintenance script)', () => {
|
|
it('uses DATABASE_URL when it is set', () => {
|
|
expect(resolveBackupTarget({ DATABASE_URL: 'postgres://x/y' })).toBe('postgres://x/y')
|
|
})
|
|
|
|
it('resolves the production target with no DATABASE_URL exported — the whole point of the fix', () => {
|
|
// NODE_ENV=production with no DATABASE_URL used to be an instant "DATABASE_URL is not set"
|
|
// exit; it must now resolve to the same Postgres the server picks.
|
|
const url = resolveBackupTarget({ NODE_ENV: 'production' })
|
|
expect(url).toMatch(/^postgres:\/\//)
|
|
})
|
|
|
|
it('refuses (never silently no-ops) when nothing resolves a target', () => {
|
|
expect(() => resolveBackupTarget({})).toThrow(/refusing to run/i)
|
|
})
|
|
|
|
it('says WHY, naming pg_dump, the SQLite path it landed on, and the SQLite snapshot script', () => {
|
|
const err = (() => { try { resolveBackupTarget({ HQ_DATA_DIR: 'D:/hq-data' }); return '' } catch (e) { return String(e) } })()
|
|
expect(err).toMatch(/pg_dump/)
|
|
expect(err).toMatch(/backup-hq\.sh/)
|
|
expect(err).toMatch(/hq\.db/)
|
|
expect(err).toMatch(/DATABASE_URL=postgres/)
|
|
})
|
|
})
|
|
|
|
describe('backup file naming + rotation', () => {
|
|
it('names dumps hq-YYYYMMDD-HHMMSS.dump', () => {
|
|
expect(backupFileName(new Date(2026, 6, 19, 2, 5, 9))).toBe('hq-20260719-020509.dump')
|
|
})
|
|
|
|
it('rotates out everything past the newest KEEP, ignoring unrelated files', () => {
|
|
const files = [
|
|
'hq-20260101-000000.dump', 'hq-20260102-000000.dump', 'hq-20260103-000000.dump',
|
|
'notes.txt', 'hq-old.dump',
|
|
]
|
|
expect(dumpsToRotate(files, 2)).toEqual(['hq-20260101-000000.dump'])
|
|
expect(dumpsToRotate(files, 14)).toEqual([])
|
|
})
|
|
|
|
it('keeps at least one dump however low KEEP is set', () => {
|
|
expect(dumpsToRotate(['hq-20260101-000000.dump'], 0)).toEqual([])
|
|
})
|
|
})
|