Skip to main content
Glama
devkindhq

Boilerplate MCP Server

by devkindhq
swell.customers.service.test.ts18.8 kB
import { ErrorType, McpError } from '../utils/error.util.js'; import { swellClient } from '../utils/swell-client.util.js'; import swellCustomersService from './swell.customers.service.js'; import { CustomerAnalyticsOptions, CustomerGetOptions, CustomerListOptions, CustomerOrderHistoryOptions, CustomerSearchOptions, SwellCustomer, SwellCustomersList, } from './swell.customers.types.js'; import swellOrdersService from './swell.orders.service.js'; import { SwellOrdersList } from './swell.orders.types.js'; // Mock the swell client utility jest.mock('../utils/swell-client.util.js'); const mockSwellClient = swellClient as jest.Mocked<typeof swellClient>; // Mock the orders service jest.mock('./swell.orders.service.js'); const mockOrdersService = swellOrdersService as jest.Mocked< typeof swellOrdersService >; // Mock axios for the swell-node client const mockAxios = { get: jest.fn(), post: jest.fn(), put: jest.fn(), delete: jest.fn(), }; // Mock swell-node client const mockClient = { get: mockAxios.get, post: mockAxios.post, put: mockAxios.put, delete: mockAxios.delete, getClientStats: jest.fn(), }; describe('SwellCustomersService', () => { beforeEach(() => { jest.clearAllMocks(); jest.useFakeTimers(); // Setup mock client mockSwellClient.isClientInitialized.mockReturnValue(true); mockSwellClient.getClient.mockReturnValue(mockClient as any); mockSwellClient.initWithAutoConfig.mockImplementation(() => {}); // Reset axios mocks mockAxios.get.mockReset(); mockAxios.post.mockReset(); mockAxios.put.mockReset(); mockAxios.delete.mockReset(); }); afterEach(() => { jest.useRealTimers(); }); describe('list', () => { const mockCustomersList: SwellCustomersList = { count: 2, results: [ { id: 'customer-1', email: 'john.doe@example.com', first_name: 'John', last_name: 'Doe', phone: '+1-555-0123', date_created: '2023-01-01T00:00:00.000Z', order_count: 5, order_value: 299.95, addresses: [ { name: 'John Doe', address1: '123 Main St', city: 'Anytown', state: 'CA', zip: '12345', country: 'US', }, ], }, { id: 'customer-2', email: 'jane.smith@example.com', first_name: 'Jane', last_name: 'Smith', phone: '+1-555-0456', date_created: '2023-01-02T00:00:00.000Z', order_count: 3, order_value: 149.97, addresses: [], }, ], page: 1, pages: 1, }; it('should successfully fetch customers list with default options', async () => { mockAxios.get.mockResolvedValue(mockCustomersList); const result = await swellCustomersService.list(); expect(mockAxios.get).toHaveBeenCalledWith('/accounts', {}); expect(result).toEqual(mockCustomersList); }); it('should fetch customers list with filtering options', async () => { const options: CustomerListOptions = { page: 2, limit: 10, email: 'john@example.com', first_name: 'John', last_name: 'Doe', phone: '+1-555-0123', group_id: 'vip-customers', tags: ['premium', 'loyal'], date_created: { $gte: '2023-01-01', $lte: '2023-12-31' }, order_count: { $gte: 5 }, order_value: { $gte: 100 }, search: 'john', sort: 'order_value_desc', expand: ['orders', 'addresses'], }; mockAxios.get.mockResolvedValue(mockCustomersList); const result = await swellCustomersService.list(options); expect(mockAxios.get).toHaveBeenCalledWith('/accounts', { page: 2, limit: 10, email: 'john@example.com', first_name: 'John', last_name: 'Doe', phone: '+1-555-0123', group_id: 'vip-customers', tags: 'premium,loyal', date_created: { $gte: '2023-01-01', $lte: '2023-12-31' }, order_count: { $gte: 5 }, order_value: { $gte: 100 }, search: 'john', sort: 'order_value_desc', expand: 'orders,addresses', }); expect(result).toEqual(mockCustomersList); }); it('should initialize client if not initialized', async () => { mockSwellClient.isClientInitialized.mockReturnValue(false); mockAxios.get.mockResolvedValue(mockCustomersList); await swellCustomersService.list(); expect(mockSwellClient.initWithAutoConfig).toHaveBeenCalled(); }); it('should handle API errors', async () => { const apiError = new Error('API Error'); mockAxios.get.mockRejectedValue(apiError); await expect(swellCustomersService.list()).rejects.toThrow( McpError, ); }); it('should handle Zod validation errors', async () => { const invalidResponse = { invalid: 'data' }; mockAxios.get.mockResolvedValue(invalidResponse); await expect(swellCustomersService.list()).rejects.toThrow( McpError, ); }); }); describe('get', () => { const mockCustomer: SwellCustomer = { id: 'customer-1', email: 'john.doe@example.com', first_name: 'John', last_name: 'Doe', phone: '+1-555-0123', date_created: '2023-01-01T00:00:00.000Z', order_count: 5, order_value: 299.95, addresses: [ { name: 'John Doe', address1: '123 Main St', city: 'Anytown', state: 'CA', zip: '12345', country: 'US', }, ], }; it('should successfully fetch customer by ID', async () => { mockAxios.get.mockResolvedValue(mockCustomer); const result = await swellCustomersService.get('customer-1'); expect(mockAxios.get).toHaveBeenCalledWith( '/accounts/customer-1', {}, ); expect(result).toEqual(mockCustomer); }); it('should fetch customer with expand options', async () => { const options: CustomerGetOptions = { expand: ['orders', 'addresses'], }; mockAxios.get.mockResolvedValue(mockCustomer); const result = await swellCustomersService.get( 'customer-1', options, ); expect(mockAxios.get).toHaveBeenCalledWith('/accounts/customer-1', { expand: 'orders,addresses', }); expect(result).toEqual(mockCustomer); }); it('should throw error for empty customer ID', async () => { await expect(swellCustomersService.get('')).rejects.toThrow( 'Customer ID is required', ); }); it('should handle customer not found', async () => { mockAxios.get.mockResolvedValue(null); await expect( swellCustomersService.get('nonexistent'), ).rejects.toThrow('Customer not found: nonexistent'); }); it('should handle API errors', async () => { const apiError = new Error('API Error'); mockAxios.get.mockRejectedValue(apiError); await expect( swellCustomersService.get('customer-1'), ).rejects.toThrow(McpError); }); }); describe('search', () => { const mockSearchResults: SwellCustomersList = { count: 1, results: [ { id: 'customer-1', email: 'john.doe@example.com', first_name: 'John', last_name: 'Doe', phone: '+1-555-0123', date_created: '2023-01-01T00:00:00.000Z', order_count: 5, order_value: 299.95, addresses: [], }, ], page: 1, pages: 1, }; it('should successfully search customers', async () => { mockAxios.get.mockResolvedValue(mockSearchResults); const options: CustomerSearchOptions = { query: 'john', limit: 20, }; const result = await swellCustomersService.search(options); expect(mockAxios.get).toHaveBeenCalledWith('/accounts', { search: 'john', limit: 20, }); expect(result).toEqual(mockSearchResults); }); it('should search with additional filters', async () => { mockAxios.get.mockResolvedValue(mockSearchResults); const options: CustomerSearchOptions = { query: 'john', group_id: 'vip-customers', tags: ['premium'], sort: 'order_value_desc', expand: ['orders'], }; const result = await swellCustomersService.search(options); expect(mockAxios.get).toHaveBeenCalledWith('/accounts', { search: 'john', group_id: 'vip-customers', tags: 'premium', sort: 'order_value_desc', expand: 'orders', }); expect(result).toEqual(mockSearchResults); }); it('should throw error for empty search query', async () => { const options: CustomerSearchOptions = { query: '' }; await expect(swellCustomersService.search(options)).rejects.toThrow( 'Search query is required', ); }); it('should handle search API errors', async () => { const apiError = new Error('Search API Error'); mockAxios.get.mockRejectedValue(apiError); const options: CustomerSearchOptions = { query: 'john' }; await expect(swellCustomersService.search(options)).rejects.toThrow( McpError, ); }); }); describe('getOrderHistory', () => { const mockOrderHistory: SwellOrdersList = { count: 3, results: [ { id: 'order-1', number: 'ORD-001', status: 'complete', account_id: 'customer-1', items: [ { id: 'item-1', product_id: 'product-1', product_name: 'Test Product', quantity: 2, price: 29.99, price_total: 59.98, }, ], billing: { name: 'John Doe', address1: '123 Main St', city: 'Anytown', state: 'CA', zip: '12345', country: 'US', }, shipping: { name: 'John Doe', address1: '123 Main St', city: 'Anytown', state: 'CA', zip: '12345', country: 'US', }, sub_total: 59.98, tax_total: 4.8, shipping_total: 9.99, grand_total: 74.77, date_created: '2023-01-01T00:00:00.000Z', date_updated: '2023-01-01T00:00:00.000Z', }, ], page: 1, pages: 1, }; it('should successfully fetch customer order history', async () => { mockOrdersService.list.mockResolvedValue(mockOrderHistory); const options: CustomerOrderHistoryOptions = { customer_id: 'customer-1', }; const result = await swellCustomersService.getOrderHistory(options); expect(mockOrdersService.list).toHaveBeenCalledWith({ account_id: 'customer-1', page: undefined, limit: undefined, sort: 'date_created_desc', expand: ['items'], }); expect(result).toEqual(mockOrderHistory); }); it('should fetch order history with filters', async () => { mockOrdersService.list.mockResolvedValue(mockOrderHistory); const options: CustomerOrderHistoryOptions = { customer_id: 'customer-1', status: 'complete', page: 1, limit: 10, sort: 'date_created_asc', date_from: '2023-01-01', date_to: '2023-12-31', }; const result = await swellCustomersService.getOrderHistory(options); expect(mockOrdersService.list).toHaveBeenCalledWith({ account_id: 'customer-1', page: 1, limit: 10, sort: 'date_created_asc', expand: ['items'], status: 'complete', date_created: { $gte: '2023-01-01', $lte: '2023-12-31', }, }); expect(result).toEqual(mockOrderHistory); }); it('should throw error for empty customer ID', async () => { const options: CustomerOrderHistoryOptions = { customer_id: '', }; await expect( swellCustomersService.getOrderHistory(options), ).rejects.toThrow('Customer ID is required'); }); it('should handle order history API errors', async () => { const apiError = new Error('Order History API Error'); mockOrdersService.list.mockRejectedValue(apiError); const options: CustomerOrderHistoryOptions = { customer_id: 'customer-1', }; await expect( swellCustomersService.getOrderHistory(options), ).rejects.toThrow(McpError); }); }); describe('getAnalytics', () => { const mockAnalyticsData: SwellCustomersList = { count: 50, results: [ { id: 'customer-1', email: 'john.doe@example.com', first_name: 'John', last_name: 'Doe', phone: '+1-555-0123', date_created: '2023-01-01T00:00:00.000Z', order_count: 5, order_value: 299.95, addresses: [], }, ], page: 1, pages: 5, }; it('should successfully fetch analytics with default options', async () => { mockAxios.get.mockResolvedValue(mockAnalyticsData); const result = await swellCustomersService.getAnalytics(); expect(mockAxios.get).toHaveBeenCalledWith('/accounts', { limit: 1000, expand: 'orders', sort: 'order_value_desc', }); expect(result).toEqual(mockAnalyticsData); }); it('should fetch analytics for specific customer', async () => { const mockSingleCustomer: SwellCustomer = { id: 'customer-1', email: 'john.doe@example.com', first_name: 'John', last_name: 'Doe', phone: '+1-555-0123', date_created: '2023-01-01T00:00:00.000Z', order_count: 5, order_value: 299.95, addresses: [], }; mockAxios.get.mockResolvedValue(mockSingleCustomer); const options: CustomerAnalyticsOptions = { customer_id: 'customer-1', }; const result = await swellCustomersService.getAnalytics(options); expect(mockAxios.get).toHaveBeenCalledWith('/accounts/customer-1', { expand: 'orders', }); expect(result).toEqual({ count: 1, results: [mockSingleCustomer], page: 1, pages: 1, }); }); it('should fetch analytics with date range and group filter', async () => { mockAxios.get.mockResolvedValue(mockAnalyticsData); const options: CustomerAnalyticsOptions = { group_id: 'vip-customers', date_from: '2023-01-01', date_to: '2023-12-31', }; const result = await swellCustomersService.getAnalytics(options); expect(mockAxios.get).toHaveBeenCalledWith('/accounts', { limit: 1000, expand: 'orders', sort: 'order_value_desc', group_id: 'vip-customers', date_created: { $gte: '2023-01-01', $lte: '2023-12-31', }, }); expect(result).toEqual(mockAnalyticsData); }); it('should handle analytics API errors', async () => { const apiError = new Error('Analytics API Error'); mockAxios.get.mockRejectedValue(apiError); await expect(swellCustomersService.getAnalytics()).rejects.toThrow( McpError, ); }); }); describe('parameter validation and formatting', () => { it('should properly format array parameters', async () => { const mockResponse: SwellCustomersList = { count: 0, results: [], page: 1, pages: 1, }; mockAxios.get.mockResolvedValue(mockResponse); const options: CustomerListOptions = { tags: ['tag1', 'tag2', 'tag3'], expand: ['orders', 'addresses'], }; await swellCustomersService.list(options); expect(mockAxios.get).toHaveBeenCalledWith('/accounts', { tags: 'tag1,tag2,tag3', expand: 'orders,addresses', }); }); it('should handle complex query parameters', async () => { const mockResponse: SwellCustomersList = { count: 0, results: [], page: 1, pages: 1, }; mockAxios.get.mockResolvedValue(mockResponse); const options: CustomerListOptions = { date_created: { $gte: '2023-01-01', $lte: '2023-12-31', }, order_count: { $gte: 5, $lte: 100, }, order_value: { $gte: 100.0, }, }; await swellCustomersService.list(options); expect(mockAxios.get).toHaveBeenCalledWith('/accounts', { date_created: { $gte: '2023-01-01', $lte: '2023-12-31', }, order_count: { $gte: 5, $lte: 100, }, order_value: { $gte: 100.0, }, }); }); it('should validate string parameters are trimmed', async () => { await expect( swellCustomersService.get(' \t \n '), ).rejects.toThrow('Customer ID is required'); await expect( swellCustomersService.search({ query: ' \t \n ' }), ).rejects.toThrow('Search query is required'); await expect( swellCustomersService.getOrderHistory({ customer_id: ' \t \n ', }), ).rejects.toThrow('Customer ID is required'); }); }); describe('client recycling behavior', () => { it('should handle client recycling during requests', async () => { const mockResponse: SwellCustomersList = { count: 1, results: [ { id: 'customer-1', email: 'john.doe@example.com', first_name: 'John', last_name: 'Doe', phone: '+1-555-0123', date_created: '2023-01-01T00:00:00.000Z', order_count: 5, order_value: 299.95, addresses: [], }, ], page: 1, pages: 1, }; // Mock client stats to simulate recycling conditions mockClient.getClientStats.mockReturnValue({ activeClient: { createdAt: Date.now() - 20000, activeRequests: 0, totalRequests: 1500, ageMs: 20000, }, oldClientsCount: 0, oldClients: [], }); mockAxios.get.mockResolvedValue(mockResponse); // Advance timers to simulate time passing jest.advanceTimersByTime(16000); const result = await swellCustomersService.list(); expect(result).toEqual(mockResponse); expect(mockAxios.get).toHaveBeenCalledWith('/accounts', {}); }); }); describe('error handling scenarios', () => { it('should handle ECONNREFUSED errors', async () => { const connectionError = new Error('Connection refused'); (connectionError as any).code = 'ECONNREFUSED'; mockAxios.get.mockRejectedValue(connectionError); await expect(swellCustomersService.list()).rejects.toThrow( McpError, ); }); it('should handle timeout errors', async () => { const timeoutError = new Error('Request timeout'); (timeoutError as any).code = 'ECONNABORTED'; mockAxios.get.mockRejectedValue(timeoutError); await expect(swellCustomersService.list()).rejects.toThrow( McpError, ); }); it('should handle authentication errors', async () => { const authError = new Error('Unauthorized'); (authError as any).status = 401; mockAxios.get.mockRejectedValue(authError); await expect(swellCustomersService.list()).rejects.toThrow( McpError, ); }); it('should handle 404 errors', async () => { const notFoundError = new Error('Not Found'); (notFoundError as any).status = 404; mockAxios.get.mockRejectedValue(notFoundError); await expect( swellCustomersService.get('nonexistent'), ).rejects.toThrow(McpError); }); it('should handle validation errors', async () => { const invalidResponse = { invalid: 'data' }; mockAxios.get.mockResolvedValue(invalidResponse); await expect(swellCustomersService.list()).rejects.toThrow( McpError, ); }); it('should handle order history service errors', async () => { const orderServiceError = new McpError( 'Order service error', ErrorType.API_ERROR, 500, ); mockOrdersService.list.mockRejectedValue(orderServiceError); const options: CustomerOrderHistoryOptions = { customer_id: 'customer-1', }; await expect( swellCustomersService.getOrderHistory(options), ).rejects.toThrow(McpError); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/devkindhq/swell-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server