/** * Global Setup — Seed the database with test users via Better Auth API * and insert supporting data (berths, system_settings) via direct SQL. * * This runs BEFORE any test spec via Playwright's `dependencies` config. */ import { test as setup } from '@playwright/test'; const BASE = 'http://localhost:3000'; // ── Test user credentials ─────────────────────────────────────────────────── export const USERS = { super_admin: { email: 'admin@portnimara.test', password: 'SuperAdmin12345!', name: 'Test Admin', }, sales_agent: { email: 'agent@portnimara.test', password: 'SalesAgent12345!', name: 'Test Agent', }, viewer: { email: 'viewer@portnimara.test', password: 'ViewerUser12345!', name: 'Test Viewer', }, }; // ── Helpers ───────────────────────────────────────────────────────────────── /** Sign up a user via Better Auth REST API */ async function signUpUser(email: string, password: string, name: string) { const headers = { 'Content-Type': 'application/json', 'Origin': BASE, 'Referer': `${BASE}/`, }; const res = await fetch(`${BASE}/api/auth/sign-up/email`, { method: 'POST', headers, body: JSON.stringify({ email, password, name }), }); if (res.ok) { const data = await res.json(); return data.user?.id ?? data.id; } // User may already exist — try sign-in instead const loginRes = await fetch(`${BASE}/api/auth/sign-in/email`, { method: 'POST', headers, body: JSON.stringify({ email, password }), }); if (loginRes.ok) { const loginData = await loginRes.json(); return loginData.user?.id ?? loginData.id; } const errorBody = await loginRes.text().catch(() => 'no body'); throw new Error(`Failed to create or sign in user ${email}: ${loginRes.status} ${errorBody}`); } /** Run raw SQL via docker psql using stdin piping */ async function runSQL(sql: string) { const { execSync } = await import('child_process'); execSync( `docker compose -f docker-compose.yml -f docker-compose.dev.yml exec -T postgres psql -U crm -d port_nimara_crm`, { cwd: process.cwd(), input: sql, stdio: ['pipe', 'pipe', 'pipe'] }, ); } // ── Setup ─────────────────────────────────────────────────────────────────── setup('seed test database', async () => { setup.setTimeout(120_000); console.log('🔧 Creating test users via Better Auth...'); // 1. Create users via Better Auth sign-up endpoint const adminId = await signUpUser( USERS.super_admin.email, USERS.super_admin.password, USERS.super_admin.name, ); console.log(` ✓ super_admin created: ${adminId}`); const agentId = await signUpUser( USERS.sales_agent.email, USERS.sales_agent.password, USERS.sales_agent.name, ); console.log(` ✓ sales_agent created: ${agentId}`); const viewerId = await signUpUser( USERS.viewer.email, USERS.viewer.password, USERS.viewer.name, ); console.log(` ✓ viewer created: ${viewerId}`); // 2. Get portId and roleIds from seed data console.log('🔧 Linking users to port and roles...'); // Create user_profiles + user_port_roles for each test user // The super_admin profile already exists from db:seed with a placeholder userId. // We need to update it and create profiles for agent + viewer. await runSQL(` -- Update super_admin profile to match the real auth user ID UPDATE user_profiles SET user_id = '${adminId}' WHERE user_id = 'super-admin-matt-portnimara'; -- If that didn't match (profile might not exist), insert it INSERT INTO user_profiles (id, user_id, display_name, is_super_admin, is_active, preferences) VALUES (gen_random_uuid()::text, '${adminId}', 'Test Admin', true, true, '{}') ON CONFLICT (user_id) DO UPDATE SET is_super_admin = true, is_active = true; -- Create sales_agent profile INSERT INTO user_profiles (id, user_id, display_name, is_super_admin, is_active, preferences) VALUES (gen_random_uuid()::text, '${agentId}', 'Test Agent', false, true, '{}') ON CONFLICT (user_id) DO NOTHING; -- Create viewer profile INSERT INTO user_profiles (id, user_id, display_name, is_super_admin, is_active, preferences) VALUES (gen_random_uuid()::text, '${viewerId}', 'Test Viewer', false, true, '{}') ON CONFLICT (user_id) DO NOTHING; `); await runSQL(` -- Assign super_admin role to admin user INSERT INTO user_port_roles (id, user_id, port_id, role_id) SELECT gen_random_uuid()::text, '${adminId}', p.id, r.id FROM ports p, roles r WHERE p.slug = 'port-nimara' AND r.name = 'super_admin' ON CONFLICT DO NOTHING; -- Assign sales_agent role to agent user INSERT INTO user_port_roles (id, user_id, port_id, role_id) SELECT gen_random_uuid()::text, '${agentId}', p.id, r.id FROM ports p, roles r WHERE p.slug = 'port-nimara' AND r.name = 'sales_agent' ON CONFLICT DO NOTHING; -- Assign viewer role to viewer user INSERT INTO user_port_roles (id, user_id, port_id, role_id) SELECT gen_random_uuid()::text, '${viewerId}', p.id, r.id FROM ports p, roles r WHERE p.slug = 'port-nimara' AND r.name = 'viewer' ON CONFLICT DO NOTHING; `); console.log(' ✓ Users linked to port-nimara with correct roles'); // 3. Seed berths for testing console.log('🔧 Seeding berths...'); await runSQL(` INSERT INTO berths (id, port_id, mooring_number, area, status, length_ft, width_ft, price, tenure_type) SELECT gen_random_uuid()::text, p.id, 'A-001', 'Marina A', 'available', '60', '20', '150000', 'permanent' FROM ports p WHERE p.slug = 'port-nimara' ON CONFLICT DO NOTHING; INSERT INTO berths (id, port_id, mooring_number, area, status, length_ft, width_ft, price, tenure_type) SELECT gen_random_uuid()::text, p.id, 'A-002', 'Marina A', 'available', '80', '25', '250000', 'permanent' FROM ports p WHERE p.slug = 'port-nimara' ON CONFLICT DO NOTHING; INSERT INTO berths (id, port_id, mooring_number, area, status, length_ft, width_ft, price, tenure_type) SELECT gen_random_uuid()::text, p.id, 'B-001', 'Marina B', 'under_offer', '45', '15', '95000', 'fixed_term' FROM ports p WHERE p.slug = 'port-nimara' ON CONFLICT DO NOTHING; `); console.log(' ✓ 3 berths seeded'); // 4. Seed system settings console.log('🔧 Seeding system settings...'); await runSQL(` INSERT INTO system_settings (key, value, port_id) SELECT 'invoice_prefix', '"INV"'::jsonb, p.id FROM ports p WHERE p.slug = 'port-nimara' ON CONFLICT DO NOTHING; INSERT INTO system_settings (key, value, port_id) SELECT 'default_payment_terms', '"net30"'::jsonb, p.id FROM ports p WHERE p.slug = 'port-nimara' ON CONFLICT DO NOTHING; `); console.log(' ✓ System settings seeded'); console.log('✅ Global setup complete!'); });