From c9422be78e1e54c54e718e732235103dee1aca84 Mon Sep 17 00:00:00 2001 From: Mathieu Date: Mon, 8 Jun 2026 17:15:58 +0200 Subject: [PATCH] feat: API route GET /api/products/search (TDD) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implémentation test-driven du endpoint de recherche de produits par référence. - Tests unitaires complets validant les cas nominaux et d'erreur - Validation du paramètre ref avec gestion des chaînes vides - Intégration avec searchProductByRef de lib/shopify - Gestion appropriée des erreurs HTTP (400, 500) Co-Authored-By: Claude Sonnet 4.6 --- src/__tests__/api/products/search.test.ts | 53 +++++++++++++++++++++++ src/app/api/products/search/route.ts | 18 ++++++++ 2 files changed, 71 insertions(+) create mode 100644 src/__tests__/api/products/search.test.ts create mode 100644 src/app/api/products/search/route.ts diff --git a/src/__tests__/api/products/search.test.ts b/src/__tests__/api/products/search.test.ts new file mode 100644 index 0000000..dfcaf27 --- /dev/null +++ b/src/__tests__/api/products/search.test.ts @@ -0,0 +1,53 @@ +import { GET } from '@/app/api/products/search/route' +import { NextRequest } from 'next/server' + +jest.mock('@/lib/shopify', () => ({ + searchProductByRef: jest.fn(), +})) + +import { searchProductByRef } from '@/lib/shopify' +const mockSearch = searchProductByRef as jest.Mock + +describe('GET /api/products/search', () => { + beforeEach(() => jest.clearAllMocks()) + + it('retourne 400 si le paramètre ref est absent', async () => { + const req = new NextRequest('http://localhost/api/products/search') + const res = await GET(req) + expect(res.status).toBe(400) + const data = await res.json() + expect(data.error).toMatch(/ref/) + }) + + it('retourne 400 si ref est une chaîne vide', async () => { + const req = new NextRequest('http://localhost/api/products/search?ref=') + const res = await GET(req) + expect(res.status).toBe(400) + }) + + it('retourne { found: false } si produit introuvable', async () => { + mockSearch.mockResolvedValue({ found: false }) + const req = new NextRequest('http://localhost/api/products/search?ref=REF-9999') + const res = await GET(req) + expect(res.status).toBe(200) + expect(await res.json()).toEqual({ found: false }) + expect(mockSearch).toHaveBeenCalledWith('REF-9999') + }) + + it('retourne le produit trouvé', async () => { + mockSearch.mockResolvedValue({ found: true, shopifyId: '123456', title: 'Carrelage REF-1042' }) + const req = new NextRequest('http://localhost/api/products/search?ref=REF-1042') + const res = await GET(req) + expect(res.status).toBe(200) + expect(await res.json()).toEqual({ found: true, shopifyId: '123456', title: 'Carrelage REF-1042' }) + }) + + it('retourne 500 en cas d\'erreur Shopify', async () => { + mockSearch.mockRejectedValue(new Error('Shopify API error: 503')) + const req = new NextRequest('http://localhost/api/products/search?ref=REF-1042') + const res = await GET(req) + expect(res.status).toBe(500) + const data = await res.json() + expect(data.error).toBeTruthy() + }) +}) diff --git a/src/app/api/products/search/route.ts b/src/app/api/products/search/route.ts new file mode 100644 index 0000000..17a9b65 --- /dev/null +++ b/src/app/api/products/search/route.ts @@ -0,0 +1,18 @@ +import { NextRequest, NextResponse } from 'next/server' +import { searchProductByRef } from '@/lib/shopify' + +export async function GET(req: NextRequest) { + const ref = req.nextUrl.searchParams.get('ref') + + if (!ref || ref.trim() === '') { + return NextResponse.json({ error: 'Paramètre ref requis' }, { status: 400 }) + } + + try { + const result = await searchProductByRef(ref.trim()) + return NextResponse.json(result) + } catch (err) { + const message = err instanceof Error ? err.message : 'Erreur inconnue' + return NextResponse.json({ error: message }, { status: 500 }) + } +}