优化
This commit is contained in:
@@ -55,7 +55,7 @@ test('fulfillment API uses dedicated PDA routes and never sends customer or ware
|
||||
|
||||
test('fulfillment API calls the frozen list detail pick complete and depart routes with exact payloads', async () => {
|
||||
const calls = []
|
||||
session.apiBase = '/overseas-api'; session.token = 'overseas-token'
|
||||
session.apiBase = '/api'; session.token = 'overseas-token'
|
||||
globalThis.uni = {
|
||||
request(options) {
|
||||
calls.push({ method: options.method, url: options.url, data: options.data, authorization: options.header.Authorization })
|
||||
|
||||
@@ -15,7 +15,6 @@ function tokens(version = 'old') {
|
||||
const base = { user_id: 42, membership_id: 84, tenant_id: 0, app_code: 'PLATFORM', overseas_warehouse_id: 900, exp: future(), version }
|
||||
return {
|
||||
accessToken: jwt({ ...base, username: 'picker-42' }),
|
||||
overseasAccessToken: jwt(base),
|
||||
refreshToken: `refresh-${version}`,
|
||||
roles: [PDA_ROLE], authorities: [PDA_AUTHORITY]
|
||||
}
|
||||
@@ -43,7 +42,7 @@ test('H5 proxy and App API roots normalize request upload and download paths to
|
||||
globalThis.uni.uploadFile = (options) => { uploads.push(options.url); options.success({ statusCode: 200, data: JSON.stringify({ code: '200', data: { fileId: '1' } }), header: {} }) }
|
||||
globalThis.uni.downloadFile = (options) => { downloads.push(options.url); options.success({ statusCode: 200, tempFilePath: '/tmp/photo.jpg', header: {} }) }
|
||||
|
||||
session.apiBase = '/overseas-api'
|
||||
session.apiBase = '/api'
|
||||
await resolvePallet('PLT-1')
|
||||
await overseasUpload('/api/pda/v1/last-leg/photos/files', '/tmp/photo.jpg')
|
||||
await overseasDownload('/api/pda/v1/last-leg/photos/files/1/download')
|
||||
@@ -54,15 +53,15 @@ test('H5 proxy and App API roots normalize request upload and download paths to
|
||||
await overseasDownload('/api/pda/v1/last-leg/photos/files/1/download')
|
||||
|
||||
assert.deepEqual(requests, [
|
||||
'/overseas-api/pda/v1/last-leg/pallet-operations/pallets/resolve',
|
||||
'/api/pda/v1/last-leg/pallet-operations/pallets/resolve',
|
||||
'https://overseas.example.com/api/pda/v1/last-leg/pallet-operations/pallets/resolve'
|
||||
])
|
||||
assert.deepEqual(uploads, [
|
||||
'/overseas-api/pda/v1/last-leg/photos/files',
|
||||
'/api/pda/v1/last-leg/photos/files',
|
||||
'https://overseas.example.com/api/pda/v1/last-leg/photos/files'
|
||||
])
|
||||
assert.deepEqual(downloads, [
|
||||
'/overseas-api/pda/v1/last-leg/photos/files/1/download',
|
||||
'/api/pda/v1/last-leg/photos/files/1/download',
|
||||
'https://overseas.example.com/api/pda/v1/last-leg/photos/files/1/download'
|
||||
])
|
||||
for (const url of [...requests, ...uploads, ...downloads]) assert.doesNotMatch(url, /\/api\/api\//)
|
||||
@@ -76,11 +75,11 @@ test('concurrent old-token 401 responses share one refresh and each replay once
|
||||
refreshCount += 1
|
||||
return setTimeout(() => options.success(ok(next)), 10)
|
||||
}
|
||||
if (options.header.Authorization === `Bearer ${old.overseasAccessToken}`) {
|
||||
if (options.header.Authorization === `Bearer ${old.accessToken}`) {
|
||||
oldCalls += 1
|
||||
return setTimeout(() => options.success(denied()), oldCalls === 1 ? 0 : 30)
|
||||
}
|
||||
assert.equal(options.header.Authorization, `Bearer ${next.overseasAccessToken}`)
|
||||
assert.equal(options.header.Authorization, `Bearer ${next.accessToken}`)
|
||||
newCalls += 1
|
||||
setTimeout(() => options.success(ok({ call: newCalls })), 0)
|
||||
}
|
||||
@@ -92,8 +91,7 @@ test('concurrent old-token 401 responses share one refresh and each replay once
|
||||
assert.equal(refreshCount, 1)
|
||||
assert.equal(oldCalls, 2)
|
||||
assert.equal(newCalls, 2)
|
||||
assert.equal(session.token, next.overseasAccessToken)
|
||||
assert.equal(session.domesticToken, next.accessToken)
|
||||
assert.equal(session.token, next.accessToken)
|
||||
})
|
||||
|
||||
test('request retries only once and preserves the final request and trace identifiers', async () => {
|
||||
@@ -134,7 +132,6 @@ test('refresh rejection clears every local token and returns the auth trace', as
|
||||
})
|
||||
assert.equal(session.currentUser, null)
|
||||
assert.equal(session.token, '')
|
||||
assert.equal(session.domesticToken, '')
|
||||
assert.equal(session.refreshToken, '')
|
||||
})
|
||||
|
||||
@@ -148,7 +145,7 @@ test('photo upload refreshes and replays once while retaining the file payload',
|
||||
}
|
||||
globalThis.uni.uploadFile = (options) => {
|
||||
uploads.push({ filePath: options.filePath, name: options.name, authorization: options.header.Authorization })
|
||||
if (options.header.Authorization === `Bearer ${old.overseasAccessToken}`) {
|
||||
if (options.header.Authorization === `Bearer ${old.accessToken}`) {
|
||||
return options.success({ statusCode: 401, data: JSON.stringify({ code: 'A0401', requestId: 'upload-old', traceId: 'upload-old-trace' }), header: {} })
|
||||
}
|
||||
options.success({ statusCode: 200, data: JSON.stringify({ code: '200', data: { fileId: '99' } }), header: {} })
|
||||
@@ -178,11 +175,11 @@ test('H5 blob photo uses authenticated multipart fetch instead of uni.uploadFile
|
||||
return new Response(JSON.stringify({ code: '200', data: { fileId: 'h5-1' } }), { status: 200 })
|
||||
}
|
||||
try {
|
||||
session.apiBase = '/overseas-api'
|
||||
session.apiBase = '/api'
|
||||
assert.deepEqual(await overseasUpload('/api/pda/v1/last-leg/photos/files', 'blob:http://localhost/photo'), { fileId: 'h5-1' })
|
||||
assert.equal(uploadFileCalls, 0)
|
||||
assert.equal(uploaded.url, '/overseas-api/pda/v1/last-leg/photos/files')
|
||||
assert.equal(uploaded.options.headers.Authorization, `Bearer ${value.overseasAccessToken}`)
|
||||
assert.equal(uploaded.url, '/api/pda/v1/last-leg/photos/files')
|
||||
assert.equal(uploaded.options.headers.Authorization, `Bearer ${value.accessToken}`)
|
||||
assert.ok(uploaded.options.body instanceof FormData)
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch
|
||||
@@ -203,14 +200,14 @@ test('H5 File photo is appended directly without rereading a temporary blob URL'
|
||||
let calls = 0
|
||||
globalThis.uni.uploadFile = (options) => {
|
||||
calls += 1
|
||||
assert.equal(options.url, '/overseas-api/pda/photo')
|
||||
assert.equal(options.header.Authorization, `Bearer ${value.overseasAccessToken}`)
|
||||
assert.equal(options.url, '/api/pda/photo')
|
||||
assert.equal(options.header.Authorization, `Bearer ${value.accessToken}`)
|
||||
assert.equal(options.files[0].file, selected)
|
||||
assert.equal(options.files[0].name, 'file')
|
||||
options.success({ statusCode: 200, data: JSON.stringify({ code: '200', data: { fileId: 'direct-file' } }), header: {} })
|
||||
}
|
||||
try {
|
||||
session.apiBase = '/overseas-api'
|
||||
session.apiBase = '/api'
|
||||
assert.deepEqual(await overseasUpload('/pda/photo', selected), { fileId: 'direct-file' })
|
||||
assert.equal(calls, 1)
|
||||
} finally {
|
||||
@@ -225,13 +222,13 @@ test('native file runtime rejects an H5-relative overseas root before upload wit
|
||||
prepareSession()
|
||||
const originalLocation = globalThis.location
|
||||
globalThis.location = { protocol: 'file:' }
|
||||
session.apiBase = '/overseas-api'
|
||||
session.apiBase = '/api'
|
||||
let uploadCalls = 0
|
||||
globalThis.uni.uploadFile = () => { uploadCalls += 1 }
|
||||
try {
|
||||
await assert.rejects(overseasUpload('/pda/photo', '_doc/photo.jpg'), (error) => {
|
||||
assert.equal(error.code, 'PDA_OVERSEAS_API_BASE_REQUIRED')
|
||||
assert.match(error.message, /VITE_OVERSEAS_API_BASE_URL/)
|
||||
assert.equal(error.code, 'PDA_API_BASE_REQUIRED')
|
||||
assert.match(error.message, /VITE_API_BASE_URL/)
|
||||
assert.match(error.message, /HTTP\(S\)/)
|
||||
return true
|
||||
})
|
||||
@@ -270,8 +267,8 @@ test('authenticated photo download maps the public api path through the H5 overs
|
||||
}
|
||||
assert.equal(await overseasDownload('/api/pda/v1/last-leg/receiving-containers/current/unload-photos/files/99/download'), '/tmp/restored.jpg')
|
||||
assert.equal(calls.length, 1)
|
||||
assert.equal(calls[0].url, '/overseas-api/pda/v1/last-leg/receiving-containers/current/unload-photos/files/99/download')
|
||||
assert.equal(calls[0].header.Authorization, `Bearer ${value.overseasAccessToken}`)
|
||||
assert.equal(calls[0].url, '/api/pda/v1/last-leg/receiving-containers/current/unload-photos/files/99/download')
|
||||
assert.equal(calls[0].header.Authorization, `Bearer ${value.accessToken}`)
|
||||
})
|
||||
|
||||
test('native image download accepts a valid temp file when App-Plus omits response headers', async () => {
|
||||
|
||||
@@ -14,9 +14,8 @@ test('overseas PDA is an independent Vue 3 app with separate packaging identity'
|
||||
assert.doesNotMatch(pages, /pages\/picking|pages\/tasks/)
|
||||
assert.match(pkg, /"build:app"\s*:\s*"uni build -p app"/)
|
||||
assert.match(vite, /API_PROXY_TARGET/)
|
||||
assert.match(vite, /OVERSEAS_API_PROXY_TARGET/)
|
||||
assert.match(vite, /\/overseas-api/)
|
||||
assert.match(vite, /replace\(\/\^\\\/overseas-api\//)
|
||||
assert.doesNotMatch(vite, /OVERSEAS_API_PROXY_TARGET|gateway-overseas/)
|
||||
assert.match(vite, /\/api/)
|
||||
assert.match(vite, /removeHeader\('origin'\)/)
|
||||
assert.match(vite, /removeHeader\('referer'\)/)
|
||||
assert.match(vite, /__VUE_I18N_FULL_INSTALL__:\s*true/)
|
||||
@@ -35,7 +34,7 @@ test('overseas workbench contains confirmed warehouse and phase-five outbound do
|
||||
for (const label of ['入仓作业', '拆柜收货', '退货入库', '退货合箱', '出库履约', '排车拣货', '出库作业', '库内作业', '换标作业', '现场查询']) assert.match(data, new RegExp(label))
|
||||
for (const excluded of ['集装箱拣货', '装柜取证', '海外派送', '派送单', '客户回访']) assert.doesNotMatch(data, new RegExp(excluded))
|
||||
assert.match(session, /overseas-pda-auth-session/)
|
||||
assert.match(runtime, /VITE_OVERSEAS_API_BASE_URL/)
|
||||
assert.match(runtime, /VITE_API_BASE_URL/)
|
||||
})
|
||||
|
||||
test('login has no committed demo credentials and inbound operation implements the locked container flow', async () => {
|
||||
@@ -49,9 +48,9 @@ test('login has no committed demo credentials and inbound operation implements t
|
||||
assert.match(auth, /authRequest\('\/auth\/login', \{ loginId, password \}\)/)
|
||||
assert.doesNotMatch(auth, /authRequest\('\/auth\/login', \{ username, password \}\)/)
|
||||
assert.match(runtime, /runtimeEnvironment\.DEV|VITE_PDA_DEV_USERNAME|VITE_PDA_DEV_PASSWORD/)
|
||||
assert.match(runtime, /'\/overseas-api'/)
|
||||
assert.match(runtime, /#ifdef H5[\s\S]*?VITE_H5_AUTH_API_BASE_URL/)
|
||||
assert.match(runtime, /#ifdef H5[\s\S]*?VITE_H5_OVERSEAS_API_BASE_URL/)
|
||||
assert.match(runtime, /'\/api'/)
|
||||
assert.match(runtime, /#ifdef H5[\s\S]*?VITE_H5_API_BASE_URL/)
|
||||
assert.match(runtime, /#ifdef H5[\s\S]*?VITE_H5_API_BASE_URL/)
|
||||
assert.match(operation, /startScanBroadcast/)
|
||||
assert.match(operation, /palletWorkflowActive = Boolean\(this\.activeInbound \|\| this\.resolvedPallet\)/)
|
||||
assert.match(operation, /queuedTarget: scan\.target/)
|
||||
|
||||
@@ -13,16 +13,16 @@ test('packing preview keeps customer scope internal and exposes stable order ref
|
||||
|
||||
test('return packing API uses exact PDA inventory routes without submitting customer or warehouse scope', async () => {
|
||||
const calls = []
|
||||
session.apiBase = '/overseas-api'; session.token = 'packing-token'
|
||||
session.apiBase = '/api'; session.token = 'packing-token'
|
||||
globalThis.uni = { request(options) { calls.push({ method: options.method, url: options.url, data: options.data, authorization: options.header.Authorization }); let data = {}; if (options.url.includes('/packing/orders')) data = { customerId: '100', orders: [], skus: [] }; else if (options.url.endsWith('/cartons/merge')) data = { cartonMark: 'RC-1', skuCode: 'SKU-1', totalQuantity: 2, label: { cartonMark: 'RC-1', items: [] } }; else data = { cartonMark: 'RC/1', totalQuantity: 2, items: [] }; options.success({ statusCode: 200, data: { code: '200', data }, header: {} }) } }
|
||||
await findPackingOrders('RT/1')
|
||||
const request = { returnNos: ['RT/1'], items: [{ skuCode: 'SKU-1', quantity: 2 }], length: 10, width: 20, height: 30, weight: 4, idempotencyKey: 'merge-key' }
|
||||
await mergeReturnCartons(request)
|
||||
await getReturnCartonLabel('RC/1')
|
||||
assert.deepEqual(calls.map((item) => item.url), [
|
||||
'/overseas-api/pda/v1/last-leg/return-inventory/packing/orders?returnNo=RT%2F1',
|
||||
'/overseas-api/pda/v1/last-leg/return-inventory/cartons/merge',
|
||||
'/overseas-api/pda/v1/last-leg/return-inventory/cartons/RC%2F1/label'
|
||||
'/api/pda/v1/last-leg/return-inventory/packing/orders?returnNo=RT%2F1',
|
||||
'/api/pda/v1/last-leg/return-inventory/cartons/merge',
|
||||
'/api/pda/v1/last-leg/return-inventory/cartons/RC%2F1/label'
|
||||
])
|
||||
assert.deepEqual(calls[1].data, request)
|
||||
assert.ok(calls.every((item) => item.authorization === 'Bearer packing-token'))
|
||||
@@ -30,13 +30,13 @@ test('return packing API uses exact PDA inventory routes without submitting cust
|
||||
})
|
||||
|
||||
test('return packing lookup treats not found as an empty preview', async () => {
|
||||
session.apiBase = '/overseas-api'; session.token = 'packing-token'
|
||||
session.apiBase = '/api'; session.token = 'packing-token'
|
||||
globalThis.uni = { request(options) { options.success({ statusCode: 404, data: { code: 'A0404', message: '没有匹配的可合箱库存' }, header: {} }) } }
|
||||
assert.deepEqual(await findPackingOrders('MISSING-RETURN'), { customerKey: '', orders: [], skus: [] })
|
||||
})
|
||||
|
||||
test('return packing lookup still rejects real service errors', async () => {
|
||||
session.apiBase = '/overseas-api'; session.token = 'packing-token'
|
||||
session.apiBase = '/api'; session.token = 'packing-token'
|
||||
globalThis.uni = { request(options) { options.success({ statusCode: 500, data: { code: 'B0500', message: '服务暂不可用' }, header: {} }) } }
|
||||
await assert.rejects(() => findPackingOrders('RT-1'), (error) => error.code === 'B0500')
|
||||
})
|
||||
|
||||
@@ -24,7 +24,7 @@ test('confirmed receipt normalization preserves generated carton and batch refer
|
||||
|
||||
test('return receipt API calls only the dedicated PDA routes and preserves the receipt payload', async () => {
|
||||
const calls = []
|
||||
session.apiBase = '/overseas-api'; session.token = 'return-token'
|
||||
session.apiBase = '/api'; session.token = 'return-token'
|
||||
globalThis.uni = {
|
||||
request(options) {
|
||||
calls.push({ method: options.method, url: options.url, data: options.data, authorization: options.header.Authorization })
|
||||
@@ -40,9 +40,9 @@ test('return receipt API calls only the dedicated PDA routes and preserves the r
|
||||
const confirm = { returnNo: 'RT-001', trackingNo: 'TRACK-1', startedBy: 'SKU', palletCode: 'PLT-1', items: [{ skuCode: 'SKU-1', receivedQuantity: 2 }], idempotencyKey: 'return-key' }
|
||||
await confirmReturnReceipt(confirm)
|
||||
assert.deepEqual(calls.map((item) => item.url), [
|
||||
'/overseas-api/pda/v1/last-leg/return-receipts/start',
|
||||
'/overseas-api/pda/v1/last-leg/return-receipts/sku-candidates',
|
||||
'/overseas-api/pda/v1/last-leg/return-receipts/confirm'
|
||||
'/api/pda/v1/last-leg/return-receipts/start',
|
||||
'/api/pda/v1/last-leg/return-receipts/sku-candidates',
|
||||
'/api/pda/v1/last-leg/return-receipts/confirm'
|
||||
])
|
||||
assert.deepEqual(calls[0].data, { returnNo: 'RT-001' })
|
||||
assert.deepEqual(calls[1].data, { skuCode: 'SKU-1' })
|
||||
@@ -52,7 +52,7 @@ test('return receipt API calls only the dedicated PDA routes and preserves the r
|
||||
})
|
||||
|
||||
test('return receipt lookup treats A0404 and HTTP 404 as empty results', async () => {
|
||||
session.apiBase = '/overseas-api'; session.token = 'return-token'
|
||||
session.apiBase = '/api'; session.token = 'return-token'
|
||||
let requestCount = 0
|
||||
globalThis.uni = {
|
||||
request(options) {
|
||||
@@ -66,7 +66,7 @@ test('return receipt lookup treats A0404 and HTTP 404 as empty results', async (
|
||||
})
|
||||
|
||||
test('return receipt lookup still rejects real service errors', async () => {
|
||||
session.apiBase = '/overseas-api'; session.token = 'return-token'
|
||||
session.apiBase = '/api'; session.token = 'return-token'
|
||||
globalThis.uni = {
|
||||
request(options) {
|
||||
options.success({ statusCode: 500, data: { code: 'B0500', message: '服务暂不可用' }, header: {} })
|
||||
|
||||
+37
-20
@@ -3,7 +3,7 @@ import assert from 'node:assert/strict'
|
||||
|
||||
import {
|
||||
PDA_AUTHORITY, PDA_ROLE, applyWarehouseProfile, replaceTokens, restoreSession, session,
|
||||
decodeAccessToken, signInWithTokens, signOut, validatePdaTokenPair
|
||||
decodeAccessToken, signInWithTokens, signOut, validatePdaToken
|
||||
} from '../src/stores/session.js'
|
||||
import { receiving } from '../src/stores/receiving.js'
|
||||
import { fulfillment, restoreFulfillmentDraft, updateFulfillmentDraft } from '../src/stores/fulfillment.js'
|
||||
@@ -24,9 +24,9 @@ function tokenPair(overrides = {}) {
|
||||
overseas_warehouse_id: 900, exp: future()
|
||||
}
|
||||
const domestic = { ...base, username: 'picker-42', ...(overrides.domestic || {}) }
|
||||
const overseas = { ...base, ...(overrides.overseas || {}) }
|
||||
|
||||
return {
|
||||
accessToken: jwt(domestic), overseasAccessToken: jwt(overseas),
|
||||
accessToken: jwt(domestic),
|
||||
refreshToken: overrides.refreshToken ?? 'refresh-42',
|
||||
roles: Object.hasOwn(overrides, 'roles') ? overrides.roles : [PDA_ROLE],
|
||||
authorities: Object.hasOwn(overrides, 'authorities') ? overrides.authorities : [PDA_AUTHORITY]
|
||||
@@ -46,17 +46,17 @@ function storage() {
|
||||
test('valid platform warehouse picker tokens freeze the same operator and warehouse', () => {
|
||||
storage(); signOut()
|
||||
const tokens = tokenPair()
|
||||
const pair = validatePdaTokenPair(tokens.accessToken, tokens.overseasAccessToken, tokens)
|
||||
const pair = validatePdaToken(tokens.accessToken, tokens)
|
||||
assert.equal(pair.warehouseId, '900')
|
||||
const user = signInWithTokens(tokens)
|
||||
assert.deepEqual(user.roles, [PDA_ROLE])
|
||||
assert.equal(user.warehouseId, '900')
|
||||
assert.equal(user.warehouse, '')
|
||||
assert.equal(session.tenantId, '0')
|
||||
assert.equal(session.token, tokens.overseasAccessToken)
|
||||
assert.equal(session.token, tokens.accessToken)
|
||||
assert.deepEqual(session.authorization, { roles: [PDA_ROLE], authorities: [PDA_AUTHORITY] })
|
||||
assert.equal(decodeAccessToken(tokens.overseasAccessToken).roles, undefined)
|
||||
assert.equal(decodeAccessToken(tokens.overseasAccessToken).authorities, undefined)
|
||||
assert.equal(decodeAccessToken(tokens.accessToken).roles, undefined)
|
||||
assert.equal(decodeAccessToken(tokens.accessToken).authorities, undefined)
|
||||
})
|
||||
|
||||
test('JWT decoding preserves unquoted snowflake subjects and warehouse IDs exactly', () => {
|
||||
@@ -81,11 +81,11 @@ test('login fails closed for wrong role, authority, warehouse, application, or t
|
||||
tokenPair({ authorities: [] }),
|
||||
tokenPair({ roles: null }),
|
||||
tokenPair({ authorities: null }),
|
||||
tokenPair({ overseas: { overseas_warehouse_id: null } }),
|
||||
tokenPair({ domestic: { overseas_warehouse_id: null } }),
|
||||
tokenPair({ domestic: { app_code: 'MERCHANT', tenant_id: 100 } }),
|
||||
tokenPair({ overseas: { user_id: 99 } }),
|
||||
tokenPair({ overseas: { membership_id: 99 } }),
|
||||
tokenPair({ overseas: { overseas_warehouse_id: 901 } })
|
||||
tokenPair({ domestic: { user_id: null } }),
|
||||
tokenPair({ domestic: { membership_id: null } }),
|
||||
tokenPair({ domestic: { exp: null } })
|
||||
]
|
||||
for (const tokens of invalidPairs) {
|
||||
assert.throws(() => signInWithTokens(tokens))
|
||||
@@ -93,22 +93,40 @@ test('login fails closed for wrong role, authority, warehouse, application, or t
|
||||
}
|
||||
})
|
||||
|
||||
test('refresh replacement validates both tokens before atomically changing session', () => {
|
||||
test('refresh replacement validates the unified token before atomically changing session', () => {
|
||||
storage(); signOut()
|
||||
const original = tokenPair()
|
||||
signInWithTokens(original)
|
||||
const before = { domestic: session.domesticToken, overseas: session.token, refresh: session.refreshToken }
|
||||
const before = { access: session.token, refresh: session.refreshToken }
|
||||
const invalid = tokenPair({ authorities: [], refreshToken: 'invalid-refresh' })
|
||||
assert.throws(() => replaceTokens(invalid), /lastleg:pda:operate/)
|
||||
assert.deepEqual({ domestic: session.domesticToken, overseas: session.token, refresh: session.refreshToken }, before)
|
||||
assert.deepEqual({ access: session.token, refresh: session.refreshToken }, before)
|
||||
|
||||
const replacement = tokenPair({ refreshToken: 'refresh-43' })
|
||||
replaceTokens(replacement)
|
||||
assert.equal(session.domesticToken, replacement.accessToken)
|
||||
assert.equal(session.token, replacement.overseasAccessToken)
|
||||
assert.equal(session.token, replacement.accessToken)
|
||||
assert.equal(session.refreshToken, 'refresh-43')
|
||||
})
|
||||
|
||||
test('refresh cannot carry an old operation into another user or warehouse', () => {
|
||||
storage(); signOut(); signInWithTokens(tokenPair())
|
||||
const original = session.token
|
||||
for (const domestic of [{ user_id: 99 }, { overseas_warehouse_id: 901 }]) {
|
||||
assert.throws(() => replaceTokens(tokenPair({ domestic })), /主体或绑定仓库已变更/)
|
||||
assert.equal(session.token, original)
|
||||
assert.equal(session.currentUser.warehouseId, '900')
|
||||
}
|
||||
})
|
||||
|
||||
test('legacy dual-token storage requires a fresh login', () => {
|
||||
const values = storage(); signOut(); signInWithTokens(tokenPair())
|
||||
const saved = structuredClone([...values.values()][0])
|
||||
delete saved.version
|
||||
signOut(); values.set('overseas-pda-auth-session', saved)
|
||||
assert.equal(restoreSession(), false)
|
||||
assert.equal(session.currentUser, null)
|
||||
})
|
||||
|
||||
test('restore rebuilds identity from claims and removes expired or tampered storage', () => {
|
||||
const values = storage(); signOut()
|
||||
signInWithTokens(tokenPair())
|
||||
@@ -123,14 +141,14 @@ test('restore rebuilds identity from claims and removes expired or tampered stor
|
||||
assert.equal(restoreSession(), false)
|
||||
assert.equal(values.has('overseas-pda-auth-session'), false)
|
||||
|
||||
const tampered = { ...saved, token: tokenPair({ overseas: { overseas_warehouse_id: 901 } }).overseasAccessToken }
|
||||
const tampered = { ...saved, token: tokenPair({ domestic: { overseas_warehouse_id: 901 } }).accessToken }
|
||||
signOut(); values.set('overseas-pda-auth-session', tampered)
|
||||
assert.equal(restoreSession(), false)
|
||||
assert.equal(session.currentUser, null)
|
||||
assert.equal(values.has('overseas-pda-auth-session'), false)
|
||||
|
||||
const expired = tokenPair({ domestic: { exp: 1 }, overseas: { exp: 1 } })
|
||||
values.set('overseas-pda-auth-session', { ...saved, domesticToken: expired.accessToken, token: expired.overseasAccessToken })
|
||||
const expired = tokenPair({ domestic: { exp: 1 } })
|
||||
values.set('overseas-pda-auth-session', { ...saved, token: expired.accessToken })
|
||||
assert.equal(restoreSession(), false)
|
||||
assert.equal(values.has('overseas-pda-auth-session'), false)
|
||||
})
|
||||
@@ -152,7 +170,6 @@ test('sign out clears tokens, frozen warehouse identity, and every receiving or
|
||||
signOut()
|
||||
assert.equal(session.currentUser, null)
|
||||
assert.equal(session.token, '')
|
||||
assert.equal(session.domesticToken, '')
|
||||
assert.equal(session.refreshToken, '')
|
||||
assert.deepEqual(session.authorization, { roles: [], authorities: [] })
|
||||
assert.equal(values.has('overseas-pda-auth-session'), false)
|
||||
|
||||
@@ -20,7 +20,7 @@ const read = (path) => readFile(new URL(path, import.meta.url), 'utf8')
|
||||
|
||||
function requestMock() {
|
||||
const calls = []
|
||||
session.apiBase = '/overseas-api'; session.token = 'warehouse-token'
|
||||
session.apiBase = '/api'; session.token = 'warehouse-token'
|
||||
globalThis.uni = {
|
||||
request(options) {
|
||||
calls.push({ method: options.method, url: options.url, data: options.data, authorization: options.header.Authorization })
|
||||
|
||||
Reference in New Issue
Block a user