Files
web-cad/backend/tests/AuthService.test.ts
Leopoldadmin 1bcfaffdd4 fix: replace hardcoded localhost:3001 with relative API URL + add canvas mock and integration test
- Fix frontend Network error: API_BASE now defaults to empty string (same-origin)
- Fix 3 files: api.ts, AuthContext.tsx, Dashboard.tsx
- Add Canvas 2D context mock in tests/setup.ts for jsdom
- Fix -0 vs +0 in ZoomPanController.getViewport()
- Add IntegrationWorkflow.test.ts (35 tests, full CAD workflow)
2026-06-26 17:48:24 +02:00

189 lines
7.3 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* AuthService Unit Tests Register / Login / ChangePassword / Session lifecycle
*/
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { SqliteAdapter } from '../src/database/SqliteAdapter.js';
import { AuthService } from '../src/auth/AuthService.js';
describe('AuthService', () => {
let db: SqliteAdapter;
let auth: AuthService;
beforeAll(async () => {
db = new SqliteAdapter(':memory:');
await db.init();
auth = new AuthService(db);
});
afterAll(() => {
db.close();
});
// ─── Register ───────────────────────────────────────
describe('register()', () => {
it('should register a new user successfully', async () => {
const result = await auth.register('register@example.com', 'Password123!', 'Register User', 'planer');
expect(result.user).toBeDefined();
expect(result.user.email).toBe('register@example.com');
expect(result.user.name).toBe('Register User');
expect(result.user.password_hash).toBeUndefined();
expect(result.session).toBeDefined();
expect(result.session.token).toBeDefined();
expect(result.session.userId).toBe(result.user.id);
});
it('should throw on duplicate email', async () => {
await expect(
auth.register('register@example.com', 'AnotherPass!', 'Another User', 'planer'),
).rejects.toThrow('Email already registered');
});
it('should default role to planer when not specified', async () => {
const result = await auth.register('default-role@example.com', 'Pass123!', 'Default Role User');
expect(result.user.role).toBe('planer');
});
});
// ─── Login ─────────────────────────────────────────
describe('login()', () => {
it('should login with correct credentials', async () => {
const result = await auth.login('register@example.com', 'Password123!');
expect(result.user).toBeDefined();
expect(result.user.email).toBe('register@example.com');
expect(result.session.token).toBeDefined();
expect(result.session.userId).toBe(result.user.id);
});
it('should throw on wrong password', async () => {
await expect(
auth.login('register@example.com', 'WrongPassword!'),
).rejects.toThrow('Invalid credentials');
});
it('should throw on non-existent email', async () => {
await expect(
auth.login('nobody@example.com', 'SomePassword!'),
).rejects.toThrow('Invalid credentials');
});
});
// ─── Change Password ───────────────────────────────
describe('changePassword()', () => {
it('should change password with correct old password', async () => {
const result = await auth.register('changepw@example.com', 'OldPass123!', 'ChangePW User', 'admin');
const userId = result.user.id;
await auth.changePassword(userId, 'OldPass123!', 'NewPass456!');
// Should now be able to login with new password
const loginResult = await auth.login('changepw@example.com', 'NewPass456!');
expect(loginResult.user.id).toBe(userId);
});
it('should throw on wrong old password', async () => {
const result = await auth.register('changepw2@example.com', 'OriginalPass!', 'User2', 'planer');
await expect(
auth.changePassword(result.user.id, 'WrongOldPass!', 'NewPass!'),
).rejects.toThrow('Invalid current password');
});
it('should throw on non-existent user', async () => {
await expect(
auth.changePassword('non-existent-user-id', 'OldPass!', 'NewPass!'),
).rejects.toThrow('User not found');
});
it('should reject old password after change', async () => {
const result = await auth.register('changepw3@example.com', 'Original123!', 'User3', 'planer');
await auth.changePassword(result.user.id, 'Original123!', 'Changed456!');
await expect(
auth.login('changepw3@example.com', 'Original123!'),
).rejects.toThrow('Invalid credentials');
});
});
// ─── Session Management ────────────────────────────
describe('Session lifecycle', () => {
it('should create and validate a session', async () => {
const regResult = await auth.register('session@example.com', 'Pass123!', 'Session User', 'planer');
const token = regResult.session.token;
const session = auth.validateSession(token);
expect(session).not.toBeNull();
expect(session!.token).toBe(token);
expect(session!.userId).toBe(regResult.user.id);
});
it('should return null for invalid session token', () => {
const session = auth.validateSession('invalid-token-xyz');
expect(session).toBeNull();
});
it('should destroy a session and invalidate it', async () => {
const regResult = await auth.register('destroy@example.com', 'Pass123!', 'Destroy User', 'planer');
const token = regResult.session.token;
// Session should be valid
expect(auth.validateSession(token)).not.toBeNull();
// Destroy it
auth.destroySession(token);
// Session should now be invalid
expect(auth.validateSession(token)).toBeNull();
});
it('should handle destroying a non-existent session gracefully', () => {
// Should not throw
auth.destroySession('non-existent-token');
});
});
// ─── getUserFromSession ─────────────────────────────
describe('getUserFromSession()', () => {
it('should return user from valid session', async () => {
const regResult = await auth.register('getuser@example.com', 'Pass123!', 'GetUser User', 'planer');
const token = regResult.session.token;
const user = auth.getUserFromSession(token);
expect(user).not.toBeNull();
expect(user!.email).toBe('getuser@example.com');
expect(user!.password_hash).toBeDefined(); // returns full DBUser including password_hash
});
it('should return null for invalid session token', () => {
const user = auth.getUserFromSession('invalid-token-xyz');
expect(user).toBeNull();
});
it('should return null after session is destroyed', async () => {
const regResult = await auth.register('getuser2@example.com', 'Pass123!', 'GetUser2 User', 'planer');
const token = regResult.session.token;
auth.destroySession(token);
const user = auth.getUserFromSession(token);
expect(user).toBeNull();
});
});
// ─── createSession (standalone) ─────────────────────
describe('createSession()', () => {
it('should create a session with a unique token', async () => {
const regResult = await auth.register('createsession@example.com', 'Pass123!', 'CS User', 'planer');
const session1 = auth.createSession(regResult.user.id);
const session2 = auth.createSession(regResult.user.id);
expect(session1.token).not.toBe(session2.token);
expect(session1.userId).toBe(regResult.user.id);
expect(session2.userId).toBe(regResult.user.id);
expect(session1.expiresAt).toBeGreaterThan(Date.now());
expect(session2.expiresAt).toBeGreaterThan(Date.now());
});
});
});