后端补充订单详情服务测试

This commit is contained in:
yml
2026-05-21 14:53:13 +08:00
parent 4740f870ba
commit 6d6e64beda
4 changed files with 249 additions and 2 deletions
@@ -16,6 +16,8 @@ const AGISO_SHOP_CONFIG_KEYS = [
'apiVersion',
'sendMessageEndpoint',
'tradeDetailEndpoint',
'tradeDetailApiVersion',
'tradeDetailTimeoutMs',
]
export function getAgisoShopsFilePath() {
@@ -12,11 +12,17 @@ import { parseJsonObject } from '../../../../utils/json.js'
const DEFAULT_DETAIL_TIMEOUT_MS = 5000
export async function enrichAgisoXianyuTradeOrder(parsed, { requestId = '' } = {}) {
return enrichAgisoXianyuTradeOrderWithDeps(parsed, { requestId })
}
export async function enrichAgisoXianyuTradeOrderWithDeps(parsed, { requestId = '' } = {}, deps = {}) {
const queryOrderDetail = deps.queryAgisoXianyuOrderDetail || queryAgisoXianyuOrderDetail
if (!shouldHydrateAgisoXianyuTradeOrder(parsed)) {
return { parsed, enriched: false, reason: 'not_needed' }
}
const detailResult = await queryAgisoXianyuOrderDetail({
const detailResult = await queryOrderDetail({
shopId: parsed.shopId,
platformOrderId: parsed.platformOrderId,
requestId,
@@ -58,6 +64,14 @@ export async function enrichAgisoXianyuTradeOrder(parsed, { requestId = '' } = {
}
export async function queryAgisoXianyuOrderDetail({ shopId = '', platformOrderId = '', requestId = '' } = {}) {
return queryAgisoXianyuOrderDetailWithDeps({ shopId, platformOrderId, requestId })
}
export async function queryAgisoXianyuOrderDetailWithDeps(
{ shopId = '', platformOrderId = '', requestId = '' } = {},
deps = {},
) {
const requestWithTimeout = deps.fetchWithTimeout || fetchWithTimeout
const normalizedShopId = String(shopId || '').trim()
const normalizedPlatformOrderId = String(platformOrderId || '').trim()
const config = resolveAgisoXianyuTradeDetailConfig(normalizedShopId)
@@ -94,7 +108,7 @@ export async function queryAgisoXianyuOrderDetail({ shopId = '', platformOrderId
})
try {
const response = await fetchWithTimeout(config.endpoint, {
const response = await requestWithTimeout(config.endpoint, {
method: 'POST',
headers: requestHeaders,
body: new URLSearchParams(requestBody).toString(),
@@ -0,0 +1,219 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import { runtimeConfig } from '../../../../config/runtime.js'
import {
enrichAgisoXianyuTradeOrderWithDeps,
queryAgisoXianyuOrderDetailWithDeps,
resolveAgisoXianyuOrderDeliveryState,
} from './order-detail-service.js'
test('resolveAgisoXianyuOrderDeliveryState detects shipped order by ship time or status', () => {
assert.deepEqual(resolveAgisoXianyuOrderDeliveryState({ ship_time: 1713097840 }), {
shipped: true,
shipTime: 1713097840,
orderStatus: 0,
})
assert.deepEqual(resolveAgisoXianyuOrderDeliveryState({ order_status: 3 }), {
shipped: true,
shipTime: 0,
orderStatus: 3,
})
assert.deepEqual(resolveAgisoXianyuOrderDeliveryState({ order_status: 2 }), {
shipped: false,
shipTime: 0,
orderStatus: 2,
})
})
test('queryAgisoXianyuOrderDetailWithDeps parses successful detail payload and masks network dependency', async () => {
const originalShops = runtimeConfig.platforms.agiso.messaging.shops
const originalAppSecret = runtimeConfig.platforms.agiso.appSecret
const calls = []
runtimeConfig.platforms.agiso.appSecret = ''
runtimeConfig.platforms.agiso.messaging.shops = {
'shop-detail-test': {
accessToken: 'access-token',
appSecret: 'shop-secret',
tradeDetailEndpoint: 'https://example.com/detail',
tradeDetailApiVersion: '2',
tradeDetailTimeoutMs: '3000',
},
}
try {
const result = await queryAgisoXianyuOrderDetailWithDeps({
shopId: 'shop-detail-test',
platformOrderId: 'P-DETAIL-10001',
requestId: 'req-detail',
}, {
fetchWithTimeout: async (url, options, timeoutMs) => {
calls.push({ url, options, timeoutMs })
return {
status: 200,
text: async () => JSON.stringify({
IsSuccess: true,
Data: {
total_fee: '25.50',
pay_time: '2026-04-14 20:30:40',
ship_time: 1713097840,
order_status: 3,
item: {
item_id: 'item-1',
sku: 'dnf-cdk-a|商品名称:DNF礼包',
quantity: 2,
},
},
}),
}
},
})
assert.equal(result.success, true)
assert.equal(result.responseStatus, 200)
assert.equal(result.totalAmountFen, 2550)
assert.equal(result.shipped, true)
assert.equal(result.shipTime, 1713097840)
assert.equal(result.orderStatus, 3)
assert.equal(calls[0]?.url, 'https://example.com/detail')
assert.equal(calls[0]?.timeoutMs, 3000)
assert.equal(calls[0]?.options.headers.Authorization, 'Bearer access-token')
assert.equal(calls[0]?.options.headers.ApiVersion, '2')
assert.match(String(calls[0]?.options.body), /tid=P-DETAIL-10001/)
assert.match(String(calls[0]?.options.body), /sign=/)
} finally {
runtimeConfig.platforms.agiso.messaging.shops = originalShops
runtimeConfig.platforms.agiso.appSecret = originalAppSecret
}
})
test('queryAgisoXianyuOrderDetailWithDeps returns business_error with structured message', async () => {
const originalShops = runtimeConfig.platforms.agiso.messaging.shops
runtimeConfig.platforms.agiso.messaging.shops = {
'shop-detail-test': {
accessToken: 'access-token',
appSecret: 'shop-secret',
tradeDetailEndpoint: 'https://example.com/detail',
},
}
try {
const result = await queryAgisoXianyuOrderDetailWithDeps({
shopId: 'shop-detail-test',
platformOrderId: 'P-DETAIL-10002',
}, {
fetchWithTimeout: async () => ({
status: 200,
text: async () => JSON.stringify({
IsSuccess: false,
Error_Msg: '订单不存在',
}),
}),
})
assert.equal(result.success, false)
assert.equal(result.reason, 'business_error')
assert.equal(result.errorMessage, '订单不存在')
assert.equal(result.responseStatus, 200)
} finally {
runtimeConfig.platforms.agiso.messaging.shops = originalShops
}
})
test('queryAgisoXianyuOrderDetailWithDeps skips missing config without fetching', async () => {
const originalShops = runtimeConfig.platforms.agiso.messaging.shops
const originalAppSecret = runtimeConfig.platforms.agiso.appSecret
let fetchCalled = false
runtimeConfig.platforms.agiso.messaging.shops = {}
runtimeConfig.platforms.agiso.appSecret = ''
try {
const result = await queryAgisoXianyuOrderDetailWithDeps({
shopId: 'missing-shop',
platformOrderId: 'P-DETAIL-10003',
}, {
fetchWithTimeout: async () => {
fetchCalled = true
return { status: 200, text: async () => '{}' }
},
})
assert.equal(result.success, false)
assert.equal(result.reason, 'missing_config')
assert.equal(fetchCalled, false)
} finally {
runtimeConfig.platforms.agiso.messaging.shops = originalShops
runtimeConfig.platforms.agiso.appSecret = originalAppSecret
}
})
test('enrichAgisoXianyuTradeOrderWithDeps merges detail payload into incomplete trade', async () => {
const parsed = {
provider: 'agiso',
platform: 'xianyu',
shopId: 'shop-detail-test',
shopName: '',
platformOrderId: 'P-DETAIL-10004',
totalAmount: 0,
paidAt: null,
buyerId: '',
buyerName: '',
receiverContact: '',
rawPayload: {},
items: [],
}
const result = await enrichAgisoXianyuTradeOrderWithDeps(parsed, { requestId: 'req-enrich' }, {
queryAgisoXianyuOrderDetail: async (input) => {
assert.deepEqual(input, {
shopId: 'shop-detail-test',
platformOrderId: 'P-DETAIL-10004',
requestId: 'req-enrich',
})
return {
success: true,
reason: '',
errorMessage: '',
responseStatus: 200,
payload: {},
detailPayload: {
total_fee: '19.90',
pay_time: '2026-04-14 20:30:40',
buyer_id: 'buyer-1',
buyer_name: '测试买家',
receiver_mobile: '13800138000',
shop_name: '详情店铺',
items: [
{
item_id: 'item-1',
sku: 'dnf-cdk-a|商品名称:DNF礼包',
quantity: 2,
},
],
},
totalAmountFen: 1990,
shipped: false,
shipTime: 0,
orderStatus: 2,
}
},
})
assert.equal(result.enriched, true)
assert.equal(result.totalAmountFen, 1990)
assert.equal(result.parsed.totalAmount, 1990)
assert.equal(result.parsed.buyerId, 'buyer-1')
assert.equal(result.parsed.buyerName, '测试买家')
assert.equal(result.parsed.receiverContact, '13800138000')
assert.equal(result.parsed.shopName, '详情店铺')
assert.equal(result.parsed.items.length, 1)
assert.equal(result.parsed.items[0]?.skuCode, 'dnf-cdk-a')
assert.equal(result.parsed.items[0]?.quantity, 2)
assert.deepEqual(result.parsed.rawPayload._agisoTradeDetail.total_fee, '19.90')
})
+12
View File
@@ -337,6 +337,18 @@
- `npm run typecheck`
- `npm run build`
- `npm test` 共 131 个用例通过
54. Agiso 订单详情服务迁移前补直接测试:
- 新增 `src/services/platforms/agiso/xianyu/order-detail-service.test.js`
- 新增 `enrichAgisoXianyuTradeOrderWithDeps``queryAgisoXianyuOrderDetailWithDeps`,便于隔离订单详情 HTTP 请求
- 覆盖发货状态识别、详情成功解析、业务失败、缺配置跳过、详情补全 webhook 订单五个关键分支
55. 修复 Agiso 店铺配置白名单:
- `tradeDetailApiVersion`
- `tradeDetailTimeoutMs`
56. Docker 内验证通过:
- `src/services/platforms/agiso/xianyu/order-detail-service.test.js` 共 5 个用例通过
- `npm run typecheck`
- `npm run build`
- `npm test` 共 136 个用例通过
## 下一步建议