feat(plan4): API GET /api/products/list avec filtre statut (TDD)

This commit is contained in:
2026-06-08 23:21:18 +02:00
parent 6dd6dc301e
commit 9303aab78e
2 changed files with 92 additions and 0 deletions
+59
View File
@@ -0,0 +1,59 @@
import { GET } from './route'
import * as shopifySync from '@/lib/shopifySync'
import type { ShopifyProductFull } from '@/types/sync'
jest.mock('@/lib/shopifySync')
const mockFetch = shopifySync.fetchAllShopifyProducts as jest.MockedFunction<typeof shopifySync.fetchAllShopifyProducts>
const PRODUCTS: ShopifyProductFull[] = [
{ shopifyId: '1', ref: 'REF-001', handle: 'ref-001', title: 'Brique rouge', description: '', price: '12.50', stock: 100, status: 'active' },
{ shopifyId: '2', ref: 'REF-002', handle: 'ref-002', title: 'Parpaing', description: '', price: '5.00', stock: 0, status: 'draft' },
{ shopifyId: '3', ref: 'REF-003', handle: 'ref-003', title: 'Ardoise', description: '', price: '80.00', stock: 20, status: 'active' },
]
describe('GET /api/products/list', () => {
beforeEach(() => {
process.env.SHOPIFY_STORE_DOMAIN = 'test.myshopify.com'
process.env.SHOPIFY_ADMIN_API_TOKEN = 'token'
mockFetch.mockResolvedValue(PRODUCTS)
})
afterEach(() => jest.clearAllMocks())
it('retourne tous les produits sans filtre', async () => {
const req = new Request('http://localhost/api/products/list')
const res = await GET(req as never)
const body = await res.json()
expect(body.total).toBe(3)
expect(body.products).toHaveLength(3)
})
it('filtre par status=active', async () => {
const req = new Request('http://localhost/api/products/list?status=active')
const res = await GET(req as never)
const body = await res.json()
expect(body.total).toBe(2)
body.products.forEach((p: { status: string }) => expect(p.status).toBe('active'))
})
it('filtre par status=draft', async () => {
const req = new Request('http://localhost/api/products/list?status=draft')
const res = await GET(req as never)
const body = await res.json()
expect(body.total).toBe(1)
expect(body.products[0].ref).toBe('REF-002')
})
it('retourne shopifyUrl pour chaque produit', async () => {
const req = new Request('http://localhost/api/products/list')
const res = await GET(req as never)
const body = await res.json()
expect(body.products[0].shopifyUrl).toContain('test.myshopify.com/admin/products/1')
})
it('retourne 500 si Shopify inaccessible', async () => {
mockFetch.mockRejectedValue(new Error('network error'))
const req = new Request('http://localhost/api/products/list')
const res = await GET(req as never)
expect(res.status).toBe(500)
})
})
+33
View File
@@ -0,0 +1,33 @@
import { NextRequest, NextResponse } from 'next/server'
import { fetchAllShopifyProducts } from '@/lib/shopifySync'
import type { CatalogProduct, ProductListResponse } from '@/types/products'
export async function GET(req: NextRequest): Promise<NextResponse<ProductListResponse | { error: string }>> {
try {
const { searchParams } = new URL(req.url)
const statusFilter = searchParams.get('status') ?? 'all'
const domain = process.env.SHOPIFY_STORE_DOMAIN ?? ''
const shopifyProducts = await fetchAllShopifyProducts()
let products: CatalogProduct[] = shopifyProducts.map((p) => ({
shopifyId: p.shopifyId,
ref: p.ref,
handle: p.handle,
title: p.title,
price: p.price,
stock: p.stock,
status: p.status as CatalogProduct['status'],
shopifyUrl: `https://${domain}/admin/products/${p.shopifyId}`,
}))
if (statusFilter !== 'all') {
products = products.filter((p) => p.status === statusFilter)
}
return NextResponse.json({ products, total: products.length })
} catch (err) {
const message = err instanceof Error ? err.message : 'Erreur inconnue'
return NextResponse.json({ error: message }, { status: 500 })
}
}