/** * API regression tests for authentication endpoints. * * Tests the NestJS auth controller directly via HTTP. * No browser needed — uses Playwright's request context. */ import { test, expect } from '@playwright/test'; import { TEST_USERS } from '../fixtures/test-data'; const API_BASE = process.env.API_BASE_URL || 'http://localhost/api'; test.describe('POST /api/auth/login', () => { test('should return access token for valid credentials', async ({ request }) => { const response = await request.post(`${API_BASE}/auth/login`, { data: { email: TEST_USERS.treasurer.email, password: TEST_USERS.treasurer.password, }, }); expect(response.status()).toBe(201); const body = await response.json(); expect(body).toHaveProperty('accessToken'); expect(body).toHaveProperty('user'); expect(body.user).toHaveProperty('email', TEST_USERS.treasurer.email); expect(body).toHaveProperty('organizations'); expect(Array.isArray(body.organizations)).toBe(true); }); test('should return 401 for invalid password', async ({ request }) => { const response = await request.post(`${API_BASE}/auth/login`, { data: { email: TEST_USERS.treasurer.email, password: 'WrongPassword123!', }, }); expect(response.status()).toBe(401); }); test('should return 401 for non-existent user', async ({ request }) => { const response = await request.post(`${API_BASE}/auth/login`, { data: { email: 'nonexistent@example.com', password: 'AnyPassword123!', }, }); expect(response.status()).toBe(401); }); test('should set httpOnly refresh cookie on success', async ({ request }) => { const response = await request.post(`${API_BASE}/auth/login`, { data: { email: TEST_USERS.treasurer.email, password: TEST_USERS.treasurer.password, }, }); expect(response.status()).toBe(201); // Check for Set-Cookie header with the refresh token const setCookie = response.headers()['set-cookie'] || ''; expect(setCookie).toContain('ledgeriq_rt'); expect(setCookie).toContain('HttpOnly'); }); }); test.describe('GET /api/auth/profile', () => { test('should return 401 without auth header', async ({ request }) => { const response = await request.get(`${API_BASE}/auth/profile`); expect(response.status()).toBe(401); }); test('should return user profile with valid token', async ({ request }) => { // Login first const loginResponse = await request.post(`${API_BASE}/auth/login`, { data: { email: TEST_USERS.treasurer.email, password: TEST_USERS.treasurer.password, }, }); const { accessToken } = await loginResponse.json(); // Fetch profile const response = await request.get(`${API_BASE}/auth/profile`, { headers: { Authorization: `Bearer ${accessToken}` }, }); expect(response.status()).toBe(200); const profile = await response.json(); expect(profile).toHaveProperty('email', TEST_USERS.treasurer.email); }); }); test.describe('POST /api/auth/logout', () => { test('should return success and clear cookie', async ({ request }) => { // Login first to get the cookie await request.post(`${API_BASE}/auth/login`, { data: { email: TEST_USERS.treasurer.email, password: TEST_USERS.treasurer.password, }, }); // Logout const response = await request.post(`${API_BASE}/auth/logout`); expect(response.status()).toBe(200); const body = await response.json(); expect(body).toEqual({ success: true }); }); }); test.describe('POST /api/auth/refresh', () => { test('should return 400 without refresh cookie', async ({ request }) => { // Create a fresh context without cookies const response = await request.post(`${API_BASE}/auth/refresh`); // Should fail — no refresh token cookie expect([400, 401]).toContain(response.status()); }); });