test(service): CardService et schémas de validation

This commit is contained in:
devcodetools committed 2026-09-21 17:59:25 +02:00
1 parent 12670b7134
commit e4aa5f4b3b
2 files changed
+154

No files matched your search

+39
View File
@@ -0,0 +1,39 @@
// @vitest-environment node
import { describe, expect, it } from 'vitest'
import { createCardSchema, updateCardSchema } from './card-schemas'
describe('createCardSchema', () => {
it('accepte une entrée valide et normalise les champs', () => {
const result = createCardSchema.parse({
question: ' Question ? ',
response: 'Réponse',
theme: ' javascript ',
tags: ['scope'],
})
expect(result.question).toBe('Question ?')
expect(result.theme).toBe('javascript')
})
it('accepte une entrée sans tags', () => {
expect(createCardSchema.parse({ question: 'Q', response: 'R', theme: 'git' }).tags).toBeUndefined()
})
it('rejette une question vide', () => {
expect(() => createCardSchema.parse({ question: ' ', response: 'R', theme: 'git' })).toThrow()
})
it('rejette un thème manquant', () => {
expect(() => createCardSchema.parse({ question: 'Q', response: 'R' })).toThrow()
})
})
describe('updateCardSchema', () => {
it('accepte une mise à jour partielle', () => {
const result = updateCardSchema.parse({ response: 'Nouvelle réponse' })
expect(result).toEqual({ response: 'Nouvelle réponse' })
})
it('rejette un champ vide lors d une mise à jour', () => {
expect(() => updateCardSchema.parse({ theme: '' })).toThrow()
})
})
+115
View File
@@ -0,0 +1,115 @@
// @vitest-environment node
import { describe, expect, it, vi } from 'vitest'
import type { Card } from '@/domain/card'
import type { CardRepository } from '@/domain/card-repository'
import { NotFoundError } from '@/domain/errors'
import { CardService } from './card-service'
const CARD: Card = {
id: '10000000-0000-4000-8000-000000000001',
question: 'Qu est-ce qu une closure ?',
response: 'Une fonction qui conserve la portée de sa définition.',
theme: 'javascript',
tags: ['scope'],
createdAt: new Date('2026-01-01T08:00:00Z'),
updatedAt: new Date('2026-01-01T08:00:00Z'),
}
type RepoMock = Record<keyof CardRepository, ReturnType<typeof vi.fn>>
function setup() {
const repository: RepoMock = {
findAll: vi.fn(),
findById: vi.fn(),
create: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
findRandom: vi.fn(),
findRandomByTheme: vi.fn(),
findThemes: vi.fn(),
}
const service = new CardService(repository as unknown as CardRepository)
return { service, repository }
}
describe('CardService', () => {
it('list délègue au repository et renvoie toutes les cartes', async () => {
const { service, repository } = setup()
repository.findAll.mockResolvedValueOnce([CARD])
await expect(service.list()).resolves.toEqual([CARD])
})
it('get renvoie la carte quand elle existe', async () => {
const { service, repository } = setup()
repository.findById.mockResolvedValueOnce(CARD)
await expect(service.get(CARD.id)).resolves.toEqual(CARD)
})
it('get lève NotFoundError si la carte est inconnue', async () => {
const { service, repository } = setup()
repository.findById.mockResolvedValueOnce(null)
await expect(service.get('inconnu')).rejects.toThrow(NotFoundError)
})
it('create délègue l entrée au repository', async () => {
const { service, repository } = setup()
const input = { question: 'Q ?', response: 'R', theme: 'git' }
repository.create.mockResolvedValueOnce(CARD)
await expect(service.create(input)).resolves.toEqual(CARD)
expect(repository.create).toHaveBeenCalledWith(input)
})
it('update renvoie la carte mise à jour', async () => {
const { service, repository } = setup()
repository.update.mockResolvedValueOnce({ ...CARD, question: 'Nouvelle' })
const card = await service.update(CARD.id, { question: 'Nouvelle' })
expect(card.question).toBe('Nouvelle')
})
it('update lève NotFoundError si la carte est inconnue', async () => {
const { service, repository } = setup()
repository.update.mockResolvedValueOnce(null)
await expect(service.update('inconnu', { question: 'X' })).rejects.toThrow(NotFoundError)
})
it('remove délègue la suppression', async () => {
const { service, repository } = setup()
repository.delete.mockResolvedValueOnce(true)
await expect(service.remove(CARD.id)).resolves.toBeUndefined()
expect(repository.delete).toHaveBeenCalledWith(CARD.id)
})
it('remove lève NotFoundError si aucune carte ne correspond', async () => {
const { service, repository } = setup()
repository.delete.mockResolvedValueOnce(false)
await expect(service.remove('inconnu')).rejects.toThrow(NotFoundError)
})
it('random sans thème utilise findRandom', async () => {
const { service, repository } = setup()
repository.findRandom.mockResolvedValueOnce(CARD)
await expect(service.random()).resolves.toEqual(CARD)
expect(repository.findRandomByTheme).not.toHaveBeenCalled()
})
it('random avec thème utilise findRandomByTheme', async () => {
const { service, repository } = setup()
repository.findRandomByTheme.mockResolvedValueOnce(CARD)
await expect(service.random('javascript')).resolves.toEqual(CARD)
expect(repository.findRandomByTheme).toHaveBeenCalledWith('javascript')
})
it('random lève NotFoundError si aucune carte', async () => {
const { service, repository } = setup()
repository.findRandom.mockResolvedValueOnce(null)
await expect(service.random()).rejects.toThrow(NotFoundError)
repository.findRandomByTheme.mockResolvedValueOnce(null)
await expect(service.random('react')).rejects.toThrow('Aucune carte pour le thème react')
})
it('themes renvoie la liste des thèmes', async () => {
const { service, repository } = setup()
repository.findThemes.mockResolvedValueOnce(['docker', 'git'])
await expect(service.themes()).resolves.toEqual(['docker', 'git'])
})
})