test(service): CardService et schémas de validation
This commit is contained in:
1 parent
12670b7134
commit
e4aa5f4b3b
2 files changed
+154
No files matched your search
@@ -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()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -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'])
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in new issue
Block a user