test(JiraSync): further enhance test coverage for synchronization and change detection logic
This commit is contained in:
319
src/services/data-management/__tests__/backup-scheduler.test.ts
Normal file
319
src/services/data-management/__tests__/backup-scheduler.test.ts
Normal file
@@ -0,0 +1,319 @@
|
||||
/**
|
||||
* Tests unitaires pour BackupScheduler
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
||||
import { BackupScheduler } from '../backup-scheduler';
|
||||
import { backupService } from '../backup';
|
||||
import { getToday, addMinutes } from '@/lib/date-utils';
|
||||
|
||||
// Mock de backupService
|
||||
vi.mock('../backup', () => ({
|
||||
backupService: {
|
||||
getConfigSync: vi.fn(),
|
||||
createBackup: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
// Mock de date-utils
|
||||
vi.mock('@/lib/date-utils', () => ({
|
||||
getToday: vi.fn(),
|
||||
addMinutes: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('BackupScheduler', () => {
|
||||
let scheduler: BackupScheduler;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
scheduler = new BackupScheduler();
|
||||
vi.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
scheduler.stop();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
describe('start', () => {
|
||||
it('devrait démarrer le scheduler si activé', () => {
|
||||
vi.mocked(backupService.getConfigSync).mockReturnValue({
|
||||
enabled: true,
|
||||
interval: 'hourly',
|
||||
maxBackups: 5,
|
||||
backupPath: '/backups',
|
||||
} as any);
|
||||
|
||||
scheduler.start();
|
||||
|
||||
expect(scheduler.isActive()).toBe(true);
|
||||
});
|
||||
|
||||
it('ne devrait pas démarrer si désactivé', () => {
|
||||
vi.mocked(backupService.getConfigSync).mockReturnValue({
|
||||
enabled: false,
|
||||
interval: 'hourly',
|
||||
maxBackups: 5,
|
||||
backupPath: '/backups',
|
||||
} as any);
|
||||
|
||||
scheduler.start();
|
||||
|
||||
expect(scheduler.isActive()).toBe(false);
|
||||
});
|
||||
|
||||
it('ne devrait pas démarrer deux fois', () => {
|
||||
vi.mocked(backupService.getConfigSync).mockReturnValue({
|
||||
enabled: true,
|
||||
interval: 'hourly',
|
||||
maxBackups: 5,
|
||||
backupPath: '/backups',
|
||||
} as any);
|
||||
|
||||
scheduler.start();
|
||||
const firstCall = scheduler.isActive();
|
||||
scheduler.start();
|
||||
const secondCall = scheduler.isActive();
|
||||
|
||||
expect(firstCall).toBe(true);
|
||||
expect(secondCall).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('stop', () => {
|
||||
it('devrait arrêter le scheduler', () => {
|
||||
vi.mocked(backupService.getConfigSync).mockReturnValue({
|
||||
enabled: true,
|
||||
interval: 'hourly',
|
||||
maxBackups: 5,
|
||||
backupPath: '/backups',
|
||||
} as any);
|
||||
|
||||
scheduler.start();
|
||||
expect(scheduler.isActive()).toBe(true);
|
||||
|
||||
scheduler.stop();
|
||||
expect(scheduler.isActive()).toBe(false);
|
||||
});
|
||||
|
||||
it("devrait gérer l'arrêt si pas démarré", () => {
|
||||
scheduler.stop();
|
||||
expect(scheduler.isActive()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('restart', () => {
|
||||
it('devrait redémarrer le scheduler', () => {
|
||||
vi.mocked(backupService.getConfigSync).mockReturnValue({
|
||||
enabled: true,
|
||||
interval: 'hourly',
|
||||
maxBackups: 5,
|
||||
backupPath: '/backups',
|
||||
} as any);
|
||||
|
||||
scheduler.start();
|
||||
expect(scheduler.isActive()).toBe(true);
|
||||
|
||||
scheduler.restart();
|
||||
expect(scheduler.isActive()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isActive', () => {
|
||||
it('devrait retourner false si pas démarré', () => {
|
||||
expect(scheduler.isActive()).toBe(false);
|
||||
});
|
||||
|
||||
it('devrait retourner true si démarré', () => {
|
||||
vi.mocked(backupService.getConfigSync).mockReturnValue({
|
||||
enabled: true,
|
||||
interval: 'hourly',
|
||||
maxBackups: 5,
|
||||
backupPath: '/backups',
|
||||
} as any);
|
||||
|
||||
scheduler.start();
|
||||
expect(scheduler.isActive()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('performScheduledBackup', () => {
|
||||
it('devrait exécuter une sauvegarde automatique', async () => {
|
||||
vi.mocked(backupService.getConfigSync).mockReturnValue({
|
||||
enabled: true,
|
||||
interval: 'hourly',
|
||||
maxBackups: 5,
|
||||
backupPath: '/backups',
|
||||
} as any);
|
||||
|
||||
vi.mocked(backupService.createBackup).mockResolvedValue({
|
||||
id: '1',
|
||||
filename: 'backup.db.gz',
|
||||
size: 1024,
|
||||
createdAt: new Date(),
|
||||
type: 'automatic',
|
||||
status: 'success',
|
||||
} as any);
|
||||
|
||||
scheduler.start();
|
||||
|
||||
// Avancer le timer pour déclencher la sauvegarde une seule fois
|
||||
vi.advanceTimersByTime(60 * 60 * 1000); // 1 heure
|
||||
|
||||
// Attendre que la sauvegarde soit exécutée
|
||||
await vi.waitFor(() => {
|
||||
expect(backupService.createBackup).toHaveBeenCalledWith('automatic');
|
||||
});
|
||||
|
||||
scheduler.stop();
|
||||
});
|
||||
|
||||
it('devrait gérer les sauvegardes ignorées', async () => {
|
||||
vi.mocked(backupService.getConfigSync).mockReturnValue({
|
||||
enabled: true,
|
||||
interval: 'hourly',
|
||||
maxBackups: 5,
|
||||
backupPath: '/backups',
|
||||
} as any);
|
||||
|
||||
vi.mocked(backupService.createBackup).mockResolvedValue(null);
|
||||
|
||||
scheduler.start();
|
||||
|
||||
vi.advanceTimersByTime(60 * 60 * 1000);
|
||||
await vi.waitFor(() => {
|
||||
expect(backupService.createBackup).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
scheduler.stop();
|
||||
});
|
||||
|
||||
it('devrait gérer les erreurs de sauvegarde', async () => {
|
||||
vi.mocked(backupService.getConfigSync).mockReturnValue({
|
||||
enabled: true,
|
||||
interval: 'hourly',
|
||||
maxBackups: 5,
|
||||
backupPath: '/backups',
|
||||
} as any);
|
||||
|
||||
vi.mocked(backupService.createBackup).mockRejectedValue(
|
||||
new Error('Backup failed')
|
||||
);
|
||||
|
||||
scheduler.start();
|
||||
|
||||
vi.advanceTimersByTime(60 * 60 * 1000);
|
||||
await vi.waitFor(() => {
|
||||
expect(backupService.createBackup).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
scheduler.stop();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getIntervalMs', () => {
|
||||
it('devrait convertir hourly en millisecondes', () => {
|
||||
vi.mocked(backupService.getConfigSync).mockReturnValue({
|
||||
enabled: true,
|
||||
interval: 'hourly',
|
||||
maxBackups: 5,
|
||||
backupPath: '/backups',
|
||||
} as any);
|
||||
|
||||
scheduler.start();
|
||||
const status = scheduler.getStatus();
|
||||
|
||||
expect(status.interval).toBe('hourly');
|
||||
});
|
||||
|
||||
it('devrait convertir daily en millisecondes', () => {
|
||||
vi.mocked(backupService.getConfigSync).mockReturnValue({
|
||||
enabled: true,
|
||||
interval: 'daily',
|
||||
maxBackups: 5,
|
||||
backupPath: '/backups',
|
||||
} as any);
|
||||
|
||||
scheduler.start();
|
||||
const status = scheduler.getStatus();
|
||||
|
||||
expect(status.interval).toBe('daily');
|
||||
});
|
||||
|
||||
it('devrait convertir weekly en millisecondes', () => {
|
||||
vi.mocked(backupService.getConfigSync).mockReturnValue({
|
||||
enabled: true,
|
||||
interval: 'weekly',
|
||||
maxBackups: 5,
|
||||
backupPath: '/backups',
|
||||
} as any);
|
||||
|
||||
scheduler.start();
|
||||
const status = scheduler.getStatus();
|
||||
|
||||
expect(status.interval).toBe('weekly');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getNextBackupTime', () => {
|
||||
it('devrait retourner null si pas démarré', () => {
|
||||
const nextTime = scheduler.getNextBackupTime();
|
||||
|
||||
expect(nextTime).toBeNull();
|
||||
});
|
||||
|
||||
it('devrait calculer le prochain moment de sauvegarde', () => {
|
||||
const mockDate = new Date('2024-01-15T12:00:00Z');
|
||||
vi.mocked(getToday).mockReturnValue(mockDate);
|
||||
vi.mocked(addMinutes).mockReturnValue(new Date('2024-01-15T13:00:00Z'));
|
||||
|
||||
vi.mocked(backupService.getConfigSync).mockReturnValue({
|
||||
enabled: true,
|
||||
interval: 'hourly',
|
||||
maxBackups: 5,
|
||||
backupPath: '/backups',
|
||||
} as any);
|
||||
|
||||
scheduler.start();
|
||||
const nextTime = scheduler.getNextBackupTime();
|
||||
|
||||
expect(nextTime).not.toBeNull();
|
||||
expect(addMinutes).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getStatus', () => {
|
||||
it('devrait retourner le statut du scheduler', () => {
|
||||
vi.mocked(backupService.getConfigSync).mockReturnValue({
|
||||
enabled: true,
|
||||
interval: 'daily',
|
||||
maxBackups: 10,
|
||||
backupPath: '/custom/backups',
|
||||
} as any);
|
||||
|
||||
scheduler.start();
|
||||
const status = scheduler.getStatus();
|
||||
|
||||
expect(status.isRunning).toBe(true);
|
||||
expect(status.isEnabled).toBe(true);
|
||||
expect(status.interval).toBe('daily');
|
||||
expect(status.maxBackups).toBe(10);
|
||||
expect(status.backupPath).toBe('/custom/backups');
|
||||
});
|
||||
|
||||
it('devrait retourner le statut si pas démarré', () => {
|
||||
vi.mocked(backupService.getConfigSync).mockReturnValue({
|
||||
enabled: false,
|
||||
interval: 'hourly',
|
||||
maxBackups: 5,
|
||||
backupPath: '/backups',
|
||||
} as any);
|
||||
|
||||
const status = scheduler.getStatus();
|
||||
|
||||
expect(status.isRunning).toBe(false);
|
||||
expect(status.isEnabled).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
400
src/services/data-management/__tests__/backup.test.ts
Normal file
400
src/services/data-management/__tests__/backup.test.ts
Normal file
@@ -0,0 +1,400 @@
|
||||
/**
|
||||
* Tests unitaires pour BackupService
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { BackupService } from '../backup';
|
||||
import { prisma } from '@/services/core/database';
|
||||
import { userPreferencesService } from '@/services/core/user-preferences';
|
||||
import { BackupUtils } from '@/lib/backup-utils';
|
||||
import { promises as fs } from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
// Mock de prisma
|
||||
vi.mock('@/services/core/database', () => ({
|
||||
prisma: {
|
||||
$disconnect: vi.fn(),
|
||||
$connect: vi.fn(),
|
||||
$queryRaw: vi.fn(),
|
||||
userPreferences: {
|
||||
upsert: vi.fn(),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
// Mock de userPreferencesService
|
||||
vi.mock('@/services/core/user-preferences', () => ({
|
||||
userPreferencesService: {
|
||||
getAllPreferences: vi.fn(),
|
||||
getViewPreferences: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
// Mock de BackupUtils
|
||||
vi.mock('@/lib/backup-utils', () => ({
|
||||
BackupUtils: {
|
||||
resolveBackupStoragePath: vi.fn(),
|
||||
resolveDatabasePath: vi.fn(),
|
||||
calculateFileHash: vi.fn(),
|
||||
ensureDirectory: vi.fn(),
|
||||
createSQLiteBackup: vi.fn(),
|
||||
compressFile: vi.fn(),
|
||||
decompressFileTemp: vi.fn(),
|
||||
writeLogEntry: vi.fn(),
|
||||
parseBackupFilename: vi.fn(),
|
||||
generateBackupFilename: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
// Mock de fs (pour promises)
|
||||
vi.mock('fs', () => ({
|
||||
promises: {
|
||||
readFile: vi.fn(),
|
||||
writeFile: vi.fn(),
|
||||
stat: vi.fn(),
|
||||
readdir: vi.fn(),
|
||||
unlink: vi.fn(),
|
||||
access: vi.fn(),
|
||||
copyFile: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('BackupService', () => {
|
||||
let backupService: BackupService;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
backupService = new BackupService();
|
||||
|
||||
// Mocks par défaut
|
||||
vi.mocked(BackupUtils.resolveBackupStoragePath).mockReturnValue('/backups');
|
||||
vi.mocked(BackupUtils.resolveDatabasePath).mockReturnValue('/db/dev.db');
|
||||
vi.mocked(BackupUtils.calculateFileHash).mockResolvedValue('hash123');
|
||||
vi.mocked(BackupUtils.ensureDirectory).mockResolvedValue(undefined);
|
||||
vi.mocked(BackupUtils.createSQLiteBackup).mockResolvedValue(undefined);
|
||||
vi.mocked(BackupUtils.compressFile).mockResolvedValue(
|
||||
'/backups/file.db.gz'
|
||||
);
|
||||
vi.mocked(BackupUtils.parseBackupFilename).mockReturnValue({
|
||||
type: 'manual',
|
||||
date: new Date(),
|
||||
});
|
||||
vi.mocked(BackupUtils.generateBackupFilename).mockReturnValue(
|
||||
'towercontrol_manual_2024-01-01.db'
|
||||
);
|
||||
vi.mocked(fs.stat).mockResolvedValue({
|
||||
size: 1024,
|
||||
birthtime: new Date(),
|
||||
} as any);
|
||||
vi.mocked(fs.readdir).mockResolvedValue([]);
|
||||
vi.mocked(userPreferencesService.getAllPreferences).mockResolvedValue({
|
||||
viewPreferences: {},
|
||||
} as any);
|
||||
vi.mocked(userPreferencesService.getViewPreferences).mockResolvedValue(
|
||||
{} as any
|
||||
);
|
||||
});
|
||||
|
||||
describe('createBackup', () => {
|
||||
it('devrait créer une sauvegarde manuelle', async () => {
|
||||
vi.mocked(BackupUtils.compressFile).mockResolvedValue(
|
||||
'/backups/towercontrol_manual_2024-01-01.db.gz'
|
||||
);
|
||||
vi.mocked(fs.stat).mockResolvedValue({
|
||||
size: 2048,
|
||||
birthtime: new Date(),
|
||||
} as any);
|
||||
|
||||
const result = await backupService.createBackup('manual', true);
|
||||
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.type).toBe('manual');
|
||||
expect(result?.status).toBe('success');
|
||||
expect(result?.filename).toContain('towercontrol_manual');
|
||||
});
|
||||
|
||||
it('devrait créer une sauvegarde automatique', async () => {
|
||||
vi.mocked(BackupUtils.compressFile).mockResolvedValue(
|
||||
'/backups/towercontrol_automatic_2024-01-01.db.gz'
|
||||
);
|
||||
|
||||
const result = await backupService.createBackup('automatic', true);
|
||||
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.type).toBe('automatic');
|
||||
});
|
||||
|
||||
it('devrait retourner null si pas de changements et forceCreate=false', async () => {
|
||||
// Mock pour simuler qu'il n'y a pas de changements
|
||||
vi.mocked(fs.readdir).mockResolvedValue([
|
||||
'towercontrol_manual_2024-01-01.db.gz',
|
||||
] as any);
|
||||
vi.mocked(BackupUtils.calculateFileHash).mockResolvedValue('samehash');
|
||||
vi.mocked(fs.readFile).mockResolvedValue(
|
||||
JSON.stringify({ databaseHash: 'samehash' })
|
||||
);
|
||||
|
||||
const result = await backupService.createBackup('automatic', false);
|
||||
|
||||
// Si pas de changements, devrait retourner null
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('devrait gérer les erreurs lors de la création', async () => {
|
||||
vi.mocked(BackupUtils.createSQLiteBackup).mockRejectedValue(
|
||||
new Error('Backup failed')
|
||||
);
|
||||
|
||||
const result = await backupService.createBackup('manual', true);
|
||||
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.status).toBe('failed');
|
||||
expect(result?.error).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('listBackups', () => {
|
||||
it('devrait lister les sauvegardes disponibles', async () => {
|
||||
vi.mocked(fs.readdir).mockResolvedValue([
|
||||
'towercontrol_manual_2024-01-01.db.gz',
|
||||
'towercontrol_automatic_2024-01-02.db.gz',
|
||||
] as any);
|
||||
|
||||
const backups = await backupService.listBackups();
|
||||
|
||||
expect(backups).toHaveLength(2);
|
||||
expect(backups[0].filename).toContain('towercontrol_');
|
||||
});
|
||||
|
||||
it('devrait retourner une liste vide si aucun backup', async () => {
|
||||
vi.mocked(fs.readdir).mockResolvedValue([]);
|
||||
|
||||
const backups = await backupService.listBackups();
|
||||
|
||||
expect(backups).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('devrait filtrer les fichiers non-backup', async () => {
|
||||
vi.mocked(fs.readdir).mockResolvedValue([
|
||||
'towercontrol_manual_2024-01-01.db.gz',
|
||||
'other_file.txt',
|
||||
'backup.log',
|
||||
] as any);
|
||||
|
||||
const backups = await backupService.listBackups();
|
||||
|
||||
expect(backups).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('devrait gérer les erreurs', async () => {
|
||||
(fs.readdir as any).mockRejectedValue(new Error('Read error'));
|
||||
|
||||
const backups = await backupService.listBackups();
|
||||
|
||||
expect(backups).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteBackup', () => {
|
||||
it('devrait supprimer un backup et ses métadonnées', async () => {
|
||||
await backupService.deleteBackup('backup.db.gz');
|
||||
|
||||
expect(fs.unlink).toHaveBeenCalledWith(
|
||||
path.join('/backups', 'backup.db.gz')
|
||||
);
|
||||
expect(fs.unlink).toHaveBeenCalledWith(
|
||||
path.join('/backups', 'backup.db.gz.meta.json')
|
||||
);
|
||||
});
|
||||
|
||||
it('devrait gérer les erreurs lors de la suppression', async () => {
|
||||
vi.mocked(fs.unlink).mockRejectedValue(new Error('Delete failed'));
|
||||
|
||||
await expect(
|
||||
backupService.deleteBackup('backup.db.gz')
|
||||
).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('restoreBackup', () => {
|
||||
beforeEach(() => {
|
||||
vi.mocked(prisma.$queryRaw).mockResolvedValue([
|
||||
{ integrity_check: 'ok' },
|
||||
]);
|
||||
vi.spyOn(backupService, 'createBackup').mockResolvedValue({
|
||||
id: 'pre-restore',
|
||||
filename: 'pre-restore.db.gz',
|
||||
size: 1024,
|
||||
createdAt: new Date(),
|
||||
type: 'manual',
|
||||
status: 'success',
|
||||
} as any);
|
||||
});
|
||||
|
||||
it('devrait restaurer un backup non compressé', async () => {
|
||||
vi.mocked(fs.access).mockResolvedValue(undefined);
|
||||
vi.mocked(fs.copyFile).mockResolvedValue(undefined);
|
||||
|
||||
await backupService.restoreBackup('backup.db');
|
||||
|
||||
expect(fs.copyFile).toHaveBeenCalled();
|
||||
expect(prisma.$connect).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('devrait décompresser et restaurer un backup compressé', async () => {
|
||||
vi.mocked(fs.access).mockResolvedValue(undefined);
|
||||
vi.mocked(BackupUtils.decompressFileTemp).mockResolvedValue(undefined);
|
||||
vi.mocked(fs.copyFile).mockResolvedValue(undefined);
|
||||
vi.mocked(fs.unlink).mockResolvedValue(undefined);
|
||||
|
||||
await backupService.restoreBackup('backup.db.gz');
|
||||
|
||||
expect(BackupUtils.decompressFileTemp).toHaveBeenCalled();
|
||||
expect(fs.copyFile).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('devrait gérer les erreurs lors de la restauration', async () => {
|
||||
vi.mocked(fs.access).mockRejectedValue(new Error('File not found'));
|
||||
|
||||
await expect(backupService.restoreBackup('backup.db')).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('verifyDatabaseHealth', () => {
|
||||
it("devrait vérifier l'intégrité de la base de données", async () => {
|
||||
vi.mocked(prisma.$queryRaw).mockResolvedValue([
|
||||
{ integrity_check: 'ok' },
|
||||
]);
|
||||
|
||||
await backupService.verifyDatabaseHealth();
|
||||
|
||||
expect(prisma.$queryRaw).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("devrait échouer si l'intégrité est compromise", async () => {
|
||||
vi.mocked(prisma.$queryRaw).mockResolvedValue([
|
||||
{ integrity_check: 'error in database' },
|
||||
]);
|
||||
|
||||
await expect(backupService.verifyDatabaseHealth()).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasChangedSinceLastBackup', () => {
|
||||
it('devrait retourner true si aucun backup précédent', async () => {
|
||||
vi.mocked(fs.readdir).mockResolvedValue([]);
|
||||
|
||||
const hasChanged = await backupService.hasChangedSinceLastBackup();
|
||||
|
||||
expect(hasChanged).toBe(true);
|
||||
});
|
||||
|
||||
it('devrait retourner true si le hash a changé', async () => {
|
||||
vi.mocked(fs.readdir).mockResolvedValue([
|
||||
'towercontrol_manual_2024-01-01.db.gz',
|
||||
] as any);
|
||||
vi.mocked(BackupUtils.calculateFileHash)
|
||||
.mockResolvedValueOnce('newhash')
|
||||
.mockResolvedValueOnce('oldhash');
|
||||
vi.mocked(fs.readFile).mockResolvedValue(
|
||||
JSON.stringify({ databaseHash: 'oldhash' })
|
||||
);
|
||||
|
||||
const hasChanged = await backupService.hasChangedSinceLastBackup();
|
||||
|
||||
expect(hasChanged).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBackupLogs', () => {
|
||||
it('devrait lire les logs de backup', async () => {
|
||||
vi.mocked(fs.readFile).mockResolvedValue(
|
||||
'2024-01-01: Backup created\n2024-01-02: Backup created'
|
||||
);
|
||||
|
||||
const logs = await backupService.getBackupLogs();
|
||||
|
||||
expect(logs).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("devrait retourner une liste vide si le fichier n'existe pas", async () => {
|
||||
(fs.readFile as any).mockRejectedValue(new Error('File not found'));
|
||||
|
||||
const logs = await backupService.getBackupLogs();
|
||||
|
||||
expect(logs).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('devrait limiter le nombre de lignes', async () => {
|
||||
const manyLines = Array.from({ length: 200 }, (_, i) => `Line ${i}`).join(
|
||||
'\n'
|
||||
);
|
||||
vi.mocked(fs.readFile).mockResolvedValue(manyLines);
|
||||
|
||||
const logs = await backupService.getBackupLogs(50);
|
||||
|
||||
expect(logs.length).toBeLessThanOrEqual(50);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBackupStats', () => {
|
||||
it('devrait calculer les statistiques par jour', async () => {
|
||||
const now = new Date('2024-01-15');
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(now);
|
||||
|
||||
vi.mocked(fs.readdir).mockResolvedValue([
|
||||
'towercontrol_manual_2024-01-15.db.gz',
|
||||
'towercontrol_automatic_2024-01-15.db.gz',
|
||||
'towercontrol_manual_2024-01-14.db.gz',
|
||||
] as any);
|
||||
vi.mocked(BackupUtils.parseBackupFilename)
|
||||
.mockReturnValueOnce({
|
||||
type: 'manual',
|
||||
date: new Date('2024-01-15'),
|
||||
})
|
||||
.mockReturnValueOnce({
|
||||
type: 'automatic',
|
||||
date: new Date('2024-01-15'),
|
||||
})
|
||||
.mockReturnValueOnce({
|
||||
type: 'manual',
|
||||
date: new Date('2024-01-14'),
|
||||
});
|
||||
|
||||
const stats = await backupService.getBackupStats(7);
|
||||
|
||||
expect(stats.length).toBeGreaterThan(0);
|
||||
const todayStats = stats.find((s) => s.date === '2024-01-15');
|
||||
expect(todayStats).toBeDefined();
|
||||
expect(todayStats?.manual).toBe(1);
|
||||
expect(todayStats?.automatic).toBe(1);
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getConfig', () => {
|
||||
it('devrait retourner la configuration', async () => {
|
||||
const config = await backupService.getConfig();
|
||||
|
||||
expect(config).toHaveProperty('enabled');
|
||||
expect(config).toHaveProperty('interval');
|
||||
expect(config).toHaveProperty('maxBackups');
|
||||
expect(config).toHaveProperty('backupPath');
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateConfig', () => {
|
||||
it('devrait mettre à jour la configuration', async () => {
|
||||
vi.mocked(prisma.userPreferences.upsert).mockResolvedValue({
|
||||
userId: 'default',
|
||||
} as any);
|
||||
|
||||
await backupService.updateConfig({ maxBackups: 10 });
|
||||
|
||||
expect(prisma.userPreferences.upsert).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user