/** * Test factory helpers. * These return plain data objects — NOT database-inserted records. * Safe to use whether or not a database is available. */ // ─── Client ────────────────────────────────────────────────────────────────── export interface ClientData { id: string; portId: string; fullName: string; companyName: string | null; nationality: string | null; isProxy: boolean; source: string | null; yachtLengthFt: string | null; yachtLengthM: string | null; archivedAt: Date | null; createdAt: Date; updatedAt: Date; } export function makeClient(overrides?: Partial): ClientData { return { id: crypto.randomUUID(), portId: crypto.randomUUID(), fullName: 'Test Client', companyName: null, nationality: null, isProxy: false, source: 'manual', yachtLengthFt: null, yachtLengthM: null, archivedAt: null, createdAt: new Date(), updatedAt: new Date(), ...overrides, }; } // ─── Interest ───────────────────────────────────────────────────────────────── export interface InterestData { id: string; portId: string; clientId: string; berthId: string | null; pipelineStage: string; leadCategory: string | null; source: string | null; eoiStatus: string | null; contractStatus: string | null; depositStatus: string | null; notes: string | null; archivedAt: Date | null; createdAt: Date; updatedAt: Date; } export function makeInterest(overrides?: Partial): InterestData { return { id: crypto.randomUUID(), portId: crypto.randomUUID(), clientId: crypto.randomUUID(), berthId: null, pipelineStage: 'open', leadCategory: null, source: 'manual', eoiStatus: null, contractStatus: null, depositStatus: null, notes: null, archivedAt: null, createdAt: new Date(), updatedAt: new Date(), ...overrides, }; } // ─── Berth ──────────────────────────────────────────────────────────────────── export interface BerthData { id: string; portId: string; mooringNumber: string; status: string; area: string | null; lengthM: string | null; price: string | null; tenureType: string | null; archivedAt: Date | null; createdAt: Date; updatedAt: Date; } export function makeBerth(overrides?: Partial): BerthData { return { id: crypto.randomUUID(), portId: crypto.randomUUID(), mooringNumber: `B-${Math.floor(Math.random() * 999) + 1}`, status: 'available', area: null, lengthM: '12', price: '50000', tenureType: 'freehold', archivedAt: null, createdAt: new Date(), updatedAt: new Date(), ...overrides, }; } // ─── Webhook ────────────────────────────────────────────────────────────────── export interface WebhookData { id: string; portId: string; name: string; url: string; secret: string | null; events: string[]; isActive: boolean; createdBy: string; createdAt: Date; updatedAt: Date; } export function makeWebhook(overrides?: Partial): WebhookData { return { id: crypto.randomUUID(), portId: crypto.randomUUID(), name: 'Test Webhook', url: 'https://example.com/webhook', secret: null, events: ['client.created'], isActive: true, createdBy: crypto.randomUUID(), createdAt: new Date(), updatedAt: new Date(), ...overrides, }; } // ─── Audit Log ──────────────────────────────────────────────────────────────── export interface AuditMeta { userId: string; portId: string; ipAddress: string; userAgent: string; } export function makeAuditMeta(overrides?: Partial): AuditMeta { return { userId: crypto.randomUUID(), portId: crypto.randomUUID(), ipAddress: '127.0.0.1', userAgent: 'vitest/1.0', ...overrides, }; } // ─── Auth Context ───────────────────────────────────────────────────────────── import type { RolePermissions } from '@/lib/db/schema/users'; /** Full permissions — every action allowed. */ export function makeFullPermissions(): RolePermissions { return { clients: { view: true, create: true, edit: true, delete: true, merge: true, export: true }, interests: { view: true, create: true, edit: true, delete: true, change_stage: true, generate_eoi: true, export: true }, berths: { view: true, edit: true, import: true, manage_waiting_list: true }, documents: { view: true, create: true, send_for_signing: true, upload_signed: true, delete: true }, expenses: { view: true, create: true, edit: true, delete: true, export: true, scan_receipt: true }, invoices: { view: true, create: true, edit: true, delete: true, send: true, record_payment: true, export: true }, files: { view: true, upload: true, delete: true, manage_folders: true }, email: { view: true, send: true, configure_account: true }, reminders: { view_own: true, view_all: true, create: true, edit_own: true, edit_all: true, assign_others: true }, calendar: { connect: true, view_events: true }, reports: { view_dashboard: true, view_analytics: true, export: true }, document_templates: { view: true, generate: true, manage: true }, admin: { manage_users: true, view_audit_log: true, manage_settings: true, manage_webhooks: true, manage_reports: true, manage_custom_fields: true, manage_forms: true, manage_tags: true, system_backup: true, }, }; } /** Read-only viewer permissions — no create/update/delete. */ export function makeViewerPermissions(): RolePermissions { return { clients: { view: true, create: false, edit: false, delete: false, merge: false, export: false }, interests: { view: true, create: false, edit: false, delete: false, change_stage: false, generate_eoi: false, export: false }, berths: { view: true, edit: false, import: false, manage_waiting_list: false }, documents: { view: true, create: false, send_for_signing: false, upload_signed: false, delete: false }, expenses: { view: true, create: false, edit: false, delete: false, export: false, scan_receipt: false }, invoices: { view: true, create: false, edit: false, delete: false, send: false, record_payment: false, export: false }, files: { view: true, upload: false, delete: false, manage_folders: false }, email: { view: true, send: false, configure_account: false }, reminders: { view_own: true, view_all: false, create: false, edit_own: false, edit_all: false, assign_others: false }, calendar: { connect: false, view_events: true }, reports: { view_dashboard: true, view_analytics: false, export: false }, document_templates: { view: true, generate: false, manage: false }, admin: { manage_users: false, view_audit_log: false, manage_settings: false, manage_webhooks: false, manage_reports: false, manage_custom_fields: false, manage_forms: false, manage_tags: false, system_backup: false, }, }; } /** Sales agent permissions — own clients/interests, no admin. */ export function makeSalesAgentPermissions(): RolePermissions { return { clients: { view: true, create: true, edit: true, delete: false, merge: false, export: false }, interests: { view: true, create: true, edit: true, delete: false, change_stage: true, generate_eoi: true, export: false }, berths: { view: true, edit: false, import: false, manage_waiting_list: false }, documents: { view: true, create: true, send_for_signing: true, upload_signed: true, delete: false }, expenses: { view: true, create: true, edit: true, delete: false, export: false, scan_receipt: true }, invoices: { view: true, create: false, edit: false, delete: false, send: false, record_payment: false, export: false }, files: { view: true, upload: true, delete: false, manage_folders: false }, email: { view: true, send: true, configure_account: false }, reminders: { view_own: true, view_all: false, create: true, edit_own: true, edit_all: false, assign_others: false }, calendar: { connect: true, view_events: true }, reports: { view_dashboard: true, view_analytics: false, export: false }, document_templates: { view: true, generate: true, manage: false }, admin: { manage_users: false, view_audit_log: false, manage_settings: false, manage_webhooks: false, manage_reports: false, manage_custom_fields: false, manage_forms: false, manage_tags: false, system_backup: false, }, }; } /** Sales manager — can do most things, limited admin. */ export function makeSalesManagerPermissions(): RolePermissions { return { clients: { view: true, create: true, edit: true, delete: true, merge: true, export: true }, interests: { view: true, create: true, edit: true, delete: true, change_stage: true, generate_eoi: true, export: true }, berths: { view: true, edit: true, import: false, manage_waiting_list: true }, documents: { view: true, create: true, send_for_signing: true, upload_signed: true, delete: true }, expenses: { view: true, create: true, edit: true, delete: true, export: true, scan_receipt: true }, invoices: { view: true, create: true, edit: true, delete: false, send: true, record_payment: true, export: true }, files: { view: true, upload: true, delete: true, manage_folders: true }, email: { view: true, send: true, configure_account: false }, reminders: { view_own: true, view_all: true, create: true, edit_own: true, edit_all: true, assign_others: true }, calendar: { connect: true, view_events: true }, reports: { view_dashboard: true, view_analytics: true, export: true }, document_templates: { view: true, generate: true, manage: false }, admin: { manage_users: false, view_audit_log: true, manage_settings: false, manage_webhooks: false, manage_reports: true, manage_custom_fields: false, manage_forms: false, manage_tags: true, system_backup: false, }, }; } /** Director — everything except system backup. */ export function makeDirectorPermissions(): RolePermissions { return { ...makeFullPermissions(), admin: { ...makeFullPermissions().admin, system_backup: false, }, }; } // ─── Minimal valid CreateClientInput ───────────────────────────────────────── /** Returns a minimal valid CreateClientInput object for use in service calls. */ export function makeCreateClientInput(overrides?: { fullName?: string; portId?: string }) { return { fullName: overrides?.fullName ?? 'Test Client', contacts: [{ channel: 'email' as const, value: 'test@example.com', isPrimary: true }], isProxy: false, tagIds: [] as string[], }; } /** Returns a minimal valid CreateInterestInput object. */ export function makeCreateInterestInput(overrides?: { clientId?: string; pipelineStage?: 'open' | 'details_sent' | 'in_communication' | 'visited' | 'signed_eoi_nda' | 'deposit_10pct' | 'contract' | 'completed'; }) { return { clientId: overrides?.clientId ?? crypto.randomUUID(), pipelineStage: overrides?.pipelineStage ?? ('open' as const), reminderEnabled: false, tagIds: [] as string[], }; }