Tests for: - user.ts: User class, roles, permissions, status checks - util.ts: loadFile utility - handlers.ts: multiHandler - types.ts: methodParser, requireAuth, requirePermission - logging.ts: module structure - database.ts: connectionConfig, raw queries, PostgresAuthStore - auth/token.ts: generateToken, hashToken, parseAuthorizationHeader - auth/password.ts: hashPassword, verifyPassword (scrypt) - auth/types.ts: Zod parsers, Session class, tokenLifetimes - auth/store.ts: InMemoryAuthStore - auth/service.ts: AuthService (login, register, verify, reset) - basic/*.ts: route structure tests Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
95 lines
3.0 KiB
TypeScript
95 lines
3.0 KiB
TypeScript
// Tests for auth/token.ts
|
|
// Pure unit tests - no database needed
|
|
|
|
import assert from "node:assert/strict";
|
|
import { describe, it } from "node:test";
|
|
import {
|
|
generateToken,
|
|
hashToken,
|
|
parseAuthorizationHeader,
|
|
SESSION_COOKIE_NAME,
|
|
} from "./token";
|
|
|
|
describe("token", () => {
|
|
describe("generateToken", () => {
|
|
it("generates a non-empty string", () => {
|
|
const token = generateToken();
|
|
assert.equal(typeof token, "string");
|
|
assert.ok(token.length > 0);
|
|
});
|
|
|
|
it("generates unique tokens", () => {
|
|
const tokens = new Set<string>();
|
|
for (let i = 0; i < 100; i++) {
|
|
tokens.add(generateToken());
|
|
}
|
|
assert.equal(tokens.size, 100);
|
|
});
|
|
|
|
it("generates base64url encoded tokens", () => {
|
|
const token = generateToken();
|
|
// base64url uses A-Z, a-z, 0-9, -, _
|
|
assert.match(token, /^[A-Za-z0-9_-]+$/);
|
|
});
|
|
});
|
|
|
|
describe("hashToken", () => {
|
|
it("returns a hex string", () => {
|
|
const hash = hashToken("test-token");
|
|
assert.match(hash, /^[a-f0-9]+$/);
|
|
});
|
|
|
|
it("returns consistent hash for same input", () => {
|
|
const hash1 = hashToken("test-token");
|
|
const hash2 = hashToken("test-token");
|
|
assert.equal(hash1, hash2);
|
|
});
|
|
|
|
it("returns different hash for different input", () => {
|
|
const hash1 = hashToken("token-1");
|
|
const hash2 = hashToken("token-2");
|
|
assert.notEqual(hash1, hash2);
|
|
});
|
|
|
|
it("returns 64 character hash (SHA-256)", () => {
|
|
const hash = hashToken("test-token");
|
|
assert.equal(hash.length, 64);
|
|
});
|
|
});
|
|
|
|
describe("parseAuthorizationHeader", () => {
|
|
it("returns null for undefined header", () => {
|
|
assert.equal(parseAuthorizationHeader(undefined), null);
|
|
});
|
|
|
|
it("returns null for empty string", () => {
|
|
assert.equal(parseAuthorizationHeader(""), null);
|
|
});
|
|
|
|
it("returns null for non-bearer auth", () => {
|
|
assert.equal(parseAuthorizationHeader("Basic abc123"), null);
|
|
});
|
|
|
|
it("returns null for malformed header", () => {
|
|
assert.equal(parseAuthorizationHeader("Bearer"), null);
|
|
assert.equal(parseAuthorizationHeader("Bearer token extra"), null);
|
|
});
|
|
|
|
it("extracts token from valid bearer header", () => {
|
|
assert.equal(parseAuthorizationHeader("Bearer abc123"), "abc123");
|
|
});
|
|
|
|
it("is case-insensitive for Bearer keyword", () => {
|
|
assert.equal(parseAuthorizationHeader("bearer abc123"), "abc123");
|
|
assert.equal(parseAuthorizationHeader("BEARER abc123"), "abc123");
|
|
});
|
|
});
|
|
|
|
describe("SESSION_COOKIE_NAME", () => {
|
|
it("is defined", () => {
|
|
assert.equal(typeof SESSION_COOKIE_NAME, "string");
|
|
assert.ok(SESSION_COOKIE_NAME.length > 0);
|
|
});
|
|
});
|
|
});
|