Files
admin-web/src/core/auth/crossTabRefresh.test.ts
T

84 lines
2.8 KiB
TypeScript
Raw Normal View History

2026-08-07 14:47:11 +08:00
import { afterEach, describe, expect, it, vi } from 'vitest'
2026-08-27 10:44:12 +08:00
import { coordinateCrossTabRefresh, CrossTabRefreshTimeoutError } from './crossTabRefresh'
2026-08-07 14:47:11 +08:00
afterEach(() => vi.unstubAllGlobals())
describe('cross-tab refresh coordination', () => {
it('reuses the rotated session after waiting for another tab', async () => {
let currentRefreshToken = 'refresh-old'
const refresh = vi.fn()
const reuseCurrentSession = vi.fn(async () => 'access-new')
vi.stubGlobal('navigator', {
locks: {
2026-08-27 10:44:12 +08:00
request: vi.fn(async (_name: string, _options: LockOptions, callback: () => Promise<string>) => {
2026-08-07 14:47:11 +08:00
currentRefreshToken = 'refresh-new'
return callback()
}),
},
})
await expect(coordinateCrossTabRefresh({
lockName: 'admin-refresh',
observedRefreshToken: 'refresh-old',
currentRefreshToken: () => currentRefreshToken,
reuseCurrentSession,
refresh,
})).resolves.toBe('access-new')
expect(reuseCurrentSession).toHaveBeenCalledOnce()
expect(refresh).not.toHaveBeenCalled()
})
it('performs the refresh when the token has not changed', async () => {
const refresh = vi.fn(async () => 'access-new')
vi.stubGlobal('navigator', {
2026-08-27 10:44:12 +08:00
locks: { request: vi.fn(async (_name: string, _options: LockOptions, callback: () => Promise<string>) => callback()) },
2026-08-07 14:47:11 +08:00
})
await expect(coordinateCrossTabRefresh({
lockName: 'admin-refresh',
observedRefreshToken: 'refresh-old',
currentRefreshToken: () => 'refresh-old',
reuseCurrentSession: vi.fn(),
refresh,
})).resolves.toBe('access-new')
expect(refresh).toHaveBeenCalledWith('refresh-old')
})
it('does not wait for rotation after the shared session was removed', async () => {
const refreshError = new Error('session removed')
const refresh = vi.fn(async () => { throw refreshError })
vi.stubGlobal('navigator', {})
await expect(coordinateCrossTabRefresh({
lockName: 'admin-refresh',
observedRefreshToken: 'refresh-old',
currentRefreshToken: () => '',
reuseCurrentSession: vi.fn(),
refresh,
})).rejects.toBe(refreshError)
expect(refresh).toHaveBeenCalledWith('refresh-old')
})
2026-08-27 10:44:12 +08:00
it('aborts a refresh lock that cannot be acquired', async () => {
vi.stubGlobal('navigator', {
locks: {
request: vi.fn((_name: string, options: LockOptions) => new Promise((_resolve, reject) => {
options.signal?.addEventListener('abort', () => reject(options.signal?.reason), { once: true })
})),
},
})
await expect(coordinateCrossTabRefresh({
lockName: 'admin-refresh',
lockTimeoutMs: 5,
observedRefreshToken: 'refresh-old',
currentRefreshToken: () => 'refresh-old',
reuseCurrentSession: vi.fn(),
refresh: vi.fn(),
})).rejects.toBeInstanceOf(CrossTabRefreshTimeoutError)
})
2026-08-07 14:47:11 +08:00
})