Skip to main content
Glama

Boilerplate MCP Server

by devkindhq
swell.orders.service.test.ts•17.5 kB
import swellOrdersService from './swell.orders.service.js'; import { swellClient } from '../utils/swell-client.util.js'; import { McpError } from '../utils/error.util.js'; import { SwellOrder, SwellOrdersList, OrderListOptions, OrderGetOptions, OrderStatusUpdateOptions, OrderAnalyticsOptions, OrderStatus, } 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 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('SwellOrdersService', () => { 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 mockOrdersList: SwellOrdersList = { count: 2, 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', }, { id: 'order-2', number: 'ORD-002', status: 'pending', account_id: 'customer-2', items: [ { id: 'item-2', product_id: 'product-2', product_name: 'Another Product', quantity: 1, price: 49.99, price_total: 49.99, }, ], billing: { name: 'Jane Smith', address1: '456 Oak Ave', city: 'Another City', state: 'NY', zip: '67890', country: 'US', }, shipping: { name: 'Jane Smith', address1: '456 Oak Ave', city: 'Another City', state: 'NY', zip: '67890', country: 'US', }, sub_total: 49.99, tax_total: 4.0, shipping_total: 5.99, grand_total: 59.98, date_created: '2023-01-02T00:00:00.000Z', date_updated: '2023-01-02T00:00:00.000Z', }, ], page: 1, pages: 1, }; it('should successfully fetch orders list with default options', async () => { mockAxios.get.mockResolvedValue(mockOrdersList); const result = await swellOrdersService.list(); expect(mockAxios.get).toHaveBeenCalledWith('/orders', {}); expect(result).toEqual(mockOrdersList); }); it('should fetch orders list with filtering options', async () => { const options: OrderListOptions = { page: 2, limit: 10, status: 'complete', account_id: 'customer-1', account_email: 'customer@example.com', date_created: { $gte: '2023-01-01', $lte: '2023-12-31' }, search: 'ORD-001', sort: 'date_created_desc', expand: ['items', 'payments'], }; mockAxios.get.mockResolvedValue(mockOrdersList); const result = await swellOrdersService.list(options); expect(mockAxios.get).toHaveBeenCalledWith('/orders', { page: 2, limit: 10, status: 'complete', account_id: 'customer-1', account_email: 'customer@example.com', date_created: { $gte: '2023-01-01', $lte: '2023-12-31' }, search: 'ORD-001', sort: 'date_created_desc', expand: 'items,payments', }); expect(result).toEqual(mockOrdersList); }); it('should handle multiple status filters', async () => { const options: OrderListOptions = { status: ['pending', 'complete'], }; mockAxios.get.mockResolvedValue(mockOrdersList); const result = await swellOrdersService.list(options); expect(mockAxios.get).toHaveBeenCalledWith('/orders', { status: { $in: ['pending', 'complete'] }, }); expect(result).toEqual(mockOrdersList); }); it('should initialize client if not initialized', async () => { mockSwellClient.isClientInitialized.mockReturnValue(false); mockAxios.get.mockResolvedValue(mockOrdersList); await swellOrdersService.list(); expect(mockSwellClient.initWithAutoConfig).toHaveBeenCalled(); }); it('should handle API errors', async () => { const apiError = new Error('API Error'); mockAxios.get.mockRejectedValue(apiError); await expect(swellOrdersService.list()).rejects.toThrow(McpError); }); it('should handle Zod validation errors', async () => { const invalidResponse = { invalid: 'data' }; mockAxios.get.mockResolvedValue(invalidResponse); await expect(swellOrdersService.list()).rejects.toThrow(McpError); }); }); describe('get', () => { const mockOrder: SwellOrder = { 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', }; it('should successfully fetch order by ID', async () => { mockAxios.get.mockResolvedValue(mockOrder); const result = await swellOrdersService.get('order-1'); expect(mockAxios.get).toHaveBeenCalledWith('/orders/order-1', {}); expect(result).toEqual(mockOrder); }); it('should fetch order with expand options', async () => { const options: OrderGetOptions = { expand: ['items', 'payments', 'shipments'], }; mockAxios.get.mockResolvedValue(mockOrder); const result = await swellOrdersService.get('order-1', options); expect(mockAxios.get).toHaveBeenCalledWith('/orders/order-1', { expand: 'items,payments,shipments', }); expect(result).toEqual(mockOrder); }); it('should throw error for empty order ID', async () => { await expect(swellOrdersService.get('')).rejects.toThrow( 'Order ID is required', ); }); it('should handle order not found', async () => { mockAxios.get.mockResolvedValue(null); await expect(swellOrdersService.get('nonexistent')).rejects.toThrow( 'Order not found: nonexistent', ); }); it('should handle API errors', async () => { const apiError = new Error('API Error'); mockAxios.get.mockRejectedValue(apiError); await expect(swellOrdersService.get('order-1')).rejects.toThrow( McpError, ); }); }); describe('updateStatus', () => { const mockUpdatedOrder: SwellOrder = { 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', }; it('should successfully update order status', async () => { mockAxios.put.mockResolvedValue(mockUpdatedOrder); const options: OrderStatusUpdateOptions = { status: 'complete', notes: 'Order fulfilled successfully', send_email: true, }; const result = await swellOrdersService.updateStatus( 'order-1', options, ); expect(mockAxios.put).toHaveBeenCalledWith('/orders/order-1', { status: 'complete', notes: 'Order fulfilled successfully', send_email: true, }); expect(result).toEqual(mockUpdatedOrder); }); it('should update status with minimal options', async () => { mockAxios.put.mockResolvedValue(mockUpdatedOrder); const options: OrderStatusUpdateOptions = { status: 'complete', }; const result = await swellOrdersService.updateStatus( 'order-1', options, ); expect(mockAxios.put).toHaveBeenCalledWith('/orders/order-1', { status: 'complete', }); expect(result).toEqual(mockUpdatedOrder); }); it('should throw error for empty order ID', async () => { const options: OrderStatusUpdateOptions = { status: 'complete' }; await expect( swellOrdersService.updateStatus('', options), ).rejects.toThrow('Order ID is required'); }); it('should throw error for missing status', async () => { const options = {} as OrderStatusUpdateOptions; await expect( swellOrdersService.updateStatus('order-1', options), ).rejects.toThrow('Order status is required'); }); it('should validate order status values', async () => { const options: OrderStatusUpdateOptions = { status: 'invalid_status' as OrderStatus, }; await expect( swellOrdersService.updateStatus('order-1', options), ).rejects.toThrow('Invalid order status: invalid_status'); }); it('should handle order not found during update', async () => { mockAxios.put.mockResolvedValue(null); const options: OrderStatusUpdateOptions = { status: 'complete' }; await expect( swellOrdersService.updateStatus('nonexistent', options), ).rejects.toThrow('Order not found: nonexistent'); }); it('should handle API errors during update', async () => { const apiError = new Error('Update API Error'); mockAxios.put.mockRejectedValue(apiError); const options: OrderStatusUpdateOptions = { status: 'complete' }; await expect( swellOrdersService.updateStatus('order-1', options), ).rejects.toThrow(McpError); }); }); describe('getAnalytics', () => { const mockAnalyticsData: SwellOrdersList = { count: 100, 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: 10, }; it('should successfully fetch analytics with default options', async () => { mockAxios.get.mockResolvedValue(mockAnalyticsData); const result = await swellOrdersService.getAnalytics(); expect(mockAxios.get).toHaveBeenCalledWith('/orders', { limit: 1000, sort: 'date_created', expand: 'items', }); expect(result).toEqual(mockAnalyticsData); }); it('should fetch analytics with date range', async () => { mockAxios.get.mockResolvedValue(mockAnalyticsData); const options: OrderAnalyticsOptions = { date_from: '2023-01-01', date_to: '2023-12-31', status: 'complete', }; const result = await swellOrdersService.getAnalytics(options); expect(mockAxios.get).toHaveBeenCalledWith('/orders', { limit: 1000, status: 'complete', date_created: { $gte: '2023-01-01', $lte: '2023-12-31', }, sort: 'date_created', expand: 'items', }); expect(result).toEqual(mockAnalyticsData); }); it('should handle analytics API errors', async () => { const apiError = new Error('Analytics API Error'); mockAxios.get.mockRejectedValue(apiError); await expect(swellOrdersService.getAnalytics()).rejects.toThrow( McpError, ); }); }); describe('parameter validation and formatting', () => { it('should properly format array parameters', async () => { const mockResponse: SwellOrdersList = { count: 0, results: [], page: 1, pages: 1, }; mockAxios.get.mockResolvedValue(mockResponse); const options: OrderListOptions = { expand: ['items', 'payments', 'shipments'], }; await swellOrdersService.list(options); expect(mockAxios.get).toHaveBeenCalledWith('/orders', { expand: 'items,payments,shipments', }); }); it('should handle date range parameters', async () => { const mockResponse: SwellOrdersList = { count: 0, results: [], page: 1, pages: 1, }; mockAxios.get.mockResolvedValue(mockResponse); const options: OrderListOptions = { date_created: { $gte: '2023-01-01', $lte: '2023-12-31', }, date_updated: { $gte: '2023-06-01', }, }; await swellOrdersService.list(options); expect(mockAxios.get).toHaveBeenCalledWith('/orders', { date_created: { $gte: '2023-01-01', $lte: '2023-12-31', }, date_updated: { $gte: '2023-06-01', }, }); }); it('should validate string parameters are trimmed', async () => { await expect(swellOrdersService.get(' \t \n ')).rejects.toThrow( 'Order ID is required', ); const options: OrderStatusUpdateOptions = { status: 'complete' }; await expect( swellOrdersService.updateStatus(' \t \n ', options), ).rejects.toThrow('Order ID is required'); }); }); describe('client recycling behavior', () => { it('should handle client recycling during requests', async () => { const mockResponse: SwellOrdersList = { count: 1, results: [ { id: 'order-1', number: 'ORD-001', status: 'complete', account_id: 'customer-1', items: [], 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, }; // 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 swellOrdersService.list(); expect(result).toEqual(mockResponse); expect(mockAxios.get).toHaveBeenCalledWith('/orders', {}); }); }); 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(swellOrdersService.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(swellOrdersService.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(swellOrdersService.list()).rejects.toThrow(McpError); }); it('should handle validation errors during status update', async () => { const validationError = new Error('Validation failed'); (validationError as any).status = 400; mockAxios.put.mockRejectedValue(validationError); const options: OrderStatusUpdateOptions = { status: 'complete' }; await expect( swellOrdersService.updateStatus('order-1', 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