Files
baiyue-firstleg/scripts/admin-test-env-modules/pda-picker-fixture-contract.test.mjs
T
2026-10-22 11:56:47 +08:00

98 lines
5.5 KiB
JavaScript

import assert from 'node:assert/strict'
import test from 'node:test'
import { pickerFixtureSuffix, createPicker, disablePicker, pickerSessionHandoff } from './phase4-overseas-pda.mjs'
import { verifyPdaWarehouseRevocation } from './unified-pda-session-revocation.mjs'
test('fixed PDA actors are leased separately and only logged out, never modified', async () => {
const actors = ['11', '12'].map(membershipId => ({ membershipId, warehouseId: '91', username: `test-${membershipId}`, password: 'fixture-only' }))
const calls = [], gaps = []
const context = { options: { fixedCustomerId: '101', fixedPdaActors: actors },
async request(method, path, body) {
calls.push({ method, path })
let data = {}
if (path.includes('/iam/users?')) data = { total: 2, records: actors.map(actor => ({ ...actor,
overseasWarehouseId: '91', tenantId: '0', status: 'ACTIVE', roleIds: ['7'] })) }
if (path === '/api/auth/login') data = { accessToken: body.loginId, refreshToken: `refresh-${body.loginId}` }
return { http: 200, body: { code: '200', data } }
},
apiSuccess(_module, _name, result) { return result.body.data }, addStep() {},
addCoverageGap(...args) { gaps.push(args) },
}
const primary = await createPicker(context, 1, '91', { role: { id: '7' } })
const secondary = await createPicker(context, 2, '91', { role: { id: '7' } })
assert.notEqual(primary.membershipId, secondary.membershipId)
assert.equal(await createPicker(context, 3, '91', { role: { id: '7' } }), null)
const handoff = pickerSessionHandoff(primary)
assert.equal(handoff.reused, true)
assert.equal(handoff.password, undefined)
await verifyPdaWarehouseRevocation(context, handoff)
assert.equal(gaps.length, 1)
await disablePicker(context, handoff, 1)
await disablePicker(context, primary, 1)
await disablePicker(context, secondary, 2)
assert.equal(context.fixedPdaLeases.size, 0)
assert.equal(calls.filter(call => call.path === '/api/auth/logout').length, 2)
assert.ok(calls.every(call => call.method === 'GET' || ['/api/auth/login', '/api/auth/logout'].includes(call.path)))
})
test('missing fixed PDA pool stops before network or phone allocation', async () => {
const context = { options: { fixedCustomerId: '101' }, addStep() {},
request() { throw Error('unexpected HTTP') }, findAvailablePhone() { throw Error('unexpected allocation') } }
assert.equal(await createPicker(context, 1, '91', { role: { id: '7' } }), null)
})
test('failed PDA projection checks logout and retains leases on rejected or uncertain logout', async () => {
for (const logout of ['success', 'rejected', 'throws']) {
const actor = { membershipId: '11', warehouseId: '91', username: 'fixture-actor', password: 'fixture-only' }
const steps = [], calls = []
const context = { options: { fixedCustomerId: '101', fixedPdaActors: [actor] },
async request(method, path) {
calls.push(path)
if (path.includes('/iam/users?')) return { body: { data: { records: [{ ...actor,
overseasWarehouseId: '91', tenantId: '0', status: 'ACTIVE', roleIds: ['7'] }] } } }
if (path === '/api/auth/login') return { body: { data: { accessToken: 'fixture-token', refreshToken: 'fixture-refresh' } } }
if (path === '/api/auth/logout' && logout === 'throws') throw Error('network failure')
return { http: 200, body: { code: path === '/api/auth/logout' && logout === 'success' ? '200' : 'A0403' } }
},
apiSuccess(_module, _name, response) { return response.body.data },
addStep(...args) { steps.push(args) },
}
assert.equal(await createPicker(context, 1, '91', { role: { id: '7' } }), null)
assert.equal(calls.filter(path => path === '/api/auth/logout').length, 1)
assert.equal(context.fixedPdaLeases.has('11'), logout !== 'success')
assert.ok(steps.some(step => step[1].includes('核验失败') && step[3] === false))
if (logout !== 'success') {
assert.ok(steps.some(step => step[1].includes('退出固定PDA') && step[3] === false))
const before = calls.length
assert.equal(await createPicker(context, 2, '91', { role: { id: '7' } }), null)
assert.equal(calls.length, before)
}
}
})
test('logout exception does not prevent remaining fixed actors from being cleaned up', async () => {
const calls = []
const context = { fixedPdaLeases: new Set(['11', '12']), addStep() {},
async request(_method, _path, body) {
calls.push(body.refreshToken)
if (body.refreshToken === 'first') throw Error('network failure')
return { http: 200, body: { code: '200' } }
} }
await disablePicker(context, { membershipId: '11', refreshToken: 'first', reused: true }, 1)
await disablePicker(context, { membershipId: '12', refreshToken: 'second', reused: true }, 2)
assert.deepEqual(calls, ['first', 'second'])
assert.deepEqual([...context.fixedPdaLeases], ['11'])
})
test('长场景尾部相同时PDA账号仍保留完整运行标识的区别', () => {
const first = { marker: 'ADMIN-API-20261005134027677-COMPLETED-F40-PDA' }
const second = { marker: 'ADMIN-API-20261005170914227-COMPLETED-F40-PDA' }
const oldSuffix = value => value.marker.replace(/[^A-Za-z0-9]/g, '').slice(-16)
assert.equal(oldSuffix(first), oldSuffix(second))
assert.notEqual(pickerFixtureSuffix(first), pickerFixtureSuffix(second))
assert.equal(pickerFixtureSuffix(first), pickerFixtureSuffix({ ...first }))
assert.match(pickerFixtureSuffix(first), /^[0-9A-F]{32}$/)
assert.notEqual(pickerFixtureSuffix(first), pickerFixtureSuffix({ ...first, pdaContainerSuffix: 'B' }))
assert.throws(() => pickerFixtureSuffix({ marker: '' }))
})