Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM, PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source files covering clients, berths, interests/pipeline, documents/EOI, expenses/invoices, email, notifications, dashboard, admin, and client portal. CI/CD via Gitea Actions with Docker builds. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
192 lines
7.1 KiB
TypeScript
192 lines
7.1 KiB
TypeScript
/**
|
|
* 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!');
|
|
});
|