test(JiraSync): improve test coverage for synchronization scenarios and enhance assertions for change detection

This commit is contained in:
Julien Froidefond
2025-11-26 08:40:48 +01:00
parent 4fc41a5b2c
commit f57ea205c7
2 changed files with 621 additions and 0 deletions

View File

@@ -0,0 +1,345 @@
/**
* Tests unitaires pour NotesService
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { NotesService } from '../notes';
import { prisma } from '@/services/core/database';
import { tagsService } from '../task-management/tags';
// Mock de prisma
vi.mock('@/services/core/database', () => ({
prisma: {
note: {
findMany: vi.fn(),
findFirst: vi.fn(),
findUnique: vi.fn(),
create: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
count: vi.fn(),
},
noteTag: {
createMany: vi.fn(),
deleteMany: vi.fn(),
findMany: vi.fn(),
},
},
}));
// Mock de tagsService
vi.mock('../task-management/tags', () => ({
tagsService: {
ensureTagsExist: vi.fn(),
},
}));
describe('NotesService', () => {
let service: NotesService;
const mockUserId = 'user-123';
beforeEach(() => {
vi.clearAllMocks();
service = new NotesService();
});
describe('getNotes', () => {
it('devrait récupérer toutes les notes pour un utilisateur', async () => {
const mockNotes = [
{
id: 'note-1',
title: 'Note 1',
content: 'Content 1',
userId: mockUserId,
taskId: null,
createdAt: new Date(),
updatedAt: new Date(),
noteTags: [],
task: null,
},
];
vi.mocked(prisma.note.findMany).mockResolvedValue(mockNotes as any);
const notes = await service.getNotes(mockUserId);
expect(notes).toHaveLength(1);
expect(prisma.note.findMany).toHaveBeenCalledWith({
where: { userId: mockUserId },
include: {
noteTags: {
include: {
tag: true,
},
},
task: {
include: {
taskTags: {
include: {
tag: true,
},
},
primaryTag: true,
},
},
},
orderBy: { updatedAt: 'desc' },
});
});
});
describe('getNoteById', () => {
it('devrait récupérer une note par son ID', async () => {
const mockNote = {
id: 'note-1',
title: 'Note 1',
content: 'Content 1',
userId: mockUserId,
taskId: null,
createdAt: new Date(),
updatedAt: new Date(),
noteTags: [],
task: null,
};
vi.mocked(prisma.note.findFirst).mockResolvedValue(mockNote as any);
const note = await service.getNoteById('note-1', mockUserId);
expect(note).toBeDefined();
expect(note?.id).toBe('note-1');
expect(prisma.note.findFirst).toHaveBeenCalledWith({
where: {
id: 'note-1',
userId: mockUserId,
},
include: {
noteTags: {
include: {
tag: true,
},
},
task: {
include: {
taskTags: {
include: {
tag: true,
},
},
primaryTag: true,
},
},
},
});
});
it("devrait retourner null si la note n'existe pas", async () => {
vi.mocked(prisma.note.findFirst).mockResolvedValue(null);
const note = await service.getNoteById('non-existent', mockUserId);
expect(note).toBeNull();
});
});
describe('createNote', () => {
it('devrait créer une nouvelle note', async () => {
const mockNote = {
id: 'note-1',
title: 'New Note',
content: 'Content',
userId: mockUserId,
taskId: null,
createdAt: new Date(),
updatedAt: new Date(),
noteTags: [],
task: null,
};
vi.mocked(prisma.note.create).mockResolvedValue(mockNote as any);
vi.mocked(prisma.note.findUnique).mockResolvedValue(mockNote as any);
vi.mocked(tagsService.ensureTagsExist).mockResolvedValue([]);
const note = await service.createNote({
title: 'New Note',
content: 'Content',
userId: mockUserId,
});
expect(note).toBeDefined();
expect(prisma.note.create).toHaveBeenCalled();
});
it('devrait créer une note avec des tags', async () => {
const mockNote = {
id: 'note-1',
title: 'New Note',
content: 'Content',
userId: mockUserId,
taskId: null,
createdAt: new Date(),
updatedAt: new Date(),
noteTags: [],
task: null,
};
const mockTags = [
{ id: 'tag-1', name: 'Tag 1', color: '#ff0000', isPinned: false },
];
vi.mocked(prisma.note.create).mockResolvedValue(mockNote as any);
vi.mocked(prisma.note.findUnique).mockResolvedValue({
...mockNote,
noteTags: [{ tag: mockTags[0] }],
} as any);
vi.mocked(tagsService.ensureTagsExist).mockResolvedValue(mockTags as any);
vi.mocked(prisma.noteTag.createMany).mockResolvedValue({
count: 1,
} as any);
await service.createNote({
title: 'New Note',
content: 'Content',
userId: mockUserId,
tags: ['Tag 1'],
});
expect(tagsService.ensureTagsExist).toHaveBeenCalledWith(
['Tag 1'],
mockUserId
);
});
});
describe('updateNote', () => {
it('devrait mettre à jour une note', async () => {
const mockNote = {
id: 'note-1',
title: 'Updated Note',
content: 'Updated Content',
userId: mockUserId,
taskId: null,
createdAt: new Date(),
updatedAt: new Date(),
noteTags: [],
task: null,
};
vi.mocked(prisma.note.findFirst).mockResolvedValue({
id: 'note-1',
userId: mockUserId,
} as any);
vi.mocked(prisma.note.update).mockResolvedValue(mockNote as any);
vi.mocked(prisma.note.findUnique).mockResolvedValue({
...mockNote,
noteTags: [],
} as any);
vi.mocked(prisma.noteTag.deleteMany).mockResolvedValue({
count: 0,
} as any);
vi.mocked(tagsService.ensureTagsExist).mockResolvedValue([]);
const note = await service.updateNote('note-1', mockUserId, {
title: 'Updated Note',
content: 'Updated Content',
});
expect(note).toBeDefined();
expect(prisma.note.update).toHaveBeenCalled();
});
it("devrait lancer une erreur si la note n'existe pas", async () => {
vi.mocked(prisma.note.findFirst).mockResolvedValue(null);
await expect(
service.updateNote('non-existent', mockUserId, {
title: 'Updated',
})
).rejects.toThrow();
});
});
describe('deleteNote', () => {
it('devrait supprimer une note', async () => {
vi.mocked(prisma.note.findFirst).mockResolvedValue({
id: 'note-1',
userId: mockUserId,
} as any);
vi.mocked(prisma.note.delete).mockResolvedValue({} as any);
await service.deleteNote('note-1', mockUserId);
expect(prisma.note.delete).toHaveBeenCalledWith({
where: { id: 'note-1' },
});
});
it("devrait lancer une erreur si la note n'existe pas", async () => {
vi.mocked(prisma.note.findFirst).mockResolvedValue(null);
await expect(
service.deleteNote('non-existent', mockUserId)
).rejects.toThrow();
});
});
describe('searchNotes', () => {
it('devrait rechercher dans les notes', async () => {
const mockNotes = [
{
id: 'note-1',
title: 'Test Note',
content: 'Content',
userId: mockUserId,
taskId: null,
createdAt: new Date(),
updatedAt: new Date(),
noteTags: [],
task: null,
},
];
vi.mocked(prisma.note.findMany).mockResolvedValue(mockNotes as any);
const notes = await service.searchNotes(mockUserId, 'Test');
expect(notes).toHaveLength(1);
expect(prisma.note.findMany).toHaveBeenCalledWith({
where: {
userId: mockUserId,
OR: [
{ title: { contains: 'Test' } },
{ content: { contains: 'Test' } },
],
},
orderBy: { updatedAt: 'desc' },
});
});
});
describe('getNotesStats', () => {
it('devrait retourner les statistiques des notes', async () => {
const mockNotes = [
{
id: 'note-1',
content: 'This is a test note with words',
updatedAt: new Date(),
},
{
id: 'note-2',
content: 'Another note',
updatedAt: new Date(),
},
];
vi.mocked(prisma.note.findMany).mockResolvedValue(mockNotes as any);
const stats = await service.getNotesStats(mockUserId);
expect(stats.totalNotes).toBeGreaterThanOrEqual(0);
expect(stats.totalWords).toBeGreaterThanOrEqual(0);
expect(stats.lastUpdated).toBeDefined();
});
});
});

View File

@@ -0,0 +1,276 @@
/**
* Tests unitaires pour usersService
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { usersService } from '../users';
import { prisma } from '../core/database';
import bcrypt from 'bcryptjs';
// Mock de prisma
vi.mock('../core/database', () => ({
prisma: {
user: {
create: vi.fn(),
findUnique: vi.fn(),
update: vi.fn(),
},
},
}));
// Mock de bcrypt
vi.mock('bcryptjs', () => ({
default: {
hash: vi.fn(),
compare: vi.fn(),
},
}));
describe('usersService', () => {
beforeEach(() => {
vi.clearAllMocks();
});
describe('createUser', () => {
it('devrait créer un nouvel utilisateur', async () => {
const mockUser = {
id: 'user-1',
email: 'test@example.com',
name: 'Test User',
firstName: 'Test',
lastName: 'User',
avatar: null,
role: 'user',
isActive: true,
lastLoginAt: null,
createdAt: new Date(),
updatedAt: new Date(),
};
vi.mocked(bcrypt.hash).mockResolvedValue('hashedPassword' as any);
vi.mocked(prisma.user.create).mockResolvedValue(mockUser as any);
const user = await usersService.createUser({
email: 'test@example.com',
name: 'Test User',
firstName: 'Test',
lastName: 'User',
password: 'password123',
});
expect(user).toEqual(mockUser);
expect(bcrypt.hash).toHaveBeenCalledWith('password123', 12);
expect(prisma.user.create).toHaveBeenCalledWith({
data: {
email: 'test@example.com',
name: 'Test User',
firstName: 'Test',
lastName: 'User',
avatar: undefined,
role: 'user',
password: 'hashedPassword',
},
select: expect.any(Object),
});
});
it('devrait utiliser le rôle par défaut si non spécifié', async () => {
const mockUser = {
id: 'user-1',
email: 'test@example.com',
name: null,
firstName: null,
lastName: null,
avatar: null,
role: 'user',
isActive: true,
lastLoginAt: null,
createdAt: new Date(),
updatedAt: new Date(),
};
vi.mocked(bcrypt.hash).mockResolvedValue('hashedPassword' as any);
vi.mocked(prisma.user.create).mockResolvedValue(mockUser as any);
await usersService.createUser({
email: 'test@example.com',
password: 'password123',
});
expect(prisma.user.create).toHaveBeenCalledWith(
expect.objectContaining({
data: expect.objectContaining({
role: 'user',
}),
})
);
});
});
describe('getUserByEmail', () => {
it('devrait récupérer un utilisateur par email', async () => {
const mockUser = {
id: 'user-1',
email: 'test@example.com',
name: 'Test User',
firstName: 'Test',
lastName: 'User',
avatar: null,
role: 'user',
isActive: true,
lastLoginAt: null,
password: 'hashedPassword',
createdAt: new Date(),
updatedAt: new Date(),
};
vi.mocked(prisma.user.findUnique).mockResolvedValue(mockUser as any);
const user = await usersService.getUserByEmail('test@example.com');
expect(user).toEqual(mockUser);
expect(prisma.user.findUnique).toHaveBeenCalledWith({
where: { email: 'test@example.com' },
select: expect.any(Object),
});
});
it("devrait retourner null si l'utilisateur n'existe pas", async () => {
vi.mocked(prisma.user.findUnique).mockResolvedValue(null);
const user = await usersService.getUserByEmail('nonexistent@example.com');
expect(user).toBeNull();
});
});
describe('getUserById', () => {
it('devrait récupérer un utilisateur par ID', async () => {
const mockUser = {
id: 'user-1',
email: 'test@example.com',
name: 'Test User',
firstName: 'Test',
lastName: 'User',
avatar: null,
role: 'user',
isActive: true,
lastLoginAt: null,
createdAt: new Date(),
updatedAt: new Date(),
};
vi.mocked(prisma.user.findUnique).mockResolvedValue(mockUser as any);
const user = await usersService.getUserById('user-1');
expect(user).toEqual(mockUser);
expect(prisma.user.findUnique).toHaveBeenCalledWith({
where: { id: 'user-1' },
select: expect.any(Object),
});
});
});
describe('verifyPassword', () => {
it('devrait vérifier un mot de passe correct', async () => {
vi.mocked(bcrypt.compare).mockResolvedValue(true as any);
const isValid = await usersService.verifyPassword(
'password123',
'hashedPassword'
);
expect(isValid).toBe(true);
expect(bcrypt.compare).toHaveBeenCalledWith(
'password123',
'hashedPassword'
);
});
it('devrait retourner false pour un mot de passe incorrect', async () => {
vi.mocked(bcrypt.compare).mockResolvedValue(false as any);
const isValid = await usersService.verifyPassword(
'wrongPassword',
'hashedPassword'
);
expect(isValid).toBe(false);
});
});
describe('emailExists', () => {
it("devrait retourner true si l'email existe", async () => {
vi.mocked(prisma.user.findUnique).mockResolvedValue({
id: 'user-1',
email: 'test@example.com',
} as any);
const exists = await usersService.emailExists('test@example.com');
expect(exists).toBe(true);
});
it("devrait retourner false si l'email n'existe pas", async () => {
vi.mocked(prisma.user.findUnique).mockResolvedValue(null);
const exists = await usersService.emailExists('nonexistent@example.com');
expect(exists).toBe(false);
});
});
describe('updateLastLogin', () => {
it('devrait mettre à jour la date de dernière connexion', async () => {
vi.mocked(prisma.user.update).mockResolvedValue({} as any);
await usersService.updateLastLogin('user-1');
expect(prisma.user.update).toHaveBeenCalledWith({
where: { id: 'user-1' },
data: {
lastLoginAt: expect.any(Date),
},
});
});
});
describe('updateUser', () => {
it('devrait mettre à jour un utilisateur', async () => {
const mockUser = {
id: 'user-1',
email: 'test@example.com',
name: 'Updated Name',
firstName: 'Updated',
lastName: 'Name',
avatar: null,
role: 'user',
isActive: true,
lastLoginAt: null,
createdAt: new Date(),
updatedAt: new Date(),
};
vi.mocked(prisma.user.update).mockResolvedValue(mockUser as any);
const user = await usersService.updateUser('user-1', {
name: 'Updated Name',
firstName: 'Updated',
lastName: 'Name',
});
expect(user).toEqual(mockUser);
expect(prisma.user.update).toHaveBeenCalledWith({
where: { id: 'user-1' },
data: {
name: 'Updated Name',
firstName: 'Updated',
lastName: 'Name',
},
select: expect.any(Object),
});
});
});
});