后端补充订单详情服务测试
This commit is contained in:
@@ -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')
|
||||
})
|
||||
Reference in New Issue
Block a user