import { test } from '@japa/runner' import testUtils from '@adonisjs/core/services/test_utils' import ImportDraft from '#models/import_draft' import { createTestUser } from '../helpers/auth.js' import { body, type ApiOk } from '../helpers/response.js' type DraftShape = { id: string filename: string status: 'pending' | 'validated' | 'skipped' extracted: { clientName: string; numero: string } edited: { clientName: string; numero: string } } type BatchShape = { id: string drafts: DraftShape[] } test.group('Imports — POST /invoices/upload (mock JSON)', (group) => { group.each.setup(() => testUtils.db().withGlobalTransaction()) test('crée 1 batch + N drafts depuis filenames (mock OCR)', async ({ client, assert, }) => { const { bearer } = await createTestUser() const r = await client .post('/api/v1/invoices/upload') .headers(bearer) .json({ filenames: ['facture-001.pdf', 'facture-002.pdf'] }) r.assertStatus(201) const batch = body>(r).data assert.lengthOf(batch.drafts, 2) for (const d of batch.drafts) { assert.equal(d.status, 'pending') assert.exists(d.extracted.clientName) assert.exists(d.extracted.numero) } }) test('refuse > 20 fichiers (422)', async ({ client }) => { const { bearer } = await createTestUser() const filenames = Array.from({ length: 21 }, (_, i) => `f-${i}.pdf`) const r = await client .post('/api/v1/invoices/upload') .headers(bearer) .json({ filenames }) r.assertStatus(422) }) }) test.group('Imports — Validate / Skip draft', (group) => { group.each.setup(() => testUtils.db().withGlobalTransaction()) test('validate transforme un draft en Invoice + status=validated', async ({ client, assert, }) => { const { bearer } = await createTestUser() const upload = await client .post('/api/v1/invoices/upload') .headers(bearer) .json({ filenames: ['facture-validate.pdf'] }) const batch = body>(upload).data const draft = batch.drafts[0] const r = await client .post(`/api/v1/invoices/import-batch/${batch.id}/drafts/${draft.id}/validate`) .headers(bearer) .json({ clientId: null, clientName: 'Client OCR', clientEmail: 'ocr@spec.test', numero: 'F-OCR-01', amountTtcCents: 50000, issueDate: '2026-04-01T09:00:00.000Z', dueDate: '2026-05-01T09:00:00.000Z', planId: null, }) r.assertStatus(201) const fresh = await ImportDraft.findOrFail(draft.id) assert.equal(fresh.status, 'validated') assert.isNotNull(fresh.invoiceId) }) test('validate sur draft déjà processed → 409', async ({ client }) => { const { bearer } = await createTestUser() const upload = await client .post('/api/v1/invoices/upload') .headers(bearer) .json({ filenames: ['facture-once.pdf'] }) const batch = body>(upload).data const draft = batch.drafts[0] const payload = { clientId: null, clientName: 'Client', clientEmail: 'c@spec.test', numero: 'F-OCR-X', amountTtcCents: 10000, issueDate: '2026-04-01T09:00:00.000Z', dueDate: '2026-05-01T09:00:00.000Z', planId: null, } await client .post(`/api/v1/invoices/import-batch/${batch.id}/drafts/${draft.id}/validate`) .headers(bearer) .json(payload) const r2 = await client .post(`/api/v1/invoices/import-batch/${batch.id}/drafts/${draft.id}/validate`) .headers(bearer) .json({ ...payload, numero: 'F-OCR-Y' }) r2.assertStatus(409) }) test('skip → status=skipped, idempotent', async ({ client, assert }) => { const { bearer } = await createTestUser() const upload = await client .post('/api/v1/invoices/upload') .headers(bearer) .json({ filenames: ['facture-skip.pdf'] }) const batch = body>(upload).data const draft = batch.drafts[0] const r = await client .post(`/api/v1/invoices/import-batch/${batch.id}/drafts/${draft.id}/skip`) .headers(bearer) r.assertStatus(200) const data = body>(r).data assert.equal(data.status, 'skipped') // Re-skip = idempotent (200 toujours) const again = await client .post(`/api/v1/invoices/import-batch/${batch.id}/drafts/${draft.id}/skip`) .headers(bearer) again.assertStatus(200) }) test('DELETE /import-batch/:id supprime le batch + drafts (cascade)', async ({ client, assert, }) => { const { bearer } = await createTestUser() const upload = await client .post('/api/v1/invoices/upload') .headers(bearer) .json({ filenames: ['a.pdf', 'b.pdf'] }) const batch = body>(upload).data const del = await client .delete(`/api/v1/invoices/import-batch/${batch.id}`) .headers(bearer) del.assertStatus(204) const remaining = await ImportDraft.query().where('batch_id', batch.id) assert.lengthOf(remaining, 0) }) })