UNPKG

prodobit

Version:

Open-core business application development platform

606 lines (522 loc) 14.8 kB
import { http, HttpResponse } from 'msw' import { createMockUser, createMockItem, createMockParty, createMockSalesOrder, createMockTenant } from './test-helpers.js' /** * MSW (Mock Service Worker) handlers for API mocking * Used primarily for frontend testing and integration tests */ const BASE_URL = 'http://localhost:3001/api/v1' // Auth handlers export const authHandlers = [ // Login http.post(`${BASE_URL}/auth/login`, async ({ request }) => { const body = await request.json() as any if (body.email === 'test@example.com' && body.password === 'password') { return HttpResponse.json({ message: 'Login successful', user: createMockUser(), token: 'mock-jwt-token-123' }) } if (body.email === 'invalid@example.com') { return HttpResponse.json( { error: 'Invalid email or password' }, { status: 401 } ) } return HttpResponse.json( { error: 'Invalid email or password' }, { status: 401 } ) }), // Register http.post(`${BASE_URL}/auth/register`, async ({ request }) => { const body = await request.json() as any if (body.email === 'existing@example.com') { return HttpResponse.json( { error: 'User with this email already exists' }, { status: 409 } ) } if (!body.email || !body.password || !body.firstName || !body.lastName) { return HttpResponse.json( { error: 'Missing required fields' }, { status: 400 } ) } return HttpResponse.json({ message: 'Registration successful', user: createMockUser({ email: body.email, firstName: body.firstName, lastName: body.lastName }), token: 'mock-jwt-token-456' }, { status: 201 }) }), // Get current user http.get(`${BASE_URL}/auth/me`, ({ request }) => { const auth = request.headers.get('Authorization') if (!auth || !auth.startsWith('Bearer ')) { return HttpResponse.json( { error: 'Authentication required' }, { status: 401 } ) } return HttpResponse.json({ user: createMockUser() }) }), // Logout http.post(`${BASE_URL}/auth/logout`, () => { return HttpResponse.json({ message: 'Logout successful' }) }), // Refresh token http.post(`${BASE_URL}/auth/refresh`, async ({ request }) => { const body = await request.json() as any if (body.refreshToken === 'valid-refresh-token') { return HttpResponse.json({ message: 'Token refreshed successfully', token: 'new-jwt-token-789' }) } return HttpResponse.json( { error: 'Invalid refresh token' }, { status: 401 } ) }) ] // Items handlers export const itemsHandlers = [ // Get all items http.get(`${BASE_URL}/items`, ({ request }) => { const url = new URL(request.url) const category = url.searchParams.get('category') const search = url.searchParams.get('q') let mockItems = [ createMockItem({ id: 'item-1', name: 'Laptop Computer', sku: 'COMP-001', category: 'Electronics' }), createMockItem({ id: 'item-2', name: 'Wireless Mouse', sku: 'MOUSE-001', category: 'Electronics' }), createMockItem({ id: 'item-3', name: 'Office Chair', sku: 'CHAIR-001', category: 'Furniture' }) ] if (category) { mockItems = mockItems.filter(item => item.category === category) } if (search) { mockItems = mockItems.filter(item => item.name.toLowerCase().includes(search.toLowerCase()) || item.sku.toLowerCase().includes(search.toLowerCase()) ) } return HttpResponse.json({ items: mockItems, total: mockItems.length }) }), // Get single item http.get(`${BASE_URL}/items/:id`, ({ params }) => { if (params.id === 'non-existent') { return HttpResponse.json( { error: 'Item not found' }, { status: 404 } ) } return HttpResponse.json({ item: createMockItem({ id: params.id as string, itemAttributes: [ { attribute: { name: 'Color', type: 'text' }, value: 'Black' } ] }) }) }), // Create item http.post(`${BASE_URL}/items`, async ({ request }) => { const body = await request.json() as any if (!body.name || !body.sku) { return HttpResponse.json( { error: 'Name and SKU are required' }, { status: 400 } ) } if (body.sku === 'DUPLICATE-SKU') { return HttpResponse.json( { error: 'SKU already exists' }, { status: 409 } ) } const newItem = createMockItem({ id: `item-${Date.now()}`, ...body }) return HttpResponse.json({ message: 'Item created successfully', item: newItem }, { status: 201 }) }), // Update item http.put(`${BASE_URL}/items/:id`, async ({ params, request }) => { if (params.id === 'non-existent') { return HttpResponse.json( { error: 'Item not found' }, { status: 404 } ) } const body = await request.json() as any const updatedItem = createMockItem({ id: params.id as string, ...body }) return HttpResponse.json({ message: 'Item updated successfully', item: updatedItem }) }), // Delete item http.delete(`${BASE_URL}/items/:id`, ({ params }) => { if (params.id === 'non-existent') { return HttpResponse.json( { error: 'Item not found' }, { status: 404 } ) } return HttpResponse.json({ message: 'Item deleted successfully' }) }), // Search items http.get(`${BASE_URL}/items/search`, ({ request }) => { const url = new URL(request.url) const query = url.searchParams.get('q') if (!query) { return HttpResponse.json( { error: 'Search query is required' }, { status: 400 } ) } const searchResults = [ createMockItem({ id: 'search-1', name: `${query} Product 1`, sku: `${query.toUpperCase()}-001` }), createMockItem({ id: 'search-2', name: `${query} Product 2`, sku: `${query.toUpperCase()}-002` }) ] return HttpResponse.json({ items: searchResults, total: searchResults.length, query }) }), // Get items by category http.get(`${BASE_URL}/items/category/:category`, ({ params }) => { const category = params.category as string const categoryItems = [ createMockItem({ id: 'cat-1', name: `${category} Product 1`, category }), createMockItem({ id: 'cat-2', name: `${category} Product 2`, category }) ] return HttpResponse.json({ items: categoryItems, total: categoryItems.length, category }) }) ] // Parties handlers export const partiesHandlers = [ // Get all parties http.get(`${BASE_URL}/parties`, ({ request }) => { const url = new URL(request.url) const type = url.searchParams.get('type') let mockParties = [ createMockParty({ id: 'party-1', name: 'Customer A', type: 'customer' }), createMockParty({ id: 'party-2', name: 'Supplier B', type: 'supplier' }), createMockParty({ id: 'party-3', name: 'Customer C', type: 'customer' }) ] if (type) { mockParties = mockParties.filter(party => party.type === type) } return HttpResponse.json({ parties: mockParties, total: mockParties.length }) }), // Get single party http.get(`${BASE_URL}/parties/:id`, ({ params }) => { if (params.id === 'non-existent') { return HttpResponse.json( { error: 'Party not found' }, { status: 404 } ) } return HttpResponse.json({ party: createMockParty({ id: params.id as string }) }) }), // Create party http.post(`${BASE_URL}/parties`, async ({ request }) => { const body = await request.json() as any if (!body.name || !body.type) { return HttpResponse.json( { error: 'Name and type are required' }, { status: 400 } ) } const newParty = createMockParty({ id: `party-${Date.now()}`, ...body }) return HttpResponse.json({ message: 'Party created successfully', party: newParty }, { status: 201 }) }) ] // Sales Orders handlers export const salesOrdersHandlers = [ // Get all sales orders http.get(`${BASE_URL}/sales-orders`, ({ request }) => { const url = new URL(request.url) const customerId = url.searchParams.get('customerId') const status = url.searchParams.get('status') let mockOrders = [ createMockSalesOrder({ id: 'order-1', orderNumber: 'SO-2024-001', customerId: 'customer-1', status: 'draft' }), createMockSalesOrder({ id: 'order-2', orderNumber: 'SO-2024-002', customerId: 'customer-2', status: 'confirmed' }) ] if (customerId) { mockOrders = mockOrders.filter(order => order.customerId === customerId) } if (status) { mockOrders = mockOrders.filter(order => order.status === status) } return HttpResponse.json({ orders: mockOrders, total: mockOrders.length }) }), // Get single sales order http.get(`${BASE_URL}/sales-orders/:id`, ({ params }) => { if (params.id === 'non-existent') { return HttpResponse.json( { error: 'Sales order not found' }, { status: 404 } ) } return HttpResponse.json({ order: createMockSalesOrder({ id: params.id as string, customer: createMockParty({ type: 'customer' }), orderItems: [ { id: 'item-1', itemId: 'product-1', quantity: 2, unitPrice: '99.99', totalPrice: '199.98', item: createMockItem() } ] }) }) }), // Create sales order http.post(`${BASE_URL}/sales-orders`, async ({ request }) => { const body = await request.json() as any if (!body.customerId || !body.orderItems || body.orderItems.length === 0) { return HttpResponse.json( { error: 'Customer and order items are required' }, { status: 400 } ) } const newOrder = createMockSalesOrder({ id: `order-${Date.now()}`, orderNumber: `SO-2024-${String(Date.now()).slice(-3).padStart(3, '0')}`, ...body }) return HttpResponse.json({ message: 'Sales order created successfully', order: newOrder }, { status: 201 }) }), // Update sales order status http.patch(`${BASE_URL}/sales-orders/:id/status`, async ({ params, request }) => { if (params.id === 'non-existent') { return HttpResponse.json( { error: 'Sales order not found' }, { status: 404 } ) } const body = await request.json() as any if (!body.status) { return HttpResponse.json( { error: 'Status is required' }, { status: 400 } ) } const updatedOrder = createMockSalesOrder({ id: params.id as string, status: body.status }) return HttpResponse.json({ message: 'Order status updated successfully', order: updatedOrder }) }) ] // Tenants handlers export const tenantsHandlers = [ // Get current tenant http.get(`${BASE_URL}/tenants/current`, () => { return HttpResponse.json({ tenant: createMockTenant() }) }), // Update tenant settings http.put(`${BASE_URL}/tenants/current`, async ({ request }) => { const body = await request.json() as any const updatedTenant = createMockTenant({ ...body }) return HttpResponse.json({ message: 'Tenant updated successfully', tenant: updatedTenant }) }) ] // Health check handler export const healthHandlers = [ http.get('/health', () => { return HttpResponse.json({ status: 'ok', message: 'Server is healthy', timestamp: new Date().toISOString(), version: '1.0.0', modules: ['core', 'sales'], checks: { database: { status: 'ok' } } }) }), http.get(`${BASE_URL}/health`, () => { return HttpResponse.json({ status: 'ok', message: 'API is healthy', timestamp: new Date().toISOString() }) }) ] // Error simulation handlers export const errorHandlers = [ // Simulate 500 error http.get(`${BASE_URL}/error/500`, () => { return HttpResponse.json( { error: 'Internal server error' }, { status: 500 } ) }), // Simulate network timeout http.get(`${BASE_URL}/error/timeout`, () => { return new Promise(resolve => { setTimeout(() => { resolve(HttpResponse.json({ error: 'Request timeout' }, { status: 408 })) }, 5000) // 5 second delay }) }), // Simulate rate limiting http.post(`${BASE_URL}/error/rate-limit`, () => { return HttpResponse.json( { error: 'Too many requests', retryAfter: 60 }, { status: 429, headers: { 'Retry-After': '60' } } ) }) ] // Combine all handlers export const handlers = [ ...authHandlers, ...itemsHandlers, ...partiesHandlers, ...salesOrdersHandlers, ...tenantsHandlers, ...healthHandlers, ...errorHandlers ] // Handler groups for specific test scenarios export const handlerGroups = { auth: authHandlers, items: itemsHandlers, parties: partiesHandlers, salesOrders: salesOrdersHandlers, tenants: tenantsHandlers, health: healthHandlers, errors: errorHandlers, all: handlers } // Dynamic handler creation export const createDynamicHandlers = (config: { baseUrl?: string delayMs?: number failureRate?: number }) => { const { baseUrl = BASE_URL, delayMs = 0, failureRate = 0 } = config return handlers.map(handler => { const originalResolver = handler.resolver return { ...handler, resolver: async (input: any) => { // Add artificial delay if (delayMs > 0) { await new Promise(resolve => setTimeout(resolve, delayMs)) } // Simulate random failures if (failureRate > 0 && Math.random() < failureRate) { return HttpResponse.json( { error: 'Simulated server error' }, { status: 500 } ) } return originalResolver(input) } } }) }