gongxiangp2/lib/__tests__/taobao-detail-plus-route.test.ts
2026-05-07 16:37:22 +08:00

246 lines
8.0 KiB
TypeScript

/** @jest-environment node */
import { GET } from '@/app/api/taobao-detail-plus/route'
import { getServerSession } from 'next-auth'
jest.mock('next-auth', () => ({
getServerSession: jest.fn(),
}))
jest.mock('@/lib/auth', () => ({
authOptions: {},
}))
describe('taobao-detail-plus route', () => {
const originalApiKey = process.env.TAOBAO_DETAIL_PLUS_API_KEY
const originalBaseUrl = process.env.TAOBAO_DETAIL_PLUS_API_BASE_URL
beforeEach(() => {
jest.clearAllMocks()
;(getServerSession as jest.Mock).mockResolvedValue({
user: { id: 'user-1', name: 'tester' },
})
process.env.TAOBAO_DETAIL_PLUS_API_KEY = 'test-api-key'
process.env.TAOBAO_DETAIL_PLUS_API_BASE_URL = 'http://mocked.example.com'
global.fetch = jest.fn() as jest.Mock
})
afterAll(() => {
if (originalApiKey === undefined) {
delete process.env.TAOBAO_DETAIL_PLUS_API_KEY
} else {
process.env.TAOBAO_DETAIL_PLUS_API_KEY = originalApiKey
}
if (originalBaseUrl === undefined) {
delete process.env.TAOBAO_DETAIL_PLUS_API_BASE_URL
} else {
process.env.TAOBAO_DETAIL_PLUS_API_BASE_URL = originalBaseUrl
}
})
function createRequest(url: string) {
return {
nextUrl: new URL(url),
} as any
}
test('未登录时应返回 401', async () => {
;(getServerSession as jest.Mock).mockResolvedValue(null)
const response = await GET(createRequest('https://example.com/api/taobao-detail-plus?itemId=867081550619'))
const body = await response.json()
expect(response.status).toBe(401)
expect(body.error.code).toBe('UNAUTHORIZED')
})
test('商品ID 非法时应返回 400', async () => {
const response = await GET(createRequest('https://example.com/api/taobao-detail-plus?itemId=abc'))
const body = await response.json()
expect(response.status).toBe(400)
expect(body.error.code).toBe('INVALID_ITEM_ID')
})
test('未配置第三方密钥时应返回 500', async () => {
delete process.env.TAOBAO_DETAIL_PLUS_API_KEY
const response = await GET(createRequest('https://example.com/api/taobao-detail-plus?itemId=867081550619'))
const body = await response.json()
expect(response.status).toBe(500)
expect(body.error.code).toBe('SERVICE_NOT_CONFIGURED')
})
test('上游返回 404 时应转成商品不存在的人话提示', async () => {
;(global.fetch as jest.Mock).mockResolvedValue({
ok: false,
status: 404,
json: async () => ({
success: false,
error: '未找到该商品数据',
}),
})
const response = await GET(createRequest('https://example.com/api/taobao-detail-plus?itemId=930071432357'))
const body = await response.json()
expect(response.status).toBe(404)
expect(body.error.code).toBe('ITEM_NOT_FOUND')
expect(body.error.message).toBe('未找到该商品数据')
})
test('查询成功时应返回精简后的商品摘要', async () => {
;(global.fetch as jest.Mock).mockResolvedValue({
ok: true,
json: async () => ({
success: true,
data: {
item_id: '867081550619',
title: '床边桌可移动升降折叠桌子',
shop_name: '耐家官方旗舰店',
price: '59.50',
location: '江苏常州',
seller_type: '天猫',
detail_url: 'https://item.taobao.com/item.htm?id=867081550619',
video_url: 'https://cloud.video.taobao.com/video.mp4',
item_imgs_count: 5,
skus_count: 25,
attributes_count: 17,
total_count: '900+',
item_imgs_json: [
{ url: 'https://img.example.com/1.jpg' },
{ url: 'https://img.example.com/2.jpg' },
],
skus_json: [
{ img: 'https://img.example.com/sku-1.jpg' },
{ image: 'https://img.example.com/sku-2.jpg' },
],
desc_imgs_json: [
{ url: 'https://img.example.com/desc-1.jpg' },
],
},
}),
})
const response = await GET(createRequest('https://example.com/api/taobao-detail-plus?itemId=867081550619'))
const body = await response.json()
expect(response.status).toBe(200)
expect(body.success).toBe(true)
expect(body.data).toEqual({
itemId: '867081550619',
title: '床边桌可移动升降折叠桌子',
shopName: '耐家官方旗舰店',
price: '59.50',
location: '江苏常州',
sellerType: '天猫',
detailUrl: 'https://item.taobao.com/item.htm?id=867081550619',
videoUrl: 'https://cloud.video.taobao.com/video.mp4',
mainImageUrl: 'https://img.example.com/1.jpg',
itemImages: ['https://img.example.com/1.jpg', 'https://img.example.com/2.jpg'],
allItemImages: ['https://img.example.com/1.jpg', 'https://img.example.com/2.jpg'],
skuImages: ['https://img.example.com/sku-1.jpg', 'https://img.example.com/sku-2.jpg'],
descImages: ['https://img.example.com/desc-1.jpg'],
itemImageCount: 5,
skuCount: 25,
attributeCount: 17,
totalCount: '900+',
})
expect(global.fetch).toHaveBeenCalledWith(
'http://mocked.example.com/api/public/taobao-detail-plus?itemId=867081550619',
expect.objectContaining({
headers: {
'x-api-key': 'test-api-key',
},
method: 'GET',
}),
)
})
test('应兼容从 skuBase 和详情 HTML 中提取图片', async () => {
;(global.fetch as jest.Mock).mockResolvedValue({
ok: true,
json: async () => ({
success: true,
data: {
item_id: '867081550619',
title: '测试商品',
shop_name: '测试店铺',
item_imgs_json: [
{ url: 'https://img.example.com/main-1.jpg' },
],
raw_data_json: {
skuBase: {
props: [
{
values: [
{ image: '//img.example.com/sku-a.png' },
{ imgUrl: 'https://img.example.com/sku-b.png' },
],
},
],
},
descHtml:
'<div><img src="https://img.example.com/desc-a.jpg" /><img src="//img.example.com/desc-b.webp" /></div>',
},
},
}),
})
const response = await GET(createRequest('https://example.com/api/taobao-detail-plus?itemId=867081550619'))
const body = await response.json()
expect(response.status).toBe(200)
expect(body.data.skuImages).toEqual([
'https://img.example.com/sku-a.png',
'https://img.example.com/sku-b.png',
])
expect(body.data.descImages).toEqual([
'https://img.example.com/desc-a.jpg',
'https://img.example.com/desc-b.webp',
])
})
test('应兼容从 specs_json 和字符串化 JSON 字段中提取 SKU 图与详情图', async () => {
;(global.fetch as jest.Mock).mockResolvedValue({
ok: true,
json: async () => ({
success: true,
data: {
item_id: '867081550619',
title: '测试商品 2',
shop_name: '测试店铺 2',
item_imgs_json: JSON.stringify([
{ url: 'https://img.example.com/main-1.jpg' },
]),
specs_json: JSON.stringify({
颜色分类: {
a: ['胡桃色', 'http://img.example.com/spec-1.jpg'],
b: ['纯白色', 'https://img.example.com/spec-2.png'],
},
}),
raw_data_json: JSON.stringify({
desc:
'<div><img src="https://img.example.com/desc-from-raw.jpg" /></div>',
}),
},
}),
})
const response = await GET(createRequest('https://example.com/api/taobao-detail-plus?itemId=867081550619'))
const body = await response.json()
expect(response.status).toBe(200)
expect(body.data.itemImages).toEqual(['https://img.example.com/main-1.jpg'])
expect(body.data.skuImages).toEqual([
'http://img.example.com/spec-1.jpg',
'https://img.example.com/spec-2.png',
])
expect(body.data.descImages).toEqual([
'https://img.example.com/desc-from-raw.jpg',
])
})
})