proposal-system/web/src/lib/auth/__tests__/authStorage.test.ts

77 lines
2.4 KiB
TypeScript
Raw Normal View History

/**
* QA-C5: Auth storage module tests (replaces the Redux authSlice tests).
*
* Tests the sessionStorage-backed session persistence (WEB-C1):
* - setAuthUser/getAuthUser round-trip
* - getAuthUser tolerates malformed stored JSON
* - clearAuth removes the stored session
* - isTokenValid accepts unexpired JWTs and rejects expired/malformed ones
*/
import { describe, it, expect, beforeEach } from 'vitest';
import type { AuthUser } from '@proposal-system/api-contracts';
import { getAuthUser, setAuthUser, clearAuth, isTokenValid } from '../authStorage';
import { STORAGE_KEY_TOKEN } from '../../../constants';
function createToken(expOffsetSeconds: number): string {
const header = btoa(JSON.stringify({ alg: 'HS256' }));
const payload = btoa(JSON.stringify({
sub: 'test-user',
exp: Math.floor(Date.now() / 1000) + expOffsetSeconds,
}));
return `${header}.${payload}.fake-signature`;
}
const user: AuthUser = {
id: 'user-1',
email: 'admin@test.com',
displayName: 'Admin',
role: 'Admin',
token: createToken(3600),
};
describe('authStorage', () => {
beforeEach(() => {
window.sessionStorage.clear();
});
it('QA-C5: setAuthUser persists to sessionStorage and getAuthUser reads it back', () => {
setAuthUser(user);
expect(window.sessionStorage.getItem(STORAGE_KEY_TOKEN)).not.toBeNull();
expect(getAuthUser()).toEqual(user);
});
it('QA-C5: getAuthUser returns null when nothing is stored', () => {
expect(getAuthUser()).toBeNull();
});
it('QA-C5: getAuthUser returns null for malformed stored JSON', () => {
window.sessionStorage.setItem(STORAGE_KEY_TOKEN, 'not-valid-json{{{');
expect(getAuthUser()).toBeNull();
});
it('QA-C5: clearAuth removes the stored session', () => {
setAuthUser(user);
clearAuth();
expect(window.sessionStorage.getItem(STORAGE_KEY_TOKEN)).toBeNull();
expect(getAuthUser()).toBeNull();
});
it('QA-C5: isTokenValid accepts a token expiring in the future', () => {
expect(isTokenValid(createToken(3600))).toBe(true);
});
it('QA-C5: isTokenValid rejects an expired token', () => {
expect(isTokenValid(createToken(-3600))).toBe(false);
});
it('QA-C5: isTokenValid rejects missing or malformed tokens', () => {
expect(isTokenValid(null)).toBe(false);
expect(isTokenValid(undefined)).toBe(false);
expect(isTokenValid('')).toBe(false);
expect(isTokenValid('not-a-jwt')).toBe(false);
});
});