89 lines
3.7 KiB
TypeScript
89 lines
3.7 KiB
TypeScript
import { describe, it } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { createAuthRouter } from './auth.router.ts';
|
|
import { AuthService } from './auth.service.ts';
|
|
import { Pool } from 'pg';
|
|
import { Request, Response } from 'express';
|
|
|
|
function createMockResponse() {
|
|
const res: Partial<Response> = {};
|
|
const data = { statusCode: 200, jsonPayload: null as any, cookies: {} as any };
|
|
|
|
res.status = function (code: number) { data.statusCode = code; return this as Response; };
|
|
res.json = function (payload: any) { data.jsonPayload = payload; return this as Response; };
|
|
res.cookie = function (name: string, val: string, options: any) {
|
|
data.cookies[name] = { val, options };
|
|
return this as Response;
|
|
};
|
|
return { mockRes: res as Response, data };
|
|
}
|
|
|
|
function createMockDb(userRows: any[], sessionRows: any[] = []) {
|
|
return {
|
|
query: async (text: string, values: any[]) => {
|
|
if (text.trim().startsWith('SELECT') && text.includes('FROM users')) {
|
|
return { rows: userRows };
|
|
}
|
|
return { rows: sessionRows };
|
|
}
|
|
} as unknown as Pool;
|
|
}
|
|
|
|
describe('Auth Router (TDD)', () => {
|
|
const authService = new AuthService();
|
|
|
|
describe('POST /register', () => {
|
|
it('should block registrations missing username or password with a 400 status', async () => {
|
|
const mockDb = createMockDb([]);
|
|
const router = createAuthRouter(mockDb, authService);
|
|
|
|
const mockReq = { body: { username: '' } } as unknown as Request;
|
|
const { mockRes, data } = createMockResponse();
|
|
|
|
const handler = router.stack.find((l: any) => l.route.path === '/register').route.stack[0].handle;
|
|
await handler(mockReq, mockRes);
|
|
|
|
assert.equal(data.statusCode, 400);
|
|
assert.equal(data.jsonPayload.success, false);
|
|
});
|
|
});
|
|
|
|
describe('POST /login', () => {
|
|
it('should successfully issue a high-entropy cookie on valid credentials', async () => {
|
|
const password = 'secure-password';
|
|
const hash = await authService.hashPassword(password);
|
|
|
|
// Simulate database finding the registered user
|
|
const mockDb = createMockDb([{ id: 42, username: 'testuser', password_hash: hash }]);
|
|
const router = createAuthRouter(mockDb, authService);
|
|
|
|
const mockReq = { body: { username: 'testuser', password } } as unknown as Request;
|
|
const { mockRes, data } = createMockResponse();
|
|
|
|
const handler = router.stack.find((l: any) => l.route.path === '/login').route.stack[0].handle;
|
|
await handler(mockReq, mockRes);
|
|
|
|
assert.equal(data.statusCode, 200);
|
|
assert.equal(data.jsonPayload.success, true);
|
|
assert.ok(data.cookies['session_token']);
|
|
assert.equal(data.cookies['session_token'].options.httpOnly, true);
|
|
assert.equal(data.cookies['session_token'].options.sameSite, 'strict');
|
|
});
|
|
|
|
it('should reject invalid passwords with a 401 status code', async () => {
|
|
const hash = await authService.hashPassword('real-password');
|
|
const mockDb = createMockDb([{ id: 42, username: 'testuser', password_hash: hash }]);
|
|
const router = createAuthRouter(mockDb, authService);
|
|
|
|
const mockReq = { body: { username: 'testuser', password: 'wrong-password' } } as unknown as Request;
|
|
const { mockRes, data } = createMockResponse();
|
|
|
|
const handler = router.stack.find((l: any) => l.route.path === '/login').route.stack[0].handle;
|
|
await handler(mockReq, mockRes);
|
|
|
|
assert.equal(data.statusCode, 401);
|
|
assert.equal(data.jsonPayload.success, false);
|
|
});
|
|
});
|
|
});
|