232 lines
6.9 KiB
JavaScript
232 lines
6.9 KiB
JavaScript
|
|
import {describe, it, expect, vi, beforeEach} from 'vitest'
|
||
|
|
import hookFactory from '../index.js'
|
||
|
|
|
||
|
|
// Helpers pour construire les mocks Directus ---------------------------------
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Construit un mock de database (knex) chaînable.
|
||
|
|
* Chaque méthode retourne `this` sauf `.first()` qui résout la valeur fournie.
|
||
|
|
*/
|
||
|
|
const makeDatabase = (resolvedRow) => {
|
||
|
|
const db = vi.fn(() => db)
|
||
|
|
db.select = vi.fn(() => db)
|
||
|
|
db.where = vi.fn(() => db)
|
||
|
|
db.first = vi.fn(() => Promise.resolve(resolvedRow))
|
||
|
|
return db
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Construit un mock de VersionsService.
|
||
|
|
* `compareResult` est la valeur renvoyée par `compare()`.
|
||
|
|
*/
|
||
|
|
const makeVersionsService = (compareResult) => ({
|
||
|
|
VersionsService: class {
|
||
|
|
compare() {
|
||
|
|
return Promise.resolve(compareResult)
|
||
|
|
}
|
||
|
|
},
|
||
|
|
})
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Enregistre les callbacks du hook et les expose par événement.
|
||
|
|
* Permet d'appeler directement `callbacks['items.create'](input, meta, ctx)`.
|
||
|
|
*/
|
||
|
|
const mountHook = (services) => {
|
||
|
|
const callbacks = {}
|
||
|
|
const filter = (event, handler) => {
|
||
|
|
callbacks[event] = handler
|
||
|
|
}
|
||
|
|
|
||
|
|
hookFactory({filter}, {services})
|
||
|
|
return callbacks
|
||
|
|
}
|
||
|
|
|
||
|
|
// Dates de référence ---------------------------------------------------------
|
||
|
|
const RECENT_DATE = new Date(Date.now() - 1 * 24 * 60 * 60 * 1000).toISOString() // hier
|
||
|
|
const OLD_DATE = new Date(Date.now() - 4 * 24 * 60 * 60 * 1000).toISOString() // il y a 4 jours
|
||
|
|
|
||
|
|
const SCHEMA = {}
|
||
|
|
|
||
|
|
// Tests ----------------------------------------------------------------------
|
||
|
|
|
||
|
|
describe('disallow-votes — items.create', () => {
|
||
|
|
let callbacks
|
||
|
|
|
||
|
|
beforeEach(() => {
|
||
|
|
callbacks = mountHook(makeVersionsService({outdated: false}))
|
||
|
|
})
|
||
|
|
|
||
|
|
it('lève une erreur si versionId est absent', async () => {
|
||
|
|
const db = makeDatabase(null)
|
||
|
|
await expect(
|
||
|
|
callbacks['items.create']({}, {collection: 'votes'}, {database: db, schema: SCHEMA})
|
||
|
|
).rejects.toThrow('identifiant de la version est manquant')
|
||
|
|
})
|
||
|
|
|
||
|
|
it('lève une erreur si la version est introuvable', async () => {
|
||
|
|
const db = makeDatabase(undefined)
|
||
|
|
await expect(
|
||
|
|
callbacks['items.create'](
|
||
|
|
{content_version_id: 'abc'},
|
||
|
|
{collection: 'votes'},
|
||
|
|
{database: db, schema: SCHEMA},
|
||
|
|
)
|
||
|
|
).rejects.toThrow('Version non trouvée')
|
||
|
|
})
|
||
|
|
|
||
|
|
it('lève une erreur si la version a plus de 3 jours', async () => {
|
||
|
|
const db = makeDatabase({date_created: OLD_DATE})
|
||
|
|
await expect(
|
||
|
|
callbacks['items.create'](
|
||
|
|
{content_version_id: 'abc'},
|
||
|
|
{collection: 'votes'},
|
||
|
|
{database: db, schema: SCHEMA},
|
||
|
|
)
|
||
|
|
).rejects.toThrow('3 jours')
|
||
|
|
})
|
||
|
|
|
||
|
|
it('lève une erreur si la version est obsolète', async () => {
|
||
|
|
const db = makeDatabase({date_created: RECENT_DATE})
|
||
|
|
callbacks = mountHook(makeVersionsService({outdated: true}))
|
||
|
|
await expect(
|
||
|
|
callbacks['items.create'](
|
||
|
|
{content_version_id: 'abc'},
|
||
|
|
{collection: 'votes'},
|
||
|
|
{database: db, schema: SCHEMA},
|
||
|
|
)
|
||
|
|
).rejects.toThrow('version obsolète')
|
||
|
|
})
|
||
|
|
|
||
|
|
it('autorise le vote si la version est récente et valide', async () => {
|
||
|
|
const db = makeDatabase({date_created: RECENT_DATE})
|
||
|
|
const input = {content_version_id: 'abc'}
|
||
|
|
const result = await callbacks['items.create'](
|
||
|
|
input,
|
||
|
|
{collection: 'votes'},
|
||
|
|
{database: db, schema: SCHEMA},
|
||
|
|
)
|
||
|
|
|
||
|
|
expect(result).toBe(input)
|
||
|
|
})
|
||
|
|
|
||
|
|
it('autorise le vote si compare() échoue (non bloquant)', async () => {
|
||
|
|
const db = makeDatabase({date_created: RECENT_DATE})
|
||
|
|
const servicesWithBrokenCompare = {
|
||
|
|
VersionsService: class {
|
||
|
|
compare() {
|
||
|
|
return Promise.reject(new Error('service indisponible'))
|
||
|
|
}
|
||
|
|
},
|
||
|
|
}
|
||
|
|
callbacks = mountHook(servicesWithBrokenCompare)
|
||
|
|
const input = {content_version_id: 'abc'}
|
||
|
|
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
||
|
|
|
||
|
|
const result = await callbacks['items.create'](
|
||
|
|
input,
|
||
|
|
{collection: 'votes'},
|
||
|
|
{database: db, schema: SCHEMA},
|
||
|
|
)
|
||
|
|
|
||
|
|
expect(result).toBe(input)
|
||
|
|
expect(warnSpy).toHaveBeenCalled()
|
||
|
|
warnSpy.mockRestore()
|
||
|
|
})
|
||
|
|
|
||
|
|
it('ignore les collections qui ne sont pas "votes"', async () => {
|
||
|
|
const db = makeDatabase(null)
|
||
|
|
const input = {content_version_id: 'abc'}
|
||
|
|
const result = await callbacks['items.create'](
|
||
|
|
input,
|
||
|
|
{collection: 'articles'},
|
||
|
|
{database: db, schema: SCHEMA},
|
||
|
|
)
|
||
|
|
|
||
|
|
expect(result).toBe(input)
|
||
|
|
expect(db).not.toHaveBeenCalled()
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
describe('disallow-votes — items.update', () => {
|
||
|
|
it('applique la même logique que items.create', async () => {
|
||
|
|
const db = makeDatabase({date_created: OLD_DATE})
|
||
|
|
const callbacks = mountHook(makeVersionsService({outdated: false}))
|
||
|
|
|
||
|
|
await expect(
|
||
|
|
callbacks['items.update'](
|
||
|
|
{content_version_id: 'abc'},
|
||
|
|
{collection: 'votes'},
|
||
|
|
{database: db, schema: SCHEMA},
|
||
|
|
)
|
||
|
|
).rejects.toThrow('3 jours')
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
describe('disallow-votes — items.delete', () => {
|
||
|
|
it('lève une erreur si voteId est absent', async () => {
|
||
|
|
const db = makeDatabase(null)
|
||
|
|
const callbacks = mountHook(makeVersionsService({outdated: false}))
|
||
|
|
|
||
|
|
await expect(
|
||
|
|
callbacks['items.delete']([], {collection: 'votes'}, {database: db, schema: SCHEMA})
|
||
|
|
).rejects.toThrow('identifiant du vote est manquant')
|
||
|
|
})
|
||
|
|
|
||
|
|
it('lève une erreur si le vote est introuvable', async () => {
|
||
|
|
const db = makeDatabase(undefined)
|
||
|
|
const callbacks = mountHook(makeVersionsService({outdated: false}))
|
||
|
|
|
||
|
|
await expect(
|
||
|
|
callbacks['items.delete'](['vote-1'], {collection: 'votes'}, {database: db, schema: SCHEMA})
|
||
|
|
).rejects.toThrow('Vote non trouvé')
|
||
|
|
})
|
||
|
|
|
||
|
|
it('lève une erreur si la version associée au vote a plus de 3 jours', async () => {
|
||
|
|
let callCount = 0
|
||
|
|
const db = vi.fn(() => db)
|
||
|
|
db.select = vi.fn(() => db)
|
||
|
|
db.where = vi.fn(() => db)
|
||
|
|
db.first = vi.fn(() => {
|
||
|
|
callCount++
|
||
|
|
// Premier appel : lookup du vote → retourne content_version_id
|
||
|
|
// Deuxième appel : lookup de la version → retourne date ancienne
|
||
|
|
return Promise.resolve(
|
||
|
|
callCount === 1
|
||
|
|
? {content_version_id: 'v-abc'}
|
||
|
|
: {date_created: OLD_DATE}
|
||
|
|
)
|
||
|
|
})
|
||
|
|
|
||
|
|
const callbacks = mountHook(makeVersionsService({outdated: false}))
|
||
|
|
|
||
|
|
await expect(
|
||
|
|
callbacks['items.delete'](['vote-1'], {collection: 'votes'}, {database: db, schema: SCHEMA})
|
||
|
|
).rejects.toThrow('3 jours')
|
||
|
|
})
|
||
|
|
|
||
|
|
it('autorise la suppression si la version est encore valide', async () => {
|
||
|
|
let callCount = 0
|
||
|
|
const db = vi.fn(() => db)
|
||
|
|
db.select = vi.fn(() => db)
|
||
|
|
db.where = vi.fn(() => db)
|
||
|
|
db.first = vi.fn(() => {
|
||
|
|
callCount++
|
||
|
|
return Promise.resolve(
|
||
|
|
callCount === 1
|
||
|
|
? {content_version_id: 'v-abc'}
|
||
|
|
: {date_created: RECENT_DATE}
|
||
|
|
)
|
||
|
|
})
|
||
|
|
|
||
|
|
const callbacks = mountHook(makeVersionsService({outdated: false}))
|
||
|
|
const input = ['vote-1']
|
||
|
|
const result = await callbacks['items.delete'](
|
||
|
|
input,
|
||
|
|
{collection: 'votes'},
|
||
|
|
{database: db, schema: SCHEMA},
|
||
|
|
)
|
||
|
|
|
||
|
|
expect(result).toBe(input)
|
||
|
|
})
|
||
|
|
})
|