src/rest/authToken.test.ts
v0.2.1 · 1.7 KB
import { describe, expect, it } from 'vitest';
import { AUTH_TOKEN_LIFETIME_MS, authHeader, createAuthTokenCache, DEFAULT_AUTH_TOKEN_TTL_MS } from './authToken.js';
describe('createAuthTokenCache', () => {
it('caches for the TTL (half the token lifetime by default) and refreshes after', async () => {
expect(DEFAULT_AUTH_TOKEN_TTL_MS * 2).toBe(AUTH_TOKEN_LIFETIME_MS);
let t = 0;
let n = 0;
const cache = createAuthTokenCache({ getToken: async () => `tok-${++n}`, now: () => t });
expect(await cache.get()).toBe('tok-1');
t = DEFAULT_AUTH_TOKEN_TTL_MS - 1;
expect(await cache.get()).toBe('tok-1');
t = DEFAULT_AUTH_TOKEN_TTL_MS;
expect(await cache.get()).toBe('tok-2');
expect(cache.peek()).toEqual({ token: 'tok-2', expiresAt: t + DEFAULT_AUTH_TOKEN_TTL_MS });
});
it('invalidate forces a new token (401/403 path)', async () => {
let n = 0;
const cache = createAuthTokenCache({ getToken: async () => `tok-${++n}` });
await cache.get();
cache.invalidate();
expect(cache.peek()).toBeNull();
expect(await cache.get()).toBe('tok-2');
});
it('shares one in-flight fetch and rejects empty tokens', async () => {
let n = 0;
const cache = createAuthTokenCache({
getToken: async () => {
n++;
await new Promise((r) => setTimeout(r, 5));
return 'tok';
},
});
await Promise.all([cache.get(), cache.get(), cache.get()]);
expect(n).toBe(1);
const empty = createAuthTokenCache({ getToken: async () => '' });
await expect(empty.get()).rejects.toThrow(/empty token/);
expect(empty.peek()).toBeNull();
});
it('authHeader has no Bearer prefix', () => {
expect(authHeader('abc')).toEqual({ authorization: 'abc' });
});
});