feat(plan4): API GET /api/products/list avec filtre statut (TDD)
This commit is contained in:
@@ -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)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -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 })
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user