fix(pda): leave expired sessions and clarify dispatch navigation
This commit is contained in:
@@ -22,7 +22,8 @@ function tokens(version = 'old') {
|
||||
|
||||
function prepareSession() {
|
||||
globalThis.uni = {
|
||||
setStorageSync() {}, getStorageSync() {}, removeStorageSync() {}
|
||||
setStorageSync() {}, getStorageSync() {}, removeStorageSync() {},
|
||||
launches: [], reLaunch(options) { this.launches.push(options.url) }
|
||||
}
|
||||
resetOverseasRequestStateForTest(); signOut()
|
||||
const value = tokens('old')
|
||||
@@ -114,6 +115,8 @@ test('request retries only once and preserves the final request and trace identi
|
||||
})
|
||||
assert.equal(refreshCount, 1)
|
||||
assert.equal(businessCalls, 2)
|
||||
assert.equal(session.token, '')
|
||||
assert.deepEqual(uni.launches, ['/pages/login/login'])
|
||||
})
|
||||
|
||||
test('refresh rejection clears every local token and returns the auth trace', async () => {
|
||||
@@ -133,8 +136,89 @@ 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.refreshToken, '')
|
||||
assert.deepEqual(uni.launches, ['/pages/login/login'])
|
||||
})
|
||||
|
||||
test('concurrent refresh rejection clears the session and navigates to login only once', async () => {
|
||||
prepareSession()
|
||||
let refreshCount = 0
|
||||
uni.request = (options) => {
|
||||
if (options.url.endsWith('/auth/refresh')) {
|
||||
refreshCount += 1
|
||||
return setTimeout(() => options.success(denied()), 5)
|
||||
}
|
||||
options.success(denied())
|
||||
}
|
||||
const results = await Promise.allSettled([overseasRequest('GET', '/one'), overseasRequest('GET', '/two')])
|
||||
assert.ok(results.every(result => result.status === 'rejected'))
|
||||
assert.equal(refreshCount, 1)
|
||||
assert.equal(session.currentUser, null)
|
||||
assert.deepEqual(uni.launches, ['/pages/login/login'])
|
||||
})
|
||||
|
||||
for (const refreshSucceeds of [true, false]) {
|
||||
test(`late refresh ${refreshSucceeds ? 'success' : 'failure'} cannot restore logout or clear a new login`, async () => {
|
||||
for (const relogin of [false, true]) {
|
||||
prepareSession()
|
||||
let completeRefresh
|
||||
let started
|
||||
const refreshStarted = new Promise(resolve => { started = resolve })
|
||||
let businessCalls = 0
|
||||
uni.request = (options) => {
|
||||
if (options.url.endsWith('/auth/refresh')) {
|
||||
completeRefresh = () => options.success(refreshSucceeds ? ok(tokens('late')) : denied())
|
||||
started()
|
||||
return
|
||||
}
|
||||
businessCalls += 1
|
||||
options.success(denied())
|
||||
}
|
||||
const pending = overseasRequest('POST', '/write')
|
||||
await refreshStarted
|
||||
signOut()
|
||||
const replacement = tokens('new-login')
|
||||
if (relogin) signInWithTokens(replacement)
|
||||
completeRefresh()
|
||||
await assert.rejects(pending)
|
||||
assert.equal(session.token, relogin ? replacement.accessToken : '')
|
||||
assert.equal(businessCalls, 1)
|
||||
assert.deepEqual(uni.launches, [])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
test('an old business response cannot replay with a newly logged-in session', async () => {
|
||||
prepareSession()
|
||||
let complete
|
||||
let calls = 0
|
||||
uni.request = options => { calls += 1; complete = () => options.success(denied()) }
|
||||
const pending = overseasRequest('POST', '/write')
|
||||
signOut()
|
||||
const replacement = tokens('another-login')
|
||||
signInWithTokens(replacement)
|
||||
complete()
|
||||
await assert.rejects(pending, /会话已变更/)
|
||||
assert.equal(calls, 1)
|
||||
assert.equal(session.token, replacement.accessToken)
|
||||
assert.deepEqual(uni.launches, [])
|
||||
})
|
||||
|
||||
for (const kind of ['upload', 'download']) {
|
||||
test(`${kind} final unauthorized response navigates once and stops after one replay`, async () => {
|
||||
prepareSession()
|
||||
let refreshCount = 0
|
||||
let calls = 0
|
||||
uni.request = options => { refreshCount += 1; options.success(ok(tokens(kind))) }
|
||||
uni.uploadFile = options => { calls += 1; options.success({ ...denied(), data: JSON.stringify(denied().data) }) }
|
||||
uni.downloadFile = options => { calls += 1; options.success(denied()) }
|
||||
await assert.rejects(kind === 'upload' ? overseasUpload('/photo', '/tmp/photo.jpg') : overseasDownload('/photo'))
|
||||
assert.equal(refreshCount, 1)
|
||||
assert.equal(calls, 2)
|
||||
assert.equal(session.currentUser, null)
|
||||
assert.deepEqual(uni.launches, ['/pages/login/login'])
|
||||
})
|
||||
}
|
||||
|
||||
test('photo upload refreshes and replays once while retaining the file payload', async () => {
|
||||
const old = prepareSession(); const next = tokens('upload')
|
||||
let refreshCount = 0; const uploads = []
|
||||
|
||||
Reference in New Issue
Block a user