This commit is contained in:
2026-08-27 10:44:14 +08:00
parent 040f73c73e
commit df4b7eba8e
9 changed files with 89 additions and 14 deletions
+32 -3
View File
@@ -1,6 +1,6 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import { coordinateCrossTabRefresh } from './crossTabRefresh.ts'
import { coordinateCrossTabRefresh, CrossTabRefreshTimeoutError } from './crossTabRefresh.ts'
test('reuses tokens rotated by another browser tab while waiting for the refresh lock', async () => {
let currentRefreshToken = 'refresh-old'
@@ -10,7 +10,7 @@ test('reuses tokens rotated by another browser tab while waiting for the refresh
configurable: true,
value: {
locks: {
async request(_name, callback) {
async request(_name, _options, callback) {
currentRefreshToken = 'refresh-new'
return callback()
},
@@ -41,7 +41,7 @@ test('refreshes normally when this tab owns the current refresh token', async ()
const originalNavigator = globalThis.navigator
Object.defineProperty(globalThis, 'navigator', {
configurable: true,
value: { locks: { request: async (_name, callback) => callback() } },
value: { locks: { request: async (_name, _options, callback) => callback() } },
})
try {
@@ -76,3 +76,32 @@ test('does not wait for rotation after the shared session was removed', async ()
Object.defineProperty(globalThis, 'navigator', { configurable: true, value: originalNavigator })
}
})
test('aborts a refresh lock that cannot be acquired', async () => {
const originalNavigator = globalThis.navigator
Object.defineProperty(globalThis, 'navigator', {
configurable: true,
value: {
locks: {
request(_name, options) {
return new Promise((_resolve, reject) => {
options.signal.addEventListener('abort', () => reject(options.signal.reason), { once: true })
})
},
},
},
})
try {
await assert.rejects(coordinateCrossTabRefresh({
lockName: 'client-refresh',
lockTimeoutMs: 5,
observedRefreshToken: 'refresh-old',
currentRefreshToken: () => 'refresh-old',
reuseCurrentSession: async () => 'unexpected',
refresh: async () => 'unexpected',
}), CrossTabRefreshTimeoutError)
} finally {
Object.defineProperty(globalThis, 'navigator', { configurable: true, value: originalNavigator })
}
})