Fix identified issues with server consolidation
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
// Phase 9 §9.4 — vitest scaffold + auth-boundary tests.
|
||||
//
|
||||
// Covers the security-critical surface in shared/auth/verify.js. Five cases
|
||||
// per the original Phase 2 testing-scaffold spec.
|
||||
|
||||
import { describe, it, expect, beforeAll } from 'vitest';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { extractBearerToken, verifyToken, TokenError } from './verify.js';
|
||||
|
||||
const SECRET = 'test-secret-please-do-not-use-in-prod';
|
||||
const WRONG_SECRET = 'a-different-secret';
|
||||
|
||||
let validToken;
|
||||
let expiredToken;
|
||||
let wrongSigToken;
|
||||
|
||||
beforeAll(() => {
|
||||
validToken = jwt.sign({ userId: 42, username: 'alice' }, SECRET, { expiresIn: '1h' });
|
||||
expiredToken = jwt.sign({ userId: 42, username: 'alice' }, SECRET, { expiresIn: '-1s' });
|
||||
wrongSigToken = jwt.sign({ userId: 42, username: 'alice' }, WRONG_SECRET, { expiresIn: '1h' });
|
||||
});
|
||||
|
||||
describe('extractBearerToken', () => {
|
||||
it('returns token from a well-formed Bearer header', () => {
|
||||
expect(extractBearerToken('Bearer abc.def.ghi')).toBe('abc.def.ghi');
|
||||
});
|
||||
|
||||
it('throws TokenError(missing) when no header is provided', () => {
|
||||
expect(() => extractBearerToken(undefined)).toThrow(TokenError);
|
||||
try { extractBearerToken(undefined); } catch (err) { expect(err.code).toBe('missing'); }
|
||||
});
|
||||
|
||||
it('throws TokenError(malformed) when header is not Bearer-prefixed', () => {
|
||||
expect(() => extractBearerToken('Basic abc')).toThrow(TokenError);
|
||||
try { extractBearerToken('Basic abc'); } catch (err) { expect(err.code).toBe('malformed'); }
|
||||
});
|
||||
|
||||
it('throws TokenError(malformed) when Bearer header has empty token', () => {
|
||||
expect(() => extractBearerToken('Bearer ')).toThrow(TokenError);
|
||||
try { extractBearerToken('Bearer '); } catch (err) { expect(err.code).toBe('malformed'); }
|
||||
});
|
||||
|
||||
it('throws TokenError(missing) when header is not a string', () => {
|
||||
expect(() => extractBearerToken(null)).toThrow(TokenError);
|
||||
expect(() => extractBearerToken(42)).toThrow(TokenError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('verifyToken', () => {
|
||||
it('returns decoded payload for a valid token', () => {
|
||||
const decoded = verifyToken(validToken, SECRET);
|
||||
expect(decoded.userId).toBe(42);
|
||||
expect(decoded.username).toBe('alice');
|
||||
});
|
||||
|
||||
it('throws TokenError(expired) for an expired token', () => {
|
||||
expect(() => verifyToken(expiredToken, SECRET)).toThrow(TokenError);
|
||||
try { verifyToken(expiredToken, SECRET); } catch (err) { expect(err.code).toBe('expired'); }
|
||||
});
|
||||
|
||||
it('throws TokenError(invalid) for a wrong-signature token', () => {
|
||||
expect(() => verifyToken(wrongSigToken, SECRET)).toThrow(TokenError);
|
||||
try { verifyToken(wrongSigToken, SECRET); } catch (err) { expect(err.code).toBe('invalid'); }
|
||||
});
|
||||
|
||||
it('throws TokenError(invalid) for malformed JWT', () => {
|
||||
expect(() => verifyToken('not-a-jwt', SECRET)).toThrow(TokenError);
|
||||
try { verifyToken('not-a-jwt', SECRET); } catch (err) { expect(err.code).toBe('invalid'); }
|
||||
});
|
||||
|
||||
it('throws TokenError(misconfigured) when secret is missing', () => {
|
||||
expect(() => verifyToken(validToken, undefined)).toThrow(TokenError);
|
||||
try { verifyToken(validToken, undefined); } catch (err) { expect(err.code).toBe('misconfigured'); }
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user