Skip to main content
Glama
upload.test.ts8.49 kB
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'; vi.mock('fs', () => ({ default: { readFileSync: vi.fn(), }, })); vi.mock('../../src/utils', () => ({ fetchImageFromUrl: vi.fn(), base64ToBuffer: vi.fn(), normalizeFilePath: vi.fn(), })); vi.mock('../../src/services', async () => { const actual = await vi.importActual('../../src/services') as Record<string, any>; return { ...actual, UploadServiceFactory: { create: vi.fn(), }, }; }); // Mock process.env // Import after mocking import fs from 'fs'; import { fetchImageFromUrl, base64ToBuffer, normalizeFilePath } from '../../src/utils'; import { UploadServiceFactory, UploadImageArgs } from '../../src/services'; import { UploadTool } from '../../src/tools/upload'; // Get mocked functions const mockReadFileSync = vi.mocked(fs.readFileSync); const mockFetchImageFromUrl = vi.mocked(fetchImageFromUrl); const mockBase64ToBuffer = vi.mocked(base64ToBuffer); const mockNormalizeFilePath = vi.mocked(normalizeFilePath); const mockUploadServiceFactoryCreate = vi.mocked(UploadServiceFactory.create); describe('Upload Tool', () => { let mockBuffer: Buffer; let mockUploadServiceInstance: any; const originalEnv = { ...process.env }; beforeEach(() => { vi.clearAllMocks(); process.env = { ...originalEnv }; process.env.UPLOAD_SERVICE = 's3'; process.env.S3_BUCKET = 'mock-bucket'; process.env.AWS_ACCESS_KEY_ID = 'mock-key'; process.env.AWS_SECRET_ACCESS_KEY = 'mock-secret'; process.env.S3_REGION = 'us-east-1'; mockBuffer = Buffer.from('test image data'); mockUploadServiceInstance = { upload: vi.fn().mockResolvedValue({ url: 'https://example.com/test.jpg', filename: 'test.jpg', size: mockBuffer.length, format: 'jpg', service: 's3', // Default or example service metadata: { bucket: 'test-bucket', key: 'test.jpg' }, }), }; mockUploadServiceFactoryCreate.mockReturnValue(mockUploadServiceInstance); mockNormalizeFilePath.mockImplementation((path: string) => path); mockBase64ToBuffer.mockReturnValue(mockBuffer); mockFetchImageFromUrl.mockResolvedValue(mockBuffer); mockReadFileSync.mockReturnValue(mockBuffer); }); afterEach(() => { process.env = originalEnv; }); describe('UploadTool execution', () => { // Helper to call the tool's exec method const executeUploadTool = async (args: UploadImageArgs) => { const toolInstance = new UploadTool(args, mockUploadServiceInstance); return toolInstance.exec(); }; it('should upload image from imagePath', async () => { const args: UploadImageArgs = { imagePath: 'test.jpg', service: 's3' }; const result = await executeUploadTool(args); expect(mockUploadServiceInstance.upload).toHaveBeenCalledWith(mockBuffer, 'test.jpg', args); expect(result.isError).toBeFalsy(); if (result.content && result.content.length > 0 && result.content[0].type === 'text') { const parsedResult = JSON.parse(result.content[0].text); expect(parsedResult.url).toBe('https://example.com/test.jpg'); } else { throw new Error('Expected text content for successful upload from imagePath'); } }); it('should upload image from imageUrl', async () => { const args: UploadImageArgs = { imageUrl: 'http://example.com/image.png', service: 's3' }; const result = await executeUploadTool(args); expect(mockFetchImageFromUrl).toHaveBeenCalledWith('http://example.com/image.png'); expect(mockUploadServiceInstance.upload).toHaveBeenCalledWith(mockBuffer, 'image.png', args); expect(result.isError).toBeFalsy(); if (result.content && result.content.length > 0 && result.content[0].type === 'text') { const parsedResult = JSON.parse(result.content[0].text); expect(parsedResult.url).toBe('https://example.com/test.jpg'); } else { throw new Error('Expected text content for successful upload from imageUrl'); } }); it('should upload image from base64Image', async () => { const args: UploadImageArgs = { base64Image: 'base64stringdata', service: 's3' }; const result = await executeUploadTool(args); expect(mockBase64ToBuffer).toHaveBeenCalledWith('base64stringdata'); expect(mockUploadServiceInstance.upload).toHaveBeenCalledWith(mockBuffer, expect.any(String), args); expect(result.isError).toBeFalsy(); if (result.content && result.content.length > 0 && result.content[0].type === 'text') { const parsedResult = JSON.parse(result.content[0].text); expect(parsedResult.url).toBe('https://example.com/test.jpg'); } else { throw new Error('Expected text content for successful upload from base64Image'); } }); it('should return McpErrorResponse if no image source provided', async () => { const args: UploadImageArgs = {}; // Expecting McpError to be thrown by the tool's getInputBuffer method try { await executeUploadTool(args); } catch (error: any) { expect(error).toBeInstanceOf(McpError); expect(error.code).toBe(ErrorCode.InvalidParams); expect(error.message).toBe('MCP error -32602: One of imagePath, imageUrl, or base64Image must be provided'); } }); it('should use custom filename if provided', async () => { const args: UploadImageArgs = { imagePath: 'original.jpg', filename: 'custom-name', service: 's3' }; await executeUploadTool(args); expect(mockUploadServiceInstance.upload).toHaveBeenCalledWith(mockBuffer, 'custom-name.jpg', args); }); it('should handle filename with existing extension', async () => { const args: UploadImageArgs = { imagePath: 'path/to/image.png', filename: 'custom.png', service: 's3'}; await executeUploadTool(args); expect(mockUploadServiceInstance.upload).toHaveBeenCalledWith(mockBuffer, 'custom.png', args); }); it('should handle filename without extension (derive from imagePath)', async () => { const args: UploadImageArgs = { imagePath: 'path/to/image.jpeg', filename: 'customNoExt', service: 's3'}; await executeUploadTool(args); expect(mockUploadServiceInstance.upload).toHaveBeenCalledWith(mockBuffer, 'customNoExt.jpeg', args); }); it('should return McpErrorResponse if imagePath does not exist', async () => { mockReadFileSync.mockImplementation(() => { throw new Error('File not found'); }); const args: UploadImageArgs = { imagePath: 'nonexistent.jpg', service: 's3' }; try { await executeUploadTool(args); } catch (error: any) { expect(error).toBeInstanceOf(McpError); expect(error.code).toBe(ErrorCode.InvalidParams); expect(error.message).toBe('MCP error -32602: Failed to read image from path: nonexistent.jpg. File not found'); } }); it('should return McpErrorResponse if imageUrl fetch fails', async () => { mockFetchImageFromUrl.mockRejectedValue(new Error('Network error')); const args: UploadImageArgs = { imageUrl: 'http://badurl.com/image.jpg', service: 's3' }; const result = await executeUploadTool(args) as any; expect(result.isError).toBe(true); expect(result.content[0].text).toBe('Error uploading image: Network error'); }); it('should return McpErrorResponse if base64ToBuffer fails', async () => { mockBase64ToBuffer.mockImplementation(() => { throw new Error('Invalid base64'); }); const args: UploadImageArgs = { base64Image: 'invalidbase64==', service: 's3' }; const result = await executeUploadTool(args) as any; expect(result.isError).toBe(true); expect(result.content[0].text).toBe('Error uploading image: Invalid base64'); }); it('should correctly pass through all args to upload service', async () => { const fullArgs: UploadImageArgs = { imagePath: 'test.webp', service: 's3', filename: 'final-name', folder: 'test-folder', public: false, overwrite: true, tags: ['tag1', 'tag2'], metadata: { key1: 'value1' } }; await executeUploadTool(fullArgs); expect(mockUploadServiceInstance.upload).toHaveBeenCalledWith(mockBuffer, 'final-name.webp', fullArgs); }); }); });

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/BoomLinkAi/image-worker-mcp'

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