diff --git a/src/__tests__/api/images/upload.test.ts b/src/__tests__/api/images/upload.test.ts new file mode 100644 index 0000000..aef66ef --- /dev/null +++ b/src/__tests__/api/images/upload.test.ts @@ -0,0 +1,75 @@ +import { POST } from '@/app/api/images/upload/route' +import { NextRequest } from 'next/server' + +jest.mock('@/lib/shopify', () => ({ + uploadProductImage: jest.fn(), +})) + +import { uploadProductImage } from '@/lib/shopify' +const mockUpload = uploadProductImage as jest.Mock + +describe('POST /api/images/upload', () => { + beforeEach(() => jest.clearAllMocks()) + + it('retourne 400 si shopifyProductId manquant', async () => { + const req = new NextRequest('http://localhost/api/images/upload', { + method: 'POST', + body: JSON.stringify({ imageBase64: 'abc', filename: 'test.jpg' }), + headers: { 'Content-Type': 'application/json' }, + }) + const res = await POST(req) + expect(res.status).toBe(400) + }) + + it('retourne 400 si imageBase64 manquant', async () => { + const req = new NextRequest('http://localhost/api/images/upload', { + method: 'POST', + body: JSON.stringify({ shopifyProductId: '123', filename: 'test.jpg' }), + headers: { 'Content-Type': 'application/json' }, + }) + const res = await POST(req) + expect(res.status).toBe(400) + }) + + it('retourne 400 si filename manquant', async () => { + const req = new NextRequest('http://localhost/api/images/upload', { + method: 'POST', + body: JSON.stringify({ shopifyProductId: '123', imageBase64: 'abc' }), + headers: { 'Content-Type': 'application/json' }, + }) + const res = await POST(req) + expect(res.status).toBe(400) + }) + + it('retourne l\'URL après upload réussi', async () => { + mockUpload.mockResolvedValue({ url: 'https://cdn.shopify.com/image.jpg' }) + const req = new NextRequest('http://localhost/api/images/upload', { + method: 'POST', + body: JSON.stringify({ + shopifyProductId: '123456', + imageBase64: 'base64data==', + filename: 'REF-1042.jpg', + }), + headers: { 'Content-Type': 'application/json' }, + }) + const res = await POST(req) + expect(res.status).toBe(200) + expect(await res.json()).toEqual({ url: 'https://cdn.shopify.com/image.jpg' }) + expect(mockUpload).toHaveBeenCalledWith('123456', 'base64data==', 'REF-1042.jpg') + }) + + it('retourne 500 en cas d\'erreur Shopify', async () => { + mockUpload.mockRejectedValue(new Error('Shopify upload error: 422')) + const req = new NextRequest('http://localhost/api/images/upload', { + method: 'POST', + body: JSON.stringify({ + shopifyProductId: '123', + imageBase64: 'abc', + filename: 'test.jpg', + }), + headers: { 'Content-Type': 'application/json' }, + }) + const res = await POST(req) + expect(res.status).toBe(500) + }) +}) diff --git a/src/app/api/images/upload/route.ts b/src/app/api/images/upload/route.ts new file mode 100644 index 0000000..02687b4 --- /dev/null +++ b/src/app/api/images/upload/route.ts @@ -0,0 +1,31 @@ +import { NextRequest, NextResponse } from 'next/server' +import { uploadProductImage } from '@/lib/shopify' + +export async function POST(req: NextRequest) { + let body: Record + try { + body = await req.json() + } catch { + return NextResponse.json({ error: 'Corps JSON invalide' }, { status: 400 }) + } + + const { shopifyProductId, imageBase64, filename } = body + + if (!shopifyProductId || typeof shopifyProductId !== 'string') { + return NextResponse.json({ error: 'shopifyProductId requis' }, { status: 400 }) + } + if (!imageBase64 || typeof imageBase64 !== 'string') { + return NextResponse.json({ error: 'imageBase64 requis' }, { status: 400 }) + } + if (!filename || typeof filename !== 'string') { + return NextResponse.json({ error: 'filename requis' }, { status: 400 }) + } + + try { + const result = await uploadProductImage(shopifyProductId, imageBase64, filename) + return NextResponse.json(result) + } catch (err) { + const message = err instanceof Error ? err.message : 'Erreur inconnue' + return NextResponse.json({ error: message }, { status: 500 }) + } +}