UNPKG

prodobit

Version:

Open-core business application development platform

604 lines (543 loc) 14 kB
import { vi } from 'vitest' import type { Database } from '@prodobit/database' import type { User } from '@prodobit/types' /** * Test utilities and helpers for Prodobit testing */ // Mock data generators export const createMockTenant = (overrides?: Partial<any>) => ({ id: 'tenant-123', name: 'Test Company', domain: 'test-company', settings: { timezone: 'UTC', currency: 'USD' }, createdAt: new Date('2024-01-01T00:00:00Z'), updatedAt: new Date('2024-01-01T00:00:00Z'), ...overrides }) export const createMockUser = (overrides?: Partial<User>): User => ({ id: 'user-123', email: 'test@example.com', firstName: 'John', lastName: 'Doe', tenantId: 'tenant-123', roles: ['admin'], ...overrides }) export const createMockParty = (overrides?: Partial<any>) => ({ id: 'party-123', name: 'ACME Corporation', type: 'customer', email: 'contact@acme.com', phone: '+1-555-0123', address: { street: '123 Main St', city: 'Anytown', state: 'CA', zipCode: '12345', country: 'USA' }, tenantId: 'tenant-123', createdAt: new Date('2024-01-01T00:00:00Z'), updatedAt: new Date('2024-01-01T00:00:00Z'), ...overrides }) export const createMockItem = (overrides?: Partial<any>) => ({ id: 'item-123', name: 'Test Product', description: 'A test product', sku: 'TEST-001', category: 'Electronics', unitPrice: '99.99', tenantId: 'tenant-123', createdAt: new Date('2024-01-01T00:00:00Z'), updatedAt: new Date('2024-01-01T00:00:00Z'), ...overrides }) export const createMockSalesOrder = (overrides?: Partial<any>) => ({ id: 'order-123', orderNumber: 'SO-2024-001', customerId: 'party-123', orderDate: new Date('2024-01-15T00:00:00Z'), status: 'draft', totalAmount: '199.98', tenantId: 'tenant-123', createdAt: new Date('2024-01-15T00:00:00Z'), updatedAt: new Date('2024-01-15T00:00:00Z'), ...overrides }) export const createMockSalesOrderItem = (overrides?: Partial<any>) => ({ id: 'order-item-123', orderId: 'order-123', itemId: 'item-123', quantity: 2, unitPrice: '99.99', totalPrice: '199.98', ...overrides }) // Database mock factory export const createMockDatabase = (): Database => { const mockDbQuery = { from: vi.fn(), where: vi.fn(), leftJoin: vi.fn(), rightJoin: vi.fn(), innerJoin: vi.fn(), returning: vi.fn(), set: vi.fn(), values: vi.fn(), with: vi.fn(), orderBy: vi.fn(), limit: vi.fn(), offset: vi.fn(), groupBy: vi.fn(), having: vi.fn() } // Chain all methods to return the query object for method chaining Object.keys(mockDbQuery).forEach(key => { if (typeof mockDbQuery[key as keyof typeof mockDbQuery] === 'function') { mockDbQuery[key as keyof typeof mockDbQuery] = vi.fn().mockReturnValue(mockDbQuery) } }) const mockDatabase = { select: vi.fn().mockReturnValue(mockDbQuery), insert: vi.fn().mockReturnValue(mockDbQuery), update: vi.fn().mockReturnValue(mockDbQuery), delete: vi.fn().mockReturnValue(mockDbQuery), transaction: vi.fn(), query: { tenants: { findFirst: vi.fn(), findMany: vi.fn() }, users: { findFirst: vi.fn(), findMany: vi.fn() }, parties: { findFirst: vi.fn(), findMany: vi.fn() }, items: { findFirst: vi.fn(), findMany: vi.fn() }, attributes: { findFirst: vi.fn(), findMany: vi.fn() }, itemAttributes: { findFirst: vi.fn(), findMany: vi.fn() }, locations: { findFirst: vi.fn(), findMany: vi.fn() }, assets: { findFirst: vi.fn(), findMany: vi.fn() }, salesOrders: { findFirst: vi.fn(), findMany: vi.fn() }, salesOrderItems: { findFirst: vi.fn(), findMany: vi.fn() } }, $client: vi.fn().mockImplementation((query: any) => { if (query[0] === 'SELECT 1') { return Promise.resolve([{ '?column?': 1 }]) } return Promise.resolve([]) }) } as unknown as Database return mockDatabase } // Hono context mock factory export const createMockContext = (overrides?: any) => { const context = { get: vi.fn(), set: vi.fn(), json: vi.fn().mockReturnValue({ status: 200 }), text: vi.fn().mockReturnValue({ status: 200 }), html: vi.fn().mockReturnValue({ status: 200 }), redirect: vi.fn().mockReturnValue({ status: 302 }), notFound: vi.fn().mockReturnValue({ status: 404 }), req: { method: 'GET', url: 'http://localhost:3001/test', param: vi.fn(), query: vi.fn(), header: vi.fn(), json: vi.fn(), text: vi.fn(), formData: vi.fn() }, res: { status: 200, headers: new Headers() }, env: {}, executionCtx: { waitUntil: vi.fn(), passThroughOnException: vi.fn() }, ...overrides } // Set up common default returns context.get.mockImplementation((key: string) => { if (key === 'db') return createMockDatabase() if (key === 'user') return createMockUser() if (key === 'enabledModules') return new Set(['core']) return null }) return context } // Service mock factories export const createMockItemService = () => ({ createItem: vi.fn(), getItemById: vi.fn(), getItemsWithAttributes: vi.fn(), updateItem: vi.fn(), deleteItem: vi.fn(), searchItems: vi.fn(), getItemsByCategory: vi.fn(), getItemsBySku: vi.fn(), addItemAttribute: vi.fn(), removeItemAttribute: vi.fn() }) export const createMockPartyService = () => ({ createParty: vi.fn(), getPartyById: vi.fn(), getPartiesByType: vi.fn(), updateParty: vi.fn(), deleteParty: vi.fn(), searchParties: vi.fn() }) export const createMockSalesOrderService = () => ({ createSalesOrder: vi.fn(), getSalesOrderById: vi.fn(), updateOrderStatus: vi.fn(), getOrdersByCustomer: vi.fn(), getOrdersByDateRange: vi.fn(), getOrderMetrics: vi.fn(), calculateOrderTotal: vi.fn(), generateOrderNumber: vi.fn(), validateOrderData: vi.fn(), validateStatusTransition: vi.fn() }) // Auth mocks export const createMockAuthUser = (overrides?: Partial<User>): User => ({ id: 'auth-user-123', email: 'auth@example.com', firstName: 'Auth', lastName: 'User', tenantId: 'tenant-123', roles: ['user'], ...overrides }) export const createMockJWTPayload = (overrides?: any) => ({ userId: 'user-123', email: 'test@example.com', tenantId: 'tenant-123', roles: ['admin'], iat: Math.floor(Date.now() / 1000), exp: Math.floor(Date.now() / 1000) + (7 * 24 * 60 * 60), // 7 days ...overrides }) // Configuration mocks export const createMockConfig = (overrides?: any) => ({ base: { environment: 'test', logLevel: 'info', ...overrides?.base }, database: { host: 'localhost', port: 5432, database: 'test_db', user: 'test_user', password: 'test_pass', ...overrides?.database }, server: { port: 3001, cors: { origin: ['http://localhost:3000'], credentials: true }, ...overrides?.server }, modules: { manifest: { modules: [ { name: 'core', enabled: true, version: '1.0.0' }, { name: 'sales', enabled: true, version: '1.0.0' }, { name: 'inventory', enabled: false, version: '1.0.0' } ] }, ...overrides?.modules } }) // React testing utilities export const createMockQueryClient = () => { const queryClient = { getQueryData: vi.fn(), setQueryData: vi.fn(), invalidateQueries: vi.fn(), removeQueries: vi.fn(), cancelQueries: vi.fn(), clear: vi.fn(), defaultMutationOptions: {}, defaultQueryOptions: { retry: false } } return queryClient } // ProdobitClient mock export const createMockProdobitClient = () => ({ auth: { login: vi.fn(), register: vi.fn(), logout: vi.fn(), refresh: vi.fn(), getCurrentUser: vi.fn(), isAuthenticated: vi.fn() }, items: { getAll: vi.fn(), getById: vi.fn(), create: vi.fn(), update: vi.fn(), delete: vi.fn(), search: vi.fn(), getByCategory: vi.fn() }, parties: { getAll: vi.fn(), getById: vi.fn(), create: vi.fn(), update: vi.fn(), delete: vi.fn(), getByType: vi.fn() }, salesOrders: { getAll: vi.fn(), getById: vi.fn(), create: vi.fn(), update: vi.fn(), updateStatus: vi.fn(), delete: vi.fn(), getByCustomer: vi.fn(), getByDateRange: vi.fn() }, setAuthToken: vi.fn(), clearAuthToken: vi.fn(), setBaseURL: vi.fn() }) // Test data builders export class TenantBuilder { private data: any = {} id(id: string) { this.data.id = id return this } name(name: string) { this.data.name = name return this } domain(domain: string) { this.data.domain = domain return this } settings(settings: any) { this.data.settings = settings return this } build() { return createMockTenant(this.data) } } export class UserBuilder { private data: any = {} id(id: string) { this.data.id = id return this } email(email: string) { this.data.email = email return this } name(firstName: string, lastName: string) { this.data.firstName = firstName this.data.lastName = lastName return this } tenantId(tenantId: string) { this.data.tenantId = tenantId return this } roles(roles: string[]) { this.data.roles = roles return this } build(): User { return createMockUser(this.data) } } export class ItemBuilder { private data: any = {} id(id: string) { this.data.id = id return this } name(name: string) { this.data.name = name return this } sku(sku: string) { this.data.sku = sku return this } price(price: string) { this.data.unitPrice = price return this } category(category: string) { this.data.category = category return this } tenantId(tenantId: string) { this.data.tenantId = tenantId return this } build() { return createMockItem(this.data) } } // Assertion helpers export const expectToHaveBeenCalledWithObject = ( mockFn: any, expectedObject: any ) => { const calls = mockFn.mock.calls const found = calls.some((call: any[]) => call.some(arg => typeof arg === 'object' && Object.keys(expectedObject).every(key => arg[key] === expectedObject[key]) ) ) if (!found) { throw new Error( `Expected mock to be called with object containing ${JSON.stringify(expectedObject)}, but it wasn't. Calls: ${JSON.stringify(calls)}` ) } } // Time utilities export const advanceTimersByDays = (days: number) => { vi.advanceTimersByTime(days * 24 * 60 * 60 * 1000) } export const advanceTimersByHours = (hours: number) => { vi.advanceTimersByTime(hours * 60 * 60 * 1000) } // Environment helpers export const withEnvironment = (envVars: Record<string, string>, fn: () => void | Promise<void>) => { const originalEnv = { ...process.env } Object.assign(process.env, envVars) const cleanup = () => { process.env = originalEnv } try { const result = fn() if (result && typeof result.then === 'function') { return result.finally(cleanup) } cleanup() return result } catch (error) { cleanup() throw error } } // Error simulation helpers export const simulateNetworkError = () => { const error = new Error('Network error') error.name = 'NetworkError' return error } export const simulateTimeout = () => { const error = new Error('Request timeout') error.name = 'TimeoutError' return error } export const simulateValidationError = (field: string, message: string) => { const error = new Error(`Validation error: ${field} ${message}`) error.name = 'ValidationError' return error } // Test database helpers (for integration tests) export const createTestDatabase = async () => { // This would create a test database instance // Implementation depends on your database setup return createMockDatabase() } export const cleanupTestDatabase = async (db: Database) => { // This would clean up test data // Implementation depends on your database setup } // Builders pattern for complex test data export const builders = { tenant: () => new TenantBuilder(), user: () => new UserBuilder(), item: () => new ItemBuilder() } // Common test scenarios export const testScenarios = { authenticatedUser: () => ({ user: createMockUser(), context: createMockContext({ get: vi.fn().mockImplementation((key: string) => { if (key === 'user') return createMockUser() if (key === 'db') return createMockDatabase() if (key === 'enabledModules') return new Set(['core']) return null }) }) }), unauthenticatedUser: () => ({ user: null, context: createMockContext({ get: vi.fn().mockImplementation((key: string) => { if (key === 'user') return null if (key === 'db') return createMockDatabase() if (key === 'enabledModules') return new Set(['core']) return null }) }) }), adminUser: () => ({ user: createMockUser({ roles: ['admin'] }), context: createMockContext({ get: vi.fn().mockImplementation((key: string) => { if (key === 'user') return createMockUser({ roles: ['admin'] }) if (key === 'db') return createMockDatabase() if (key === 'enabledModules') return new Set(['core']) return null }) }) }), limitedUser: () => ({ user: createMockUser({ roles: ['user'] }), context: createMockContext({ get: vi.fn().mockImplementation((key: string) => { if (key === 'user') return createMockUser({ roles: ['user'] }) if (key === 'db') return createMockDatabase() if (key === 'enabledModules') return new Set(['core']) return null }) }) }) }