test(JiraSync): improve test coverage for synchronization logic and change detection

This commit is contained in:
Julien Froidefond
2025-11-21 14:58:31 +01:00
parent 5c9b2b9d8f
commit 4496cd97f9
3 changed files with 1185 additions and 0 deletions

View File

@@ -0,0 +1,93 @@
/**
* Tests unitaires pour database.ts
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import {
testDatabaseConnection,
closeDatabaseConnection,
clearDatabase,
prisma,
} from '../database';
describe('database utilities', () => {
beforeEach(() => {
vi.clearAllMocks();
});
describe('testDatabaseConnection', () => {
it('devrait retourner true si la connexion réussit', async () => {
vi.spyOn(prisma, '$connect').mockResolvedValue(undefined);
const result = await testDatabaseConnection();
expect(result).toBe(true);
expect(prisma.$connect).toHaveBeenCalled();
});
it('devrait retourner false si la connexion échoue', async () => {
vi.spyOn(prisma, '$connect').mockRejectedValue(
new Error('Connection failed')
);
const result = await testDatabaseConnection();
expect(result).toBe(false);
});
});
describe('closeDatabaseConnection', () => {
it('devrait fermer la connexion avec succès', async () => {
vi.spyOn(prisma, '$disconnect').mockResolvedValue(undefined);
await closeDatabaseConnection();
expect(prisma.$disconnect).toHaveBeenCalled();
});
it('devrait gérer les erreurs lors de la fermeture', async () => {
vi.spyOn(prisma, '$disconnect').mockRejectedValue(
new Error('Disconnect failed')
);
// Ne devrait pas throw
await expect(closeDatabaseConnection()).resolves.not.toThrow();
});
});
describe('clearDatabase', () => {
beforeEach(() => {
// S'assurer qu'on est pas en production
vi.stubEnv('NODE_ENV', 'test');
});
afterEach(() => {
vi.unstubAllEnvs();
});
it('devrait nettoyer toutes les tables', async () => {
const deleteManySpy = vi.fn().mockResolvedValue({ count: 0 });
// Mock des méthodes deleteMany directement
(prisma.taskTag as any) = { deleteMany: deleteManySpy };
(prisma.task as any) = { deleteMany: deleteManySpy };
(prisma.tag as any) = { deleteMany: deleteManySpy };
(prisma.syncLog as any) = { deleteMany: deleteManySpy };
await clearDatabase();
expect(deleteManySpy).toHaveBeenCalledTimes(4);
});
it('ne devrait pas permettre le nettoyage en production', async () => {
vi.stubEnv('NODE_ENV', 'production');
await expect(clearDatabase()).rejects.toThrow(
'Cannot clear database in production'
);
vi.unstubAllEnvs();
});
});
});

View File

@@ -0,0 +1,256 @@
/**
* Tests unitaires pour SystemInfoService
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { SystemInfoService } from '../system-info';
import { prisma } from '../database';
import { readFile } from 'fs/promises';
import { stat } from 'fs/promises';
// Mock de prisma
vi.mock('../database', () => ({
prisma: {
task: {
count: vi.fn(),
},
userPreferences: {
count: vi.fn(),
},
tag: {
count: vi.fn(),
},
dailyCheckbox: {
count: vi.fn(),
},
},
}));
// Mock de fs/promises
vi.mock('fs/promises', () => ({
readFile: vi.fn(),
stat: vi.fn(),
}));
// Mock de path
vi.mock('path', () => ({
join: (...args: string[]) => args.join('/'),
resolve: (...args: string[]) => args.join('/'),
}));
// Mock du service backup
vi.mock('@/services/data-management/backup', () => ({
backupService: {
listBackups: vi.fn(),
},
}));
describe('SystemInfoService', () => {
beforeEach(() => {
vi.clearAllMocks();
vi.stubEnv('NODE_ENV', 'test');
});
afterEach(() => {
vi.unstubAllEnvs();
});
describe('getSystemInfo', () => {
it('devrait retourner les informations système complètes', async () => {
const mockPackageJson = {
name: 'towercontrol',
version: '1.0.0',
};
vi.mocked(readFile).mockResolvedValue(JSON.stringify(mockPackageJson));
vi.mocked(prisma.task.count).mockResolvedValue(10);
vi.mocked(prisma.userPreferences.count).mockResolvedValue(1);
vi.mocked(prisma.tag.count).mockResolvedValue(5);
vi.mocked(prisma.dailyCheckbox.count).mockResolvedValue(20);
const { backupService } = await import(
'@/services/data-management/backup'
);
vi.mocked(backupService.listBackups).mockResolvedValue([
{ id: '1', filename: 'backup1.db.gz' },
{ id: '2', filename: 'backup2.db.gz' },
] as any);
vi.mocked(stat).mockResolvedValue({
size: 1024 * 1024, // 1 MB
} as any);
const result = await SystemInfoService.getSystemInfo();
expect(result).toHaveProperty('version');
expect(result).toHaveProperty('environment');
expect(result).toHaveProperty('database');
expect(result).toHaveProperty('backups');
expect(result).toHaveProperty('app');
expect(result).toHaveProperty('uptime');
expect(result).toHaveProperty('lastUpdate');
expect(result.version).toBe('1.0.0');
expect(result.environment).toBe('test');
expect(result.database.totalTasks).toBe(10);
expect(result.database.totalUsers).toBe(1);
expect(result.database.totalBackups).toBe(2);
expect(result.database.totalTags).toBe(5);
expect(result.database.totalDailies).toBe(20);
});
it('devrait gérer les erreurs lors de la lecture du package.json', async () => {
vi.mocked(readFile).mockRejectedValue(new Error('File not found'));
vi.mocked(prisma.task.count).mockResolvedValue(0);
vi.mocked(prisma.userPreferences.count).mockResolvedValue(0);
vi.mocked(prisma.tag.count).mockResolvedValue(0);
vi.mocked(prisma.dailyCheckbox.count).mockResolvedValue(0);
const { backupService } = await import(
'@/services/data-management/backup'
);
vi.mocked(backupService.listBackups).mockResolvedValue([]);
vi.mocked(stat).mockRejectedValue(new Error('File not found'));
const result = await SystemInfoService.getSystemInfo();
expect(result.version).toBe('1.0.0'); // Valeur par défaut
expect(result.database.databaseSize).toBe('N/A');
});
it('devrait calculer correctement la taille de la base de données', async () => {
const mockPackageJson = {
name: 'towercontrol',
version: '1.0.0',
};
vi.mocked(readFile).mockResolvedValue(JSON.stringify(mockPackageJson));
vi.mocked(prisma.task.count).mockResolvedValue(0);
vi.mocked(prisma.userPreferences.count).mockResolvedValue(0);
vi.mocked(prisma.tag.count).mockResolvedValue(0);
vi.mocked(prisma.dailyCheckbox.count).mockResolvedValue(0);
const { backupService } = await import(
'@/services/data-management/backup'
);
vi.mocked(backupService.listBackups).mockResolvedValue([]);
// Test différentes tailles
vi.mocked(stat).mockResolvedValue({
size: 1024, // 1 KB
} as any);
const result = await SystemInfoService.getSystemInfo();
expect(result.database.databaseSize).toContain('KB');
});
it('devrait utiliser DATABASE_URL pour trouver la base de données', async () => {
vi.stubEnv('DATABASE_URL', 'file:/path/to/db.db');
const mockPackageJson = {
name: 'towercontrol',
version: '1.0.0',
};
vi.mocked(readFile).mockResolvedValue(JSON.stringify(mockPackageJson));
vi.mocked(prisma.task.count).mockResolvedValue(0);
vi.mocked(prisma.userPreferences.count).mockResolvedValue(0);
vi.mocked(prisma.tag.count).mockResolvedValue(0);
vi.mocked(prisma.dailyCheckbox.count).mockResolvedValue(0);
const { backupService } = await import(
'@/services/data-management/backup'
);
vi.mocked(backupService.listBackups).mockResolvedValue([]);
vi.mocked(stat).mockResolvedValue({
size: 1024,
} as any);
await SystemInfoService.getSystemInfo();
expect(stat).toHaveBeenCalled();
});
it('devrait utiliser BACKUP_DATABASE_PATH si défini', async () => {
vi.stubEnv('BACKUP_DATABASE_PATH', '/custom/path/db.db');
const mockPackageJson = {
name: 'towercontrol',
version: '1.0.0',
};
vi.mocked(readFile).mockResolvedValue(JSON.stringify(mockPackageJson));
vi.mocked(prisma.task.count).mockResolvedValue(0);
vi.mocked(prisma.userPreferences.count).mockResolvedValue(0);
vi.mocked(prisma.tag.count).mockResolvedValue(0);
vi.mocked(prisma.dailyCheckbox.count).mockResolvedValue(0);
const { backupService } = await import(
'@/services/data-management/backup'
);
vi.mocked(backupService.listBackups).mockResolvedValue([]);
vi.mocked(stat).mockResolvedValue({
size: 1024,
} as any);
await SystemInfoService.getSystemInfo();
expect(stat).toHaveBeenCalled();
});
it('devrait gérer les erreurs de base de données', async () => {
const mockPackageJson = {
name: 'towercontrol',
version: '1.0.0',
};
vi.mocked(readFile).mockResolvedValue(JSON.stringify(mockPackageJson));
vi.mocked(prisma.task.count).mockRejectedValue(
new Error('Database error')
);
const { backupService } = await import(
'@/services/data-management/backup'
);
vi.mocked(backupService.listBackups).mockResolvedValue([]);
const result = await SystemInfoService.getSystemInfo();
expect(result.database.totalTasks).toBe(0);
expect(result.database.totalUsers).toBe(0);
});
it("devrait calculer correctement l'uptime", async () => {
const mockPackageJson = {
name: 'towercontrol',
version: '1.0.0',
};
vi.mocked(readFile).mockResolvedValue(JSON.stringify(mockPackageJson));
vi.mocked(prisma.task.count).mockResolvedValue(0);
vi.mocked(prisma.userPreferences.count).mockResolvedValue(0);
vi.mocked(prisma.tag.count).mockResolvedValue(0);
vi.mocked(prisma.dailyCheckbox.count).mockResolvedValue(0);
const { backupService } = await import(
'@/services/data-management/backup'
);
vi.mocked(backupService.listBackups).mockResolvedValue([]);
vi.mocked(stat).mockResolvedValue({
size: 1024,
} as any);
const result = await SystemInfoService.getSystemInfo();
expect(result.uptime).toBeDefined();
expect(result.uptime.length).toBeGreaterThan(0);
});
});
});

View File

@@ -0,0 +1,836 @@
/**
* Tests unitaires pour UserPreferencesService
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { userPreferencesService } from '../user-preferences';
import { prisma } from '../database';
import { getConfig } from '@/lib/config';
import {
KanbanFilters,
ViewPreferences,
ColumnVisibility,
JiraConfig,
} from '@/lib/types';
import { TfsConfig } from '@/services/integrations/tfs/tfs';
// Mock de prisma
vi.mock('../database', () => ({
prisma: {
user: {
upsert: vi.fn(),
},
userPreferences: {
upsert: vi.fn(),
update: vi.fn(),
findUnique: vi.fn(),
},
$executeRaw: vi.fn(),
$queryRaw: vi.fn(),
},
}));
// Mock de getConfig
vi.mock('@/lib/config', () => ({
getConfig: vi.fn(),
}));
describe('UserPreferencesService', () => {
const userId = 'test-user-id';
beforeEach(() => {
vi.clearAllMocks();
});
describe('Kanban Filters', () => {
it('devrait sauvegarder les filtres Kanban', async () => {
const filters: KanbanFilters = {
search: 'test',
tags: ['important'],
priorities: ['high'],
showCompleted: false,
sortBy: 'priority',
showWithDueDate: true,
};
vi.mocked(prisma.user.upsert).mockResolvedValue({
id: userId,
email: `${userId}@towercontrol.local`,
name: 'Default User',
role: 'user',
password: 'default',
createdAt: new Date(),
updatedAt: new Date(),
} as any);
vi.mocked(prisma.userPreferences.upsert).mockResolvedValue({
userId,
kanbanFilters: filters,
viewPreferences: {},
columnVisibility: {},
jiraConfig: {},
} as any);
vi.mocked(prisma.userPreferences.update).mockResolvedValue({
userId,
kanbanFilters: filters,
} as any);
vi.mocked(prisma.$executeRaw).mockResolvedValue(0);
await userPreferencesService.saveKanbanFilters(userId, filters);
expect(prisma.userPreferences.update).toHaveBeenCalledWith({
where: { userId },
data: { kanbanFilters: filters },
});
});
it('devrait récupérer les filtres Kanban avec valeurs par défaut', async () => {
const filters: KanbanFilters = {
search: 'test',
tags: ['important'],
priorities: [],
showCompleted: true,
sortBy: '',
showWithDueDate: false,
};
vi.mocked(prisma.user.upsert).mockResolvedValue({
id: userId,
email: `${userId}@towercontrol.local`,
name: 'Default User',
role: 'user',
password: 'default',
createdAt: new Date(),
updatedAt: new Date(),
} as any);
vi.mocked(prisma.userPreferences.upsert).mockResolvedValue({
userId,
kanbanFilters: filters,
viewPreferences: {},
columnVisibility: {},
jiraConfig: {},
} as any);
vi.mocked(prisma.$executeRaw).mockResolvedValue(0);
const result = await userPreferencesService.getKanbanFilters(userId);
expect(result).toHaveProperty('search');
expect(result).toHaveProperty('tags');
expect(result).toHaveProperty('priorities');
expect(result).toHaveProperty('showCompleted');
});
it("devrait retourner les valeurs par défaut en cas d'erreur", async () => {
vi.mocked(prisma.user.upsert).mockRejectedValue(
new Error('Database error')
);
const result = await userPreferencesService.getKanbanFilters(userId);
expect(result).toHaveProperty('search');
expect(result.search).toBe('');
});
});
describe('View Preferences', () => {
it('devrait sauvegarder les préférences de vue', async () => {
const preferences: ViewPreferences = {
compactView: true,
swimlanesByTags: true,
swimlanesMode: 'tags',
showObjectives: false,
showFilters: true,
objectivesCollapsed: false,
theme: 'light',
fontSize: 'large',
backgroundImage: undefined,
backgroundBlur: 0,
backgroundOpacity: 100,
};
vi.mocked(prisma.user.upsert).mockResolvedValue({
id: userId,
email: `${userId}@towercontrol.local`,
name: 'Default User',
role: 'user',
password: 'default',
createdAt: new Date(),
updatedAt: new Date(),
} as any);
vi.mocked(prisma.userPreferences.upsert).mockResolvedValue({
userId,
kanbanFilters: {},
viewPreferences: preferences,
columnVisibility: {},
jiraConfig: {},
} as any);
vi.mocked(prisma.userPreferences.update).mockResolvedValue({
userId,
viewPreferences: preferences,
} as any);
vi.mocked(prisma.$executeRaw).mockResolvedValue(0);
await userPreferencesService.saveViewPreferences(userId, preferences);
expect(prisma.userPreferences.update).toHaveBeenCalledWith({
where: { userId },
data: { viewPreferences: preferences },
});
});
it('devrait récupérer le thème', async () => {
vi.mocked(prisma.user.upsert).mockResolvedValue({
id: userId,
email: `${userId}@towercontrol.local`,
name: 'Default User',
role: 'user',
password: 'default',
createdAt: new Date(),
updatedAt: new Date(),
} as any);
vi.mocked(prisma.userPreferences.upsert).mockResolvedValue({
userId,
kanbanFilters: {},
viewPreferences: { theme: 'light' },
columnVisibility: {},
jiraConfig: {},
} as any);
vi.mocked(prisma.$executeRaw).mockResolvedValue(0);
const theme = await userPreferencesService.getTheme(userId);
expect(theme).toBe('light');
});
});
describe('Column Visibility', () => {
it('devrait sauvegarder la visibilité des colonnes', async () => {
const visibility: ColumnVisibility = {
hiddenStatuses: ['archived', 'cancelled'],
};
vi.mocked(prisma.user.upsert).mockResolvedValue({
id: userId,
email: `${userId}@towercontrol.local`,
name: 'Default User',
role: 'user',
password: 'default',
createdAt: new Date(),
updatedAt: new Date(),
} as any);
vi.mocked(prisma.userPreferences.upsert).mockResolvedValue({
userId,
kanbanFilters: {},
viewPreferences: {},
columnVisibility: visibility,
jiraConfig: {},
} as any);
vi.mocked(prisma.userPreferences.update).mockResolvedValue({
userId,
columnVisibility: visibility,
} as any);
vi.mocked(prisma.$executeRaw).mockResolvedValue(0);
await userPreferencesService.saveColumnVisibility(userId, visibility);
expect(prisma.userPreferences.update).toHaveBeenCalledWith({
where: { userId },
data: { columnVisibility: visibility },
});
});
it("devrait toggle la visibilité d'une colonne", async () => {
vi.mocked(prisma.user.upsert).mockResolvedValue({
id: userId,
email: `${userId}@towercontrol.local`,
name: 'Default User',
role: 'user',
password: 'default',
createdAt: new Date(),
updatedAt: new Date(),
} as any);
vi.mocked(prisma.userPreferences.upsert).mockResolvedValue({
userId,
kanbanFilters: {},
viewPreferences: {},
columnVisibility: { hiddenStatuses: [] },
jiraConfig: {},
} as any);
vi.mocked(prisma.userPreferences.update).mockResolvedValue({
userId,
columnVisibility: { hiddenStatuses: ['archived'] },
} as any);
vi.mocked(prisma.$executeRaw).mockResolvedValue(0);
await userPreferencesService.toggleColumnVisibility(userId, 'archived');
expect(prisma.userPreferences.update).toHaveBeenCalled();
});
});
describe('Jira Config', () => {
it('devrait sauvegarder la configuration Jira', async () => {
const config: JiraConfig = {
enabled: true,
baseUrl: 'https://jira.example.com',
email: 'user@example.com',
apiToken: 'token123',
ignoredProjects: [],
};
vi.mocked(prisma.user.upsert).mockResolvedValue({
id: userId,
email: `${userId}@towercontrol.local`,
name: 'Default User',
role: 'user',
password: 'default',
createdAt: new Date(),
updatedAt: new Date(),
} as any);
vi.mocked(prisma.userPreferences.upsert).mockResolvedValue({
userId,
kanbanFilters: {},
viewPreferences: {},
columnVisibility: {},
jiraConfig: config,
} as any);
vi.mocked(prisma.userPreferences.update).mockResolvedValue({
userId,
jiraConfig: config,
} as any);
vi.mocked(prisma.$executeRaw).mockResolvedValue(0);
await userPreferencesService.saveJiraConfig(userId, config);
expect(prisma.userPreferences.update).toHaveBeenCalledWith({
where: { userId },
data: { jiraConfig: config },
});
});
it('devrait récupérer la configuration Jira depuis la DB', async () => {
const config: JiraConfig = {
enabled: true,
baseUrl: 'https://jira.example.com',
email: 'user@example.com',
apiToken: 'token123',
ignoredProjects: [],
};
vi.mocked(prisma.user.upsert).mockResolvedValue({
id: userId,
email: `${userId}@towercontrol.local`,
name: 'Default User',
role: 'user',
password: 'default',
createdAt: new Date(),
updatedAt: new Date(),
} as any);
vi.mocked(prisma.userPreferences.upsert).mockResolvedValue({
userId,
kanbanFilters: {},
viewPreferences: {},
columnVisibility: {},
jiraConfig: config,
} as any);
vi.mocked(prisma.$executeRaw).mockResolvedValue(0);
const result = await userPreferencesService.getJiraConfig(userId);
expect(result.baseUrl).toBe('https://jira.example.com');
expect(result.email).toBe('user@example.com');
});
it("devrait utiliser les variables d'environnement si pas de config DB", async () => {
vi.mocked(prisma.user.upsert).mockResolvedValue({
id: userId,
email: `${userId}@towercontrol.local`,
name: 'Default User',
role: 'user',
password: 'default',
createdAt: new Date(),
updatedAt: new Date(),
} as any);
vi.mocked(prisma.userPreferences.upsert).mockResolvedValue({
userId,
kanbanFilters: {},
viewPreferences: {},
columnVisibility: {},
jiraConfig: null,
} as any);
vi.mocked(prisma.$executeRaw).mockResolvedValue(0);
vi.mocked(getConfig).mockReturnValue({
integrations: {
jira: {
baseUrl: 'https://env-jira.example.com',
email: 'env@example.com',
enabled: true,
},
},
} as any);
const result = await userPreferencesService.getJiraConfig(userId);
expect(result.baseUrl).toBe('https://env-jira.example.com');
expect(result.email).toBe('env@example.com');
});
});
describe('TFS Config', () => {
it('devrait sauvegarder la configuration TFS', async () => {
const config: TfsConfig = {
enabled: true,
organizationUrl: 'https://dev.azure.com/org',
projectName: 'MyProject',
personalAccessToken: 'token123',
repositories: ['repo1', 'repo2'],
ignoredRepositories: [],
maxSyncPeriod: '90d',
};
vi.mocked(prisma.user.upsert).mockResolvedValue({
id: userId,
email: `${userId}@towercontrol.local`,
name: 'Default User',
role: 'user',
password: 'default',
createdAt: new Date(),
updatedAt: new Date(),
} as any);
vi.mocked(prisma.userPreferences.upsert).mockResolvedValue({
userId,
kanbanFilters: {},
viewPreferences: {},
columnVisibility: {},
jiraConfig: {},
tfsConfig: config,
} as any);
vi.mocked(prisma.userPreferences.update).mockResolvedValue({
userId,
tfsConfig: config,
} as any);
vi.mocked(prisma.$executeRaw).mockResolvedValue(0);
await userPreferencesService.saveTfsConfig(userId, config);
expect(prisma.userPreferences.update).toHaveBeenCalledWith({
where: { userId },
data: { tfsConfig: config },
});
});
it('devrait récupérer la configuration TFS', async () => {
const config: TfsConfig = {
enabled: true,
organizationUrl: 'https://dev.azure.com/org',
projectName: 'MyProject',
personalAccessToken: 'token123',
repositories: [],
ignoredRepositories: [],
maxSyncPeriod: '90d',
};
vi.mocked(prisma.user.upsert).mockResolvedValue({
id: userId,
email: `${userId}@towercontrol.local`,
name: 'Default User',
role: 'user',
password: 'default',
createdAt: new Date(),
updatedAt: new Date(),
} as any);
vi.mocked(prisma.userPreferences.upsert).mockResolvedValue({
userId,
kanbanFilters: {},
viewPreferences: {},
columnVisibility: {},
jiraConfig: {},
tfsConfig: config,
} as any);
vi.mocked(prisma.$executeRaw).mockResolvedValue(0);
const result = await userPreferencesService.getTfsConfig(userId);
expect(result.organizationUrl).toBe('https://dev.azure.com/org');
expect(result.projectName).toBe('MyProject');
});
});
describe('Scheduler Config', () => {
it('devrait sauvegarder la configuration scheduler Jira', async () => {
vi.mocked(prisma.user.upsert).mockResolvedValue({
id: userId,
email: `${userId}@towercontrol.local`,
name: 'Default User',
role: 'user',
password: 'default',
createdAt: new Date(),
updatedAt: new Date(),
} as any);
vi.mocked(prisma.userPreferences.upsert).mockResolvedValue({
userId,
kanbanFilters: {},
viewPreferences: {},
columnVisibility: {},
jiraConfig: {},
} as any);
vi.mocked(prisma.$executeRaw).mockResolvedValue(0);
await userPreferencesService.saveJiraSchedulerConfig(
userId,
true,
'hourly'
);
expect(prisma.$executeRaw).toHaveBeenCalled();
});
it('devrait récupérer la configuration scheduler Jira', async () => {
vi.mocked(prisma.user.upsert).mockResolvedValue({
id: userId,
email: `${userId}@towercontrol.local`,
name: 'Default User',
role: 'user',
password: 'default',
createdAt: new Date(),
updatedAt: new Date(),
} as any);
vi.mocked(prisma.userPreferences.upsert).mockResolvedValue({
userId,
kanbanFilters: {},
viewPreferences: {},
columnVisibility: {},
jiraConfig: {},
} as any);
vi.mocked(prisma.$executeRaw).mockResolvedValue(0);
vi.mocked(prisma.$queryRaw).mockResolvedValue([
{ jiraAutoSync: 1, jiraSyncInterval: 'hourly' },
] as any);
const result =
await userPreferencesService.getJiraSchedulerConfig(userId);
expect(result.jiraAutoSync).toBe(true);
expect(result.jiraSyncInterval).toBe('hourly');
});
it('devrait sauvegarder la configuration scheduler TFS', async () => {
vi.mocked(prisma.user.upsert).mockResolvedValue({
id: userId,
email: `${userId}@towercontrol.local`,
name: 'Default User',
role: 'user',
password: 'default',
createdAt: new Date(),
updatedAt: new Date(),
} as any);
vi.mocked(prisma.userPreferences.upsert).mockResolvedValue({
userId,
kanbanFilters: {},
viewPreferences: {},
columnVisibility: {},
jiraConfig: {},
} as any);
vi.mocked(prisma.$executeRaw).mockResolvedValue(0);
await userPreferencesService.saveTfsSchedulerConfig(
userId,
true,
'daily'
);
expect(prisma.$executeRaw).toHaveBeenCalled();
});
it('devrait récupérer la configuration scheduler TFS', async () => {
vi.mocked(prisma.user.upsert).mockResolvedValue({
id: userId,
email: `${userId}@towercontrol.local`,
name: 'Default User',
role: 'user',
password: 'default',
createdAt: new Date(),
updatedAt: new Date(),
} as any);
vi.mocked(prisma.userPreferences.upsert).mockResolvedValue({
userId,
kanbanFilters: {},
viewPreferences: {},
columnVisibility: {},
jiraConfig: {},
} as any);
vi.mocked(prisma.$executeRaw).mockResolvedValue(0);
vi.mocked(prisma.$queryRaw).mockResolvedValue([
{ tfsAutoSync: 1, tfsSyncInterval: 'daily' },
] as any);
const result = await userPreferencesService.getTfsSchedulerConfig(userId);
expect(result.tfsAutoSync).toBe(true);
expect(result.tfsSyncInterval).toBe('daily');
});
});
describe('All Preferences', () => {
it('devrait récupérer toutes les préférences', async () => {
vi.mocked(prisma.user.upsert).mockResolvedValue({
id: userId,
email: `${userId}@towercontrol.local`,
name: 'Default User',
role: 'user',
password: 'default',
createdAt: new Date(),
updatedAt: new Date(),
} as any);
vi.mocked(prisma.userPreferences.upsert).mockResolvedValue({
userId,
kanbanFilters: {},
viewPreferences: {},
columnVisibility: {},
jiraConfig: {},
} as any);
vi.mocked(prisma.$executeRaw).mockResolvedValue(0);
vi.mocked(prisma.$queryRaw).mockResolvedValue([
{ jiraAutoSync: 0, jiraSyncInterval: 'daily' },
{ tfsAutoSync: 0, tfsSyncInterval: 'daily' },
] as any);
vi.mocked(getConfig).mockReturnValue({
integrations: {
jira: {
baseUrl: '',
email: '',
enabled: false,
},
},
} as any);
const result = await userPreferencesService.getAllPreferences(userId);
expect(result).toHaveProperty('kanbanFilters');
expect(result).toHaveProperty('viewPreferences');
expect(result).toHaveProperty('columnVisibility');
expect(result).toHaveProperty('jiraConfig');
expect(result).toHaveProperty('jiraAutoSync');
expect(result).toHaveProperty('jiraSyncInterval');
expect(result).toHaveProperty('tfsConfig');
expect(result).toHaveProperty('tfsAutoSync');
expect(result).toHaveProperty('tfsSyncInterval');
});
it('devrait sauvegarder toutes les préférences', async () => {
const preferences = {
kanbanFilters: {
search: '',
tags: [],
priorities: [],
showCompleted: true,
sortBy: '',
showWithDueDate: false,
},
viewPreferences: {
compactView: false,
swimlanesByTags: false,
swimlanesMode: 'tags' as const,
showObjectives: true,
showFilters: true,
objectivesCollapsed: false,
theme: 'dark' as const,
fontSize: 'medium' as const,
backgroundImage: undefined,
backgroundBlur: 0,
backgroundOpacity: 100,
},
columnVisibility: {
hiddenStatuses: [],
},
jiraConfig: {
enabled: false,
baseUrl: '',
email: '',
apiToken: '',
ignoredProjects: [],
},
jiraAutoSync: false,
jiraSyncInterval: 'daily' as const,
tfsConfig: {
enabled: false,
organizationUrl: '',
projectName: '',
personalAccessToken: '',
repositories: [],
ignoredRepositories: [],
maxSyncPeriod: '90d' as const,
},
tfsAutoSync: false,
tfsSyncInterval: 'daily' as const,
};
vi.mocked(prisma.user.upsert).mockResolvedValue({
id: userId,
email: `${userId}@towercontrol.local`,
name: 'Default User',
role: 'user',
password: 'default',
createdAt: new Date(),
updatedAt: new Date(),
} as any);
vi.mocked(prisma.userPreferences.upsert).mockResolvedValue({
userId,
kanbanFilters: {},
viewPreferences: {},
columnVisibility: {},
jiraConfig: {},
} as any);
vi.mocked(prisma.userPreferences.update).mockResolvedValue({
userId,
} as any);
vi.mocked(prisma.$executeRaw).mockResolvedValue(0);
await userPreferencesService.saveAllPreferences(userId, preferences);
expect(prisma.userPreferences.update).toHaveBeenCalled();
expect(prisma.$executeRaw).toHaveBeenCalled();
});
it('devrait remettre à zéro toutes les préférences', async () => {
vi.mocked(prisma.user.upsert).mockResolvedValue({
id: userId,
email: `${userId}@towercontrol.local`,
name: 'Default User',
role: 'user',
password: 'default',
createdAt: new Date(),
updatedAt: new Date(),
} as any);
vi.mocked(prisma.userPreferences.upsert).mockResolvedValue({
userId,
kanbanFilters: {},
viewPreferences: {},
columnVisibility: {},
jiraConfig: {},
} as any);
vi.mocked(prisma.userPreferences.update).mockResolvedValue({
userId,
} as any);
vi.mocked(prisma.$executeRaw).mockResolvedValue(0);
await userPreferencesService.resetAllPreferences(userId);
expect(prisma.userPreferences.update).toHaveBeenCalled();
});
});
describe('Update methods', () => {
it('devrait mettre à jour partiellement les filtres Kanban', async () => {
vi.mocked(prisma.user.upsert).mockResolvedValue({
id: userId,
email: `${userId}@towercontrol.local`,
name: 'Default User',
role: 'user',
password: 'default',
createdAt: new Date(),
updatedAt: new Date(),
} as any);
vi.mocked(prisma.userPreferences.upsert).mockResolvedValue({
userId,
kanbanFilters: { search: '', tags: [] },
viewPreferences: {},
columnVisibility: {},
jiraConfig: {},
} as any);
vi.mocked(prisma.userPreferences.update).mockResolvedValue({
userId,
kanbanFilters: { search: 'new', tags: [] },
} as any);
vi.mocked(prisma.$executeRaw).mockResolvedValue(0);
await userPreferencesService.updateKanbanFilters(userId, {
search: 'new',
});
expect(prisma.userPreferences.update).toHaveBeenCalled();
});
it('devrait mettre à jour partiellement les préférences de vue', async () => {
vi.mocked(prisma.user.upsert).mockResolvedValue({
id: userId,
email: `${userId}@towercontrol.local`,
name: 'Default User',
role: 'user',
password: 'default',
createdAt: new Date(),
updatedAt: new Date(),
} as any);
vi.mocked(prisma.userPreferences.upsert).mockResolvedValue({
userId,
kanbanFilters: {},
viewPreferences: { theme: 'dark' },
columnVisibility: {},
jiraConfig: {},
} as any);
vi.mocked(prisma.userPreferences.update).mockResolvedValue({
userId,
viewPreferences: { theme: 'light' },
} as any);
vi.mocked(prisma.$executeRaw).mockResolvedValue(0);
await userPreferencesService.updateViewPreferences(userId, {
theme: 'light',
});
expect(prisma.userPreferences.update).toHaveBeenCalled();
});
});
});