import { BackupSettingsService } from './backup-settings.service'; import { PrismaService } from '../prisma/prisma.service'; import { AuditLogService } from '../audit/audit-log.service'; describe('BackupSettingsService', () => { let service: BackupSettingsService; let prisma: { backupSettings: { findUnique: jest.Mock; create: jest.Mock; update: jest.Mock; }; }; let auditLog: { record: jest.Mock }; const existing = { id: 'singleton', storagePath: '/data/backups', cronSchedule: '0 3 * * *', retentionCount: 14, updatedAt: new Date('2026-01-01'), }; beforeEach(() => { prisma = { backupSettings: { findUnique: jest.fn(), create: jest.fn(), update: jest.fn(), }, }; auditLog = { record: jest.fn().mockResolvedValue(undefined) }; service = new BackupSettingsService( prisma as unknown as PrismaService, auditLog as unknown as AuditLogService, ); }); describe('get', () => { it('returns the existing singleton row', async () => { prisma.backupSettings.findUnique.mockResolvedValue(existing); const result = await service.get(); expect(result).toEqual(existing); expect(prisma.backupSettings.create).not.toHaveBeenCalled(); }); it('creates the singleton row on first access', async () => { prisma.backupSettings.findUnique.mockResolvedValue(null); prisma.backupSettings.create.mockResolvedValue({ id: 'singleton', storagePath: null, cronSchedule: null, retentionCount: null, updatedAt: new Date(), }); const result = await service.get(); expect(prisma.backupSettings.create).toHaveBeenCalledWith({ data: { id: 'singleton' }, }); expect(result.id).toBe('singleton'); }); }); describe('update', () => { it('stores a valid value and records an audit entry', async () => { prisma.backupSettings.findUnique.mockResolvedValue(existing); prisma.backupSettings.update.mockResolvedValue({ ...existing, retentionCount: 30, }); const result = await service.update({ retentionCount: 30 }, 'user-1'); expect(prisma.backupSettings.update).toHaveBeenCalledWith({ where: { id: 'singleton' }, data: { storagePath: undefined, cronSchedule: undefined, retentionCount: 30, }, }); expect(result.retentionCount).toBe(30); expect(auditLog.record).toHaveBeenCalledWith( expect.objectContaining({ entityType: 'BackupSettings', action: 'UPDATE', userId: 'user-1', }), ); }); it('clears a string field when given an empty string', async () => { prisma.backupSettings.findUnique.mockResolvedValue(existing); prisma.backupSettings.update.mockResolvedValue({ ...existing, storagePath: null, }); await service.update({ storagePath: '' }, 'user-1'); expect(prisma.backupSettings.update).toHaveBeenCalledWith({ where: { id: 'singleton' }, data: { storagePath: null, cronSchedule: undefined, retentionCount: undefined, }, }); }); it('leaves fields untouched when omitted from the dto', async () => { prisma.backupSettings.findUnique.mockResolvedValue(existing); prisma.backupSettings.update.mockResolvedValue(existing); await service.update({}, 'user-1'); expect(prisma.backupSettings.update).toHaveBeenCalledWith({ where: { id: 'singleton' }, data: { storagePath: undefined, cronSchedule: undefined, retentionCount: undefined, }, }); }); }); });