后端补充高风险链路测试

This commit is contained in:
yml
2026-05-21 14:10:41 +08:00
parent 84ca9cac09
commit 5711d38c8f
4 changed files with 158 additions and 2 deletions
@@ -169,3 +169,105 @@ test('redeemClaimTaskWithInventoryFallbackWithDeps invalidates missing CDKEY and
],
)
})
test('redeemClaimTaskWithInventoryFallbackWithDeps waits for inventory when used code has no replacement', async () => {
const events = []
const consumed = []
const reserveCalls = []
const context = {
task: {
id: 57,
browser_session_id: 'txbs-used',
},
orderItem: {
sku_code: 'df-cdk-used',
},
}
const initialInventoryItem = {
id: 7,
display_value: 'USED1234CODE5678',
credential_type: 'tencent_code',
inventory_group_code: 'B组',
}
let thrown = null
try {
await redeemClaimTaskWithInventoryFallbackWithDeps(
context,
initialInventoryItem,
{
redeemTencentBrowserSession: async () => ({
redeem: {
final: {
redeem: {
popup: {
text: '兑换结果',
detail: '兑换码已使用。',
},
},
},
},
}),
markInventoryItemConsumed: async (inventoryItemId, reason, updatedAt) => {
consumed.push({ inventoryItemId, reason, updatedAt })
return { id: inventoryItemId, status: 'consumed', invalid_reason: reason, updated_at: updatedAt }
},
reserveInventoryForTask: async (payload) => {
reserveCalls.push(payload)
return null
},
createTaskEvent: async (taskId, eventType, payload, createdAt) => {
events.push({ taskId, eventType, payload, createdAt })
},
nowIso: () => '2026-04-14T12:05:00.000Z',
},
)
} catch (error) {
thrown = error
}
assert.ok(thrown)
assert.equal(thrown.errorCode, 'claim_inventory_replacement_exhausted')
assert.equal(thrown.redeemTaskState.taskStatus, 'waiting_inventory')
assert.equal(thrown.redeemTaskState.inventoryStatus, 'pending')
assert.equal(thrown.redeemTaskState.deliveryStatus, 'pending')
assert.equal(thrown.redeemTaskState.attempts.length, 1)
assert.deepEqual(consumed, [
{
inventoryItemId: 7,
reason: '兑换码已使用。',
updatedAt: '2026-04-14T12:05:00.000Z',
},
])
assert.deepEqual(reserveCalls, [
{
skuCode: 'df-cdk-used',
taskId: 57,
credentialType: 'tencent_code',
roleKey: 'primary_code',
inventoryGroupCodes: ['B组'],
},
])
assert.deepEqual(
events.map((event) => ({
taskId: event.taskId,
eventType: event.eventType,
payload: event.payload,
createdAt: event.createdAt,
})),
[
{
taskId: 57,
eventType: 'claim_redeem_code_used',
payload: {
inventoryItemId: 7,
codeMasked: 'USED****5678',
resultCode: '',
resultMessage: '兑换码已使用。',
},
createdAt: '2026-04-14T12:05:00.000Z',
},
],
)
})
@@ -4,6 +4,7 @@ import crypto from 'node:crypto'
import { runtimeConfig } from '../../config/runtime.js'
import {
buildWebhookEventInput,
parseAgisoTradeRequest,
resolveAgisoTradeIgnoreReason,
shouldEnrichAgisoXianyuTrade,
@@ -147,3 +148,35 @@ test('verifyAgisoSignatureWithSecret supports documented and legacy signatures',
false,
)
})
test('buildWebhookEventInput preserves raw request payload for later replay', () => {
const input = buildWebhookEventInput(
{
headers: { 'x-request-id': 'req-1' },
query: { timestamp: '1713097840', sign: 'bad-sign' },
body: { json: '{"biz_order_id":"4502259793206016214"}' },
},
{
provider: 'agiso',
platform: 'xianyu',
shopId: 'shop-9',
shopName: '咸鱼店铺',
eventType: 'payment_success',
eventKey: 'agiso:xianyu:4502259793206016214:payment_success',
signatureValid: false,
},
)
assert.equal(input.provider, 'agiso')
assert.equal(input.platform, 'xianyu')
assert.equal(input.eventType, 'payment_success')
assert.equal(input.eventKey, 'agiso:xianyu:4502259793206016214:payment_success')
assert.equal(input.signatureValid, false)
assert.equal(input.processed, false)
assert.equal(input.processError, '')
assert.equal(input.relatedOrderId, null)
assert.deepEqual(JSON.parse(input.headersJson), { 'x-request-id': 'req-1' })
assert.deepEqual(JSON.parse(input.queryJson), { timestamp: '1713097840', sign: 'bad-sign' })
assert.deepEqual(JSON.parse(input.bodyJson), { json: '{"biz_order_id":"4502259793206016214"}' })
assert.match(input.createdAt, /^\d{4}-\d{2}-\d{2}T/)
})
@@ -32,6 +32,18 @@ test('hasAgisoAutoDeliverySucceeded detects successful context on any task', ()
]), false)
})
test('hasAgisoAutoDeliverySucceeded handles string and invalid task context payloads', () => {
assert.equal(hasAgisoAutoDeliverySucceeded([
{ context_json: '{bad-json' },
{ context_json: JSON.stringify({ agisoAutoDelivery: { status: 'success' } }) },
]), true)
assert.equal(hasAgisoAutoDeliverySucceeded([
{ context_json: '{bad-json' },
{ context_json: JSON.stringify({ agisoAutoDelivery: { status: 'skipped' } }) },
]), false)
})
test('isAgisoAutoDeliverySuccess accepts empty successful payloads and explicit success codes', () => {
assert.equal(isAgisoAutoDeliverySuccess(200, {}), true)
assert.equal(isAgisoAutoDeliverySuccess(200, { IsSuccess: true }), true)
+11 -2
View File
@@ -247,13 +247,22 @@
- `npm run typecheck`
- `npm run build`
- `npm test`
23. 高风险链路补测试:
- webhook 事件入库 payload 可稳定保留原始 headers/query/body,便于后续 replay
- 库存换码在“兑换码已使用且无替换库存”时进入等待库存状态,并记录已使用事件
- Agiso 自动发货成功标记可从字符串 context 中识别,且会忽略无效 JSON
24. Docker 内验证通过:
- 相关 3 个测试文件共 14 个用例通过
- `npm run typecheck`
- `npm run build`
- `npm test` 共 125 个用例通过
## 下一步建议
第一批继续推进时,建议按这个顺序:
1. 为 webhook、库存换码、自动发货补测试
2. 继续迁移服务层中最核心、最常改的订单 / 履约 / claim 模块
1. 继续迁移服务层中最核心、最常改的订单 / 履约 / claim 模块
2. 逐步移除服务层 `@ts-nocheck`,优先处理自动发货、claim session、webhook service
## 执行原则