import { mkdtemp, rm, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { loadBackupEnv } from './config.js'; const DATABASE_URL = 'postgresql://dorfteich:pw@db:5432/dorfteich'; let dir: string; beforeEach(async () => { dir = await mkdtemp(join(tmpdir(), 'dorfteich-backup-env-')); }); afterEach(async () => { await rm(dir, { recursive: true, force: true }); }); describe('loadBackupEnv', () => { it('applies defaults and drops compose empty strings', () => { // Compose passes optional variables as "" — they must fall through to // the schema defaults, not fail enum/number parsing (issue #80 lesson). const env = loadBackupEnv({ DATABASE_URL, BACKUP_TIME: '', BACKUP_RETENTION_DAYS: '', BACKUP_MAIL_LOCALE: '', SECRETS_FILE: join(dir, 'missing.env'), }); expect(env.BACKUP_TIME).toBe('03:00'); expect(env.BACKUP_RETENTION_DAYS).toBe(30); expect(env.BACKUP_MAIL_LOCALE).toBe('en'); }); it('fills SMTP settings from the wizard-written secret store', () => { const secretsFile = join(dir, 'secrets.env'); return writeFile(secretsFile, 'SMTP_HOST="relay.example.com"\nSMTP_PORT="2525"\n').then(() => { const env = loadBackupEnv({ DATABASE_URL, SECRETS_FILE: secretsFile, // Explicit container env must win over the store. SMTP_PORT: '465', }); expect(env.SMTP_HOST).toBe('relay.example.com'); expect(env.SMTP_PORT).toBe(465); }); }); it('rejects a malformed BACKUP_TIME', () => { expect(() => loadBackupEnv({ DATABASE_URL, BACKUP_TIME: '25:99' })).toThrow( /BACKUP_TIME.*HH:MM/s, ); }); });