/** * Unit tests for the pure helpers of the backup-destinations service: * schedule-due logic + secret config (serialize → encrypt at rest → * decrypt for use, mask for API). DB-backed CRUD is covered by the e2e * verification, not here. */ import { describe, expect, it } from 'vitest'; import { decryptConfig, isScheduleDue, maskConfig, serializeConfig, } from '@/lib/services/backup-destinations.service'; describe('isScheduleDue', () => { // 2026-06-07 is a Sunday; 2026-06-08 a Monday. const sunday = new Date('2026-06-07T02:00:00Z'); const monday = new Date('2026-06-08T02:00:00Z'); it('off is never due', () => { expect(isScheduleDue('off', sunday)).toBe(false); expect(isScheduleDue('off', monday)).toBe(false); }); it('daily is always due', () => { expect(isScheduleDue('daily', sunday)).toBe(true); expect(isScheduleDue('daily', monday)).toBe(true); }); it('weekly is due only on Sunday', () => { expect(isScheduleDue('weekly', sunday)).toBe(true); expect(isScheduleDue('weekly', monday)).toBe(false); }); }); describe('secret config handling', () => { it('serialize encrypts secrets, decrypt restores them, mask hides them', () => { const incoming = { host: 'box.example.com', username: 'crm', password: 'hunter2', remoteDir: '/backups', }; const stored = serializeConfig('sftp', incoming); // Stored password must not be the plaintext. expect(stored.password).not.toBe('hunter2'); expect(stored.host).toBe('box.example.com'); // Decrypt restores the plaintext for transport use. expect(decryptConfig('sftp', stored)).toMatchObject({ host: 'box.example.com', username: 'crm', password: 'hunter2', remoteDir: '/backups', }); // Mask hides the secret and exposes only a *IsSet marker. const masked = maskConfig('sftp', stored); expect(masked.password).toBeUndefined(); expect(masked.passwordIsSet).toBe(true); expect(masked.host).toBe('box.example.com'); }); it('update with a blank secret keeps the existing encrypted value', () => { const original = serializeConfig('s3', { endpoint: 'https://s3.example.com', bucket: 'b', accessKey: 'AK', secretKey: 'SUPERSECRET', }); // Admin edits the bucket but leaves the secret key blank (unchanged). const updated = serializeConfig( 's3', { endpoint: 'https://s3.example.com', bucket: 'b2', accessKey: 'AK', secretKey: '' }, original, ); expect(updated.bucket).toBe('b2'); expect(decryptConfig('s3', updated).secretKey).toBe('SUPERSECRET'); }); it('filesystem has no secrets to mask', () => { const stored = serializeConfig('filesystem', { directory: '/mnt/nas/backups' }); expect(maskConfig('filesystem', stored)).toEqual({ directory: '/mnt/nas/backups' }); }); });