UNPKG

paper-search-mcp-nodejs

Version:

A Node.js MCP server for searching and downloading academic papers from multiple sources, including arXiv, PubMed, bioRxiv, Web of Science, and more.

657 lines (527 loc) โ€ข 14.2 kB
# AIPaper-assisant - Testing & Development Workflow ## ๐Ÿงช Testing Strategy ### Testing Philosophy - **Test-Driven Development**: Write tests before implementation - **Platform Isolation**: Each platform tested independently - **Error Scenario Coverage**: Test failure modes extensively - **Performance Validation**: Ensure rate limiting works correctly - **Integration Confidence**: Cross-platform compatibility verified ### Test Categories #### 1. Unit Tests **Purpose**: Test individual components in isolation **Coverage**: - Model validation (`PaperFactory`) - Rate limiting logic - Error handling utilities - Data transformation functions **Example**: ```typescript describe('PaperFactory', () => { it('should validate required fields', () => { const invalidData = { title: '', authors: [] }; expect(() => PaperFactory.create(invalidData)).toThrow(); }); it('should normalize DOI format', () => { const paper = PaperFactory.create({ doi: '10.1000/abc' }); expect(paper.doi).toBe('10.1000/abc'); }); }); ``` #### 2. Integration Tests **Purpose**: Test platform API integrations **Coverage**: - Actual API calls to platforms - Authentication validation - Response parsing - Error handling from APIs **Example**: ```typescript describe('ArxivSearcher Integration', () => { it('should search for papers successfully', async () => { const searcher = new ArxivSearcher(); const results = await searcher.search('machine learning'); expect(results).toBeInstanceOf(Array); expect(results.length).toBeGreaterThan(0); expect(results[0]).toHaveProperty('title'); expect(results[0]).toHaveProperty('authors'); }); it('should handle rate limiting', async () => { const searcher = new ArxivSearcher(); // Make multiple rapid requests const promises = Array(20).fill(null).map(() => searcher.search('test') ); await expect(Promise.all(promises)).resolves.not.toThrow(); }); }); ``` #### 3. End-to-End Tests **Purpose**: Test complete user workflows **Coverage**: - MCP tool registration - Full search workflows - Download processes - Multi-platform searches **Example**: ```typescript describe('E2E Search Workflow', () => { it('should search across multiple platforms', async () => { const client = await connectToMCP(); const results = await client.callTool('search_papers', { query: 'quantum computing', maxResults: 5 }); expect(results.papers).toBeDefined(); expect(results.papers.length).toBeLessThanOrEqual(5); expect(results.metadata.totalResults).toBeGreaterThan(0); }); }); ``` #### 4. Performance Tests **Purpose**: Validate system performance under load **Coverage**: - Rate limiting effectiveness - Concurrent request handling - Memory usage patterns - Response time benchmarks **Example**: ```typescript describe('Performance Tests', () => { it('should handle 100 concurrent searches', async () => { const startTime = Date.now(); const searches = Array(100).fill(null).map(() => searchPapers('artificial intelligence', { maxResults: 10 }) ); const results = await Promise.all(searches); const duration = Date.now() - startTime; expect(duration).toBeLessThan(30000); // 30 seconds max expect(results.every(r => r.success)).toBe(true); }); }); ``` #### 5. Security Tests **Purpose**: Ensure secure operation **Coverage**: - Input sanitization - API key protection - Error message safety - Rate limit enforcement **Example**: ```typescript describe('Security Tests', () => { it('should sanitize malicious input', async () => { const maliciousQuery = '<script>alert("xss")</script>'; const results = await searchPapers(maliciousQuery); // Verify no script tags in results const hasScript = results.papers.some(p => p.title.includes('<script>') || p.abstract.includes('<script>') ); expect(hasScript).toBe(false); }); it('should not expose API keys in errors', async () => { process.env.WOS_API_KEY = 'secret-key-123'; try { await searchPapers('test', { platform: 'wos' }); } catch (error) { expect(error.message).not.toContain('secret-key-123'); } }); }); ``` ### Test Structure ``` src/test/ โ”œโ”€โ”€ unit/ # Unit tests โ”‚ โ”œโ”€โ”€ models/ โ”‚ โ”œโ”€โ”€ utils/ โ”‚ โ””โ”€โ”€ platforms/ โ”œโ”€โ”€ integration/ # Integration tests โ”‚ โ”œโ”€โ”€ platforms/ โ”‚ โ””โ”€โ”€ api/ โ”œโ”€โ”€ e2e/ # End-to-end tests โ”‚ โ”œโ”€โ”€ mcp/ โ”‚ โ””โ”€โ”€ workflows/ โ”œโ”€โ”€ fixtures/ # Test data โ”‚ โ”œโ”€โ”€ papers/ โ”‚ โ””โ”€โ”€ responses/ โ””โ”€โ”€ helpers/ # Test utilities โ”œโ”€โ”€ mock-server/ โ””โ”€โ”€ test-data/ ``` ## ๐Ÿ”ง Development Workflow ### 1. Local Development Setup ```bash # 1. Clone and setup git clone https://github.com/yourusername/paper-search-nodejs.git cd paper-search-nodejs # 2. Install dependencies npm install # 3. Setup environment cp .env.example .env # Edit .env with your configurations # 4. Run in development mode npm run dev # 5. Run tests npm test ``` ### 2. Development Cycle #### Feature Development 1. **Create feature branch** ```bash git checkout -b feature/new-platform-integration ``` 2. **Write tests first** ```typescript // test/platforms/new-platform.test.ts describe('NewPlatformSearcher', () => { it('should implement search interface', async () => { const searcher = new NewPlatformSearcher(); const results = await searcher.search('test query'); expect(results).toBeInstanceOf(Array); }); }); ``` 3. **Implement feature** ```typescript // src/platforms/NewPlatformSearcher.ts export class NewPlatformSearcher extends PaperSource { constructor() { super('new-platform', { rateLimit: { tokens: 10, refillRate: 1 } }); } async search(query: string): Promise<Paper[]> { // Implementation } } ``` 4. **Run tests continuously** ```bash npm run test:watch ``` 5. **Refactor and optimize** ```bash npm run lint npm run format npm run type-check ``` #### Bug Fix Process 1. **Write failing test** 2. **Fix the bug** 3. **Verify test passes** 4. **Check for regressions** ### 3. Code Quality Tools #### Linting ```bash npm run lint # Run ESLint npm run lint:fix # Auto-fix issues ``` #### Formatting ```bash npm run format # Format with Prettier npm run format:check # Check formatting ``` #### Type Checking ```bash npm run type-check # TypeScript validation ``` #### Pre-commit Hooks Automatic checks before commits: - Lint validation - Type checking - Test execution - Format verification ### 4. Testing Commands ```bash # Run all tests npm test # Run specific test suite npm run test:unit npm run test:integration npm run test:e2e # Run with coverage npm run test:coverage # Run in watch mode npm run test:watch # Run specific test file npm test -- --testNamePattern="ArxivSearcher" # Debug tests npm run test:debug ``` ### 5. Platform-Specific Testing #### Testing New Platform Integration 1. **Create test file** ```bash touch src/test/platforms/your-platform.test.ts ``` 2. **Write comprehensive tests** ```typescript describe('YourPlatformSearcher', () => { let searcher: YourPlatformSearcher; beforeEach(() => { searcher = new YourPlatformSearcher(); }); describe('search', () => { it('should return valid paper structure', async () => { const results = await searcher.search('machine learning'); expect(results).toBeInstanceOf(Array); results.forEach(paper => { expect(paper).toHaveProperty('title'); expect(paper).toHaveProperty('authors'); expect(paper).toHaveProperty('year'); expect(paper.source).toBe('your-platform'); }); }); it('should handle empty results', async () => { const results = await searcher.search('xyzxyzxyz-noresults'); expect(results).toEqual([]); }); it('should handle API errors gracefully', async () => { // Mock API failure jest.spyOn(searcher, 'performSearch').mockRejectedValue( new Error('API Error') ); await expect(searcher.search('test')).rejects.toThrow('API Error'); }); }); describe('rate limiting', () => { it('should respect rate limits', async () => { const startTime = Date.now(); // Make multiple requests await Promise.all([ searcher.search('test1'), searcher.search('test2'), searcher.search('test3') ]); const duration = Date.now() - startTime; expect(duration).toBeGreaterThanOrEqual(2000); // Minimum delay }); }); }); ``` 3. **Test with real API** ```typescript describe('YourPlatform API Integration', () => { // Skip if no API key const describeIfKey = process.env.YOUR_PLATFORM_API_KEY ? describe : describe.skip; describeIfKey('with API key', () => { it('should authenticate successfully', async () => { const searcher = new YourPlatformSearcher(); const results = await searcher.search('test query'); expect(results.length).toBeGreaterThan(0); }); }); }); ``` ## ๐Ÿ“Š Test Coverage Guidelines ### Coverage Targets - **Statements**: > 85% - **Branches**: > 80% - **Functions**: > 90% - **Lines**: > 85% ### Coverage Reports ```bash # Generate coverage report npm run test:coverage # View HTML report open coverage/index.html # Check specific file coverage npm run test:coverage -- --testPathPattern=ArxivSearcher ``` ### What to Test #### Must Test - Core business logic - Error handling paths - Rate limiting behavior - Data transformation - Authentication flows #### Should Test - Edge cases - Boundary conditions - Concurrent operations - Performance benchmarks #### Optional - Simple getters/setters - Third-party integrations (mock) - Configuration loading ## ๐Ÿš€ Continuous Integration ### CI/CD Pipeline ```yaml # .github/workflows/ci.yml name: CI/CD Pipeline on: push: branches: [ main, develop ] pull_request: branches: [ main ] jobs: test: runs-on: ubuntu-latest strategy: matrix: node-version: [18.x, 20.x] steps: - uses: actions/checkout@v3 - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} cache: 'npm' - name: Install dependencies run: npm ci - name: Run linting run: npm run lint - name: Type check run: npm run type-check - name: Run tests run: npm run test:coverage - name: Upload coverage uses: codecov/codecov-action@v3 with: file: ./coverage/lcov.info ``` ### Pre-release Checks 1. **Version bump** ```bash npm version patch # or minor/major ``` 2. **Update changelog** ```bash npm run changelog ``` 3. **Full test suite** ```bash npm run test:all ``` 4. **Security audit** ```bash npm audit npm audit fix ``` 5. **Build verification** ```bash npm run build npm pack ``` ## ๐Ÿ—๏ธ Test Data Management ### Mock Data Strategy #### 1. Platform Responses ```typescript // test/fixtures/responses/arxiv-response.xml export const arxivResponse = ` <?xml version="1.0" encoding="UTF-8"?> <feed xmlns="http://www.w3.org/2005/Atom"> <entry> <title>Test Paper Title</title> <author><name>John Doe</name></author> <published>2024-01-01T00:00:00Z</published> </entry> </feed>`; ``` #### 2. Test Paper Objects ```typescript // test/fixtures/papers/sample-papers.ts export const samplePapers: Paper[] = [ { title: 'Attention Is All You Need', authors: ['Ashish Vaswani', 'Noam Shazeer'], year: 2017, doi: '10.5555/3295222.3295349', source: 'arxiv', pdfUrl: 'https://arxiv.org/pdf/1706.03762.pdf' } ]; ``` ### Test Environment Setup #### 1. Test Database (optional) ```typescript // test/helpers/test-db.ts export async function setupTestDB() { // Setup in-memory database for tests } export async function teardownTestDB() { // Cleanup } ``` #### 2. Mock Servers ```typescript // test/helpers/mock-server.ts export function createMockPlatform() { return http.createServer((req, res) => { if (req.url?.includes('/search')) { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(mockSearchResponse)); } }); } ``` ## ๐Ÿ“ˆ Quality Metrics ### Code Quality Gates #### Pre-commit - Lint passes - Type checks pass - Unit tests > 90% pass - No console.log statements #### Pre-push - All tests pass - Coverage > 85% - No security vulnerabilities - Documentation updated #### Pre-merge - CI pipeline passes - Code review approved - Integration tests pass - Performance benchmarks met ### Monitoring #### Test Flakiness ```bash # Run tests multiple times to detect flaky tests for i in {1..10}; do npm test -- --testNamePattern="unreliable test" done ``` #### Performance Regression ```bash # Benchmark current performance npm run test:performance:baseline # Compare with previous npm run test:performance:compare ``` ## ๐Ÿ”ง Debugging Tips ### Test Debugging 1. **Enable verbose output** ```bash DEBUG=* npm test ``` 2. **Run single test** ```bash npm test -- --testNamePattern="specific test name" ``` 3. **Debug in VS Code** ```json { "type": "node", "request": "launch", "name": "Debug Tests", "program": "${workspaceFolder}/node_modules/.bin/jest", "args": ["--runInBand", "--testNamePattern", "${input:testName}"] } ``` ### Platform Debugging 1. **Check API responses** ```typescript // Add logging to platform searcher async search(query: string): Promise<Paper[]> { console.log('Search request:', { query, timestamp: Date.now() }); const response = await this.makeRequest(query); console.log('Raw response:', response.data); return this.transformResults(response.data); } ``` 2. **Mock network issues** ```typescript // Test network failures jest.spyOn(axios, 'get').mockRejectedValue(new Error('Network error')); ``` --- *This testing and development workflow ensures high-quality code and reliable platform integrations. Regular updates to testing strategies should be made as the project evolves.*