test(phase3): verify merchant fulfillment scope

This commit is contained in:
2026-08-10 14:18:24 +08:00
parent ce25a0a72d
commit a81f73d835
@@ -68,6 +68,23 @@ export async function run(context) {
`parent=${id(tree?.parentOrderId) || 'missing'}, children=${tree?.children?.length ?? 'n/a'}, allocated=${tree?.allocatedPackageCount ?? 'n/a'}, remaining=${tree?.remainingPackageCount ?? 'n/a'}`, OPTIONS)
if (!tree) return
// Re-query through the real merchant contract as well. The customer scope is
// taken from the token; no tenant/customer id is accepted by this endpoint.
const merchantLogin = await context.request('POST', '/api/auth/login', {
username: 'merchant_admin', password: 'ChangeMe123!',
}, '')
const merchantAuth = context.apiSuccess(MODULE, '客户账号登录以验证履约树隔离', merchantLogin, '', OPTIONS)
const merchantToken = merchantAuth?.accessToken
if (merchantToken) {
const merchantTree = await context.request('GET',
`/api/merchant/v1/orders/${encodeURIComponent(orderId)}/fulfillment-tree`, undefined, merchantToken)
const merchantTreeData = context.apiSuccess(MODULE, '客户查询本客户父子履约树', merchantTree,
`orderId=${orderId}`, OPTIONS)
context.assert(MODULE, '客户履约树不返回平台内部字段且归属当前客户', merchantTree,
treeShape(merchantTreeData, orderId),
`parent=${id(merchantTreeData?.parentOrderId) || 'missing'}, children=${merchantTreeData?.children?.length ?? 'n/a'}`, OPTIONS)
}
const slicesPath = `/api/platform/v1/orders/container-bookings/${encodeURIComponent(containerBookingId)}/fulfillment-slices?tenantId=${encodeURIComponent(tenantId)}`
const slicesResponse = await context.request('GET', slicesPath)
const slices = context.apiSuccess(MODULE, '平台按柜次查询真实履约分片聚合', slicesResponse,
@@ -105,6 +122,16 @@ export async function run(context) {
Array.isArray(wrongSlicesData) && wrongSlicesData.length === 0,
`rows=${Array.isArray(wrongSlicesData) ? wrongSlicesData.length : 'n/a'}`, OPTIONS)
if (merchantToken) {
const merchantSlices = await context.request('GET',
`/api/merchant/v1/orders/container-bookings/${encodeURIComponent(containerBookingId)}/fulfillment-slices`,
undefined, merchantToken)
const merchantSlicesData = context.apiSuccess(MODULE, '客户查询本客户柜次履约分片', merchantSlices, '', OPTIONS)
context.assert(MODULE, '客户柜次分片仅返回当前客户数据', merchantSlices,
Array.isArray(merchantSlicesData) && merchantSlicesData.every((row) => sliceShape(row, containerBookingId, tenantId)),
`rows=${Array.isArray(merchantSlicesData) ? merchantSlicesData.length : 'n/a'}`, OPTIONS)
}
context.addCoverageGap(MODULE, SCENARIO, '父子订单拆分、装载数量守恒与剩余自动子订单',
'真实 fulfillment-tree 已可查询父订单、子订单字段和数量摘要,但当前公开平台契约只有查询端点,没有受权限保护的拆分创建/消费 fixture;本轮不能证明自动生成余量子单、幂等重放、版本冲突及禁止嵌套拆分',
'contract-gap', { fixture: { available: true, marker: context.marker, inboundId, containerBookingId, orderId } })