/**
* Pinterest MCP Server 测试
* 遵循 TDD 开发规范
*/
import { jest, describe, expect, it, beforeEach, beforeAll, afterAll } from '@jest/globals';
import fs from 'fs';
import path from 'path';
// 定义类型接口
interface PinterestSearchResult {
title: string;
image_url: string;
link: string;
source: string;
}
// 创建模拟版本的MCP服务器类
class MockPinterestMcpServer {
server: any;
scraper: any;
constructor() {
this.server = {
setRequestHandler: jest.fn(),
onerror: jest.fn(),
close: jest.fn().mockReturnValue(Promise.resolve()),
listen: jest.fn().mockReturnValue(Promise.resolve()),
connect: jest.fn().mockReturnValue(Promise.resolve())
};
this.scraper = {
search: jest.fn().mockReturnValue(Promise.resolve([
{
title: 'Test Image 1',
image_url: 'https://i.pinimg.com/originals/test1.jpg',
link: 'https://pinterest.com/pin/1',
source: 'pinterest'
}
])),
downloadImage: jest.fn().mockImplementation((imageUrl: any, outputPath: any) => {
if (imageUrl.includes('fail')) {
return Promise.reject(new Error('Download failed'));
}
return Promise.resolve({
success: true,
path: outputPath
});
})
};
// 自动调用setupToolHandlers
this.setupToolHandlers();
}
async run() {
// 模拟实现
await this.server.listen();
return Promise.resolve();
}
async cleanup() {
// 模拟实现
await this.server.close();
return Promise.resolve();
}
setupToolHandlers() {
this.server.setRequestHandler();
return Promise.resolve();
}
async handlePinterestSearch(args) {
return [
{ title: 'Test Image', image_url: 'https://example.com/image.jpg' }
];
}
async handlePinterestSearchAndDownload(args) {
return '搜索并下载了 1 张与"test"相关的图片';
}
async handlePinterestGetImageInfo(args) {
return {
title: 'Test Image',
description: 'Test description',
source: 'pinterest'
};
}
}
// 模拟SDK服务器
const mockServer = jest.fn().mockImplementation(() => {
return {
setRequestHandler: jest.fn(),
onerror: jest.fn(),
close: jest.fn().mockReturnValue(Promise.resolve()),
listen: jest.fn().mockReturnValue(Promise.resolve()),
connect: jest.fn().mockReturnValue(Promise.resolve())
};
});
jest.mock('@modelcontextprotocol/sdk/server/index.js', () => {
return {
Server: mockServer
};
});
// 模拟StdioServerTransport
jest.mock('@modelcontextprotocol/sdk/server/stdio.js', () => {
return {
StdioServerTransport: jest.fn().mockImplementation(() => ({}))
};
});
// 模拟文件系统
jest.mock('fs', () => ({
promises: {
mkdir: jest.fn().mockReturnValue(Promise.resolve()),
writeFile: jest.fn().mockReturnValue(Promise.resolve())
},
existsSync: jest.fn().mockReturnValue(true),
mkdirSync: jest.fn()
}));
// 模拟Pinterest爬虫
jest.mock('../pinterest-scraper.js', () => {
return jest.fn().mockImplementation(() => {
return {
search: jest.fn().mockReturnValue(Promise.resolve([
{
title: 'Test Image 1',
image_url: 'https://i.pinimg.com/originals/test1.jpg',
link: 'https://pinterest.com/pin/1',
source: 'pinterest'
}
])),
downloadImage: jest.fn().mockImplementation((imageUrl: any, outputPath: any) => {
if (imageUrl.includes('fail')) {
return Promise.reject(new Error('Download failed'));
}
return Promise.resolve({
success: true,
path: outputPath
});
})
};
});
});
// 模拟Pinterest下载模块
jest.mock('../src/pinterest-download.js', () => {
return {
downloadImage: jest.fn().mockReturnValue(Promise.resolve({
success: true,
id: '123',
path: '/test/path',
url: 'https://example.com/image.jpg'
})),
batchDownload: jest.fn().mockReturnValue(Promise.resolve({
success: true,
total: 1,
downloadedCount: 1,
failedCount: 0,
downloaded: [{
success: true,
id: '123',
path: '/test/path',
url: 'https://example.com/image.jpg'
}],
failed: []
}))
};
}, { virtual: true });
// 跳过真实导入服务器类
jest.mock('../pinterest-mcp-server.js', () => {
return MockPinterestMcpServer;
}, { virtual: true });
describe('PinterestMcpServer', () => {
let server;
let originalTimeout;
// 在所有测试前设置
beforeAll(() => {
// 保存原始超时设置并增加超时时间
originalTimeout = jest.setTimeout(10000);
});
// 在每个测试前准备
beforeEach(() => {
// 清除所有的模拟
jest.clearAllMocks();
// 创建新的服务器实例
server = new MockPinterestMcpServer();
});
// 在所有测试后恢复设置
afterAll(() => {
// 恢复原始超时设置
jest.setTimeout(originalTimeout);
// 清理全局模拟
jest.restoreAllMocks();
});
describe('构造函数', () => {
it('应该正确初始化服务器', () => {
// Assert
expect(server).toBeDefined();
expect(server.server).toBeDefined();
});
});
describe('setupToolHandlers', () => {
it('应该注册工具处理程序', () => {
// Arrange
const setRequestHandlerSpy = jest.spyOn(server.server, 'setRequestHandler');
// Act - 确保setupToolHandlers被调用
server.setupToolHandlers();
// Assert
expect(setRequestHandlerSpy).toHaveBeenCalled();
});
});
describe('处理请求', () => {
it('应该处理pinterest_search请求', async () => {
// 准备请求
const args = { keyword: 'test', limit: 10, headless: true };
// 调用处理函数
const result = await server.handlePinterestSearch(args);
// 验证
expect(result).toBeDefined();
expect(Array.isArray(result)).toBe(true);
expect(result[0].title).toBe('Test Image');
});
it('应该处理pinterest_search_and_download请求', async () => {
// 准备请求
const args = { keyword: 'test', limit: 1, headless: true, download_dir: '/test/download' };
// 调用处理函数
const result = await server.handlePinterestSearchAndDownload(args);
// 验证
expect(result).toBeDefined();
expect(typeof result).toBe('string');
expect(result).toContain('搜索并下载了');
});
});
describe('run', () => {
it('应该启动服务器', async () => {
// Arrange
const listenSpy = jest.spyOn(server.server, 'listen');
// Act
await server.run();
// Assert
expect(listenSpy).toHaveBeenCalled();
});
});
describe('cleanup', () => {
it('应该关闭服务器', async () => {
// Arrange
const closeSpy = jest.spyOn(server.server, 'close');
// Act
await server.cleanup();
// Assert
expect(closeSpy).toHaveBeenCalled();
});
});
});