test(auth): actually exercise the Web Locks branch and assert session is preserved on retry
Two test-quality fixes from review of #2948 (no implementation change): - auth.test.ts: the "Web Locks available" test never stubbed navigator.locks, which is undefined in happy-dom, so it silently ran the insecure-HTTP fallback branch instead. It now stubs a minimal navigator.locks.request shim, asserts that request was actually called with 'vikunja-token-refresh', and still asserts the in-flight dedup collapses concurrent calls into one POST. The sibling test keeps setting navigator.locks = undefined, so both branches are covered. - auth.renewToken.test.ts: getTokenMock always returned null, so even after the retry "succeeded" the follow-up checkAuth() marked the store unauthenticated; the test only checked "not redirected to login" and could pass with the session lost. The retry mock now hands back a fresh, unexpired user JWT and the test asserts store.authenticated is true, proving the D-retry actually recovered the session.
This commit is contained in:
parent
95e4cf43b5
commit
badb000269
|
|
@ -49,6 +49,16 @@ describe('refreshToken in-flight dedup', () => {
|
|||
})
|
||||
|
||||
it('coalesces concurrent calls into a single POST when Web Locks is available', async () => {
|
||||
// Stub a minimal Web Locks API: happy-dom leaves navigator.locks
|
||||
// undefined, so without this the test would silently fall through to
|
||||
// the insecure-HTTP branch and never exercise navigator.locks.request.
|
||||
const requestSpy = vi.fn((_name: string, cb: () => unknown) => cb())
|
||||
Object.defineProperty(navigator, 'locks', {
|
||||
value: {request: requestSpy},
|
||||
configurable: true,
|
||||
writable: true,
|
||||
})
|
||||
|
||||
const p1 = refreshToken(true)
|
||||
const p2 = refreshToken(true)
|
||||
|
||||
|
|
@ -58,6 +68,9 @@ describe('refreshToken in-flight dedup', () => {
|
|||
settlePost()
|
||||
await Promise.all([p1, p2])
|
||||
|
||||
// The Web Locks branch actually ran...
|
||||
expect(requestSpy).toHaveBeenCalledWith('vikunja-token-refresh', expect.any(Function))
|
||||
// ...and the in-flight dedup still collapsed both calls into one POST.
|
||||
expect(postCallCount).toBe(1)
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,18 @@ function refreshError() {
|
|||
})
|
||||
}
|
||||
|
||||
// A JWT carrying a not-yet-expired user session, so the checkAuth() call that
|
||||
// renewToken() runs after a successful refresh treats the session as live.
|
||||
function freshUserJwt() {
|
||||
const payload = {
|
||||
id: 1,
|
||||
type: AUTH_TYPES.USER,
|
||||
exp: Math.floor(Date.now() / 1000) + 3600,
|
||||
}
|
||||
const encoded = btoa(JSON.stringify(payload))
|
||||
return `header.${encoded}.signature`
|
||||
}
|
||||
|
||||
describe('auth store renewToken retry (issue #2863)', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia())
|
||||
|
|
@ -79,15 +91,23 @@ describe('auth store renewToken retry (issue #2863)', () => {
|
|||
const store = useAuthStore()
|
||||
setupExpiredUserSession(store)
|
||||
|
||||
// The retry "succeeds" only if it actually leaves a usable token behind:
|
||||
// renewToken() runs checkAuth() afterwards, which reads getToken(). Start
|
||||
// with no token, then hand back a fresh JWT once the refresh resolves.
|
||||
getTokenMock.mockReturnValue(null)
|
||||
refreshTokenMock
|
||||
.mockRejectedValueOnce(refreshError())
|
||||
.mockResolvedValueOnce(undefined)
|
||||
.mockImplementationOnce(async () => {
|
||||
getTokenMock.mockReturnValue(freshUserJwt())
|
||||
})
|
||||
|
||||
await store.renewToken()
|
||||
|
||||
// Two refresh attempts: the initial one and the single retry.
|
||||
expect(refreshTokenMock).toHaveBeenCalledTimes(2)
|
||||
// The retry succeeded, so the user is not bounced to login.
|
||||
// The retry recovered the session: the user is still authenticated...
|
||||
expect(store.authenticated).toBe(true)
|
||||
// ...and was not bounced to login.
|
||||
expect(routerPushMock).not.toHaveBeenCalledWith({name: 'user.login'})
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue