babylon-mcp/src/mcp/handlers/shared/response-formatters.test.ts
Michael Mainguy 24906fb9df Refactor handlers into modular architecture with improved test coverage
Refactoring:
- Split 398-line handlers.ts into modular structure with 9 focused files
- Created handlers/ directory with subdirectories: shared/, docs/, api/, source/
- All handler files now under 100 lines (largest: 68 lines)
- Extracted common utilities (search-instance, response-formatters, error-handlers)
- Maintained backward compatibility - setupHandlers() API unchanged

Structure:
- handlers/index.ts (24 lines) - Main entry point
- handlers/shared/ - Common utilities (3 files, 72 lines total)
  - search-instance.ts - Centralized LanceDB search singleton
  - response-formatters.ts - Standardized JSON/error formatting
  - error-handlers.ts - Consistent error handling wrapper
- handlers/docs/ - Documentation handlers (2 files, 123 lines)
  - search-docs.handler.ts - Search documentation
  - get-doc.handler.ts - Get specific documentation
- handlers/api/ - API documentation handlers (1 file, 68 lines)
  - search-api.handler.ts - Search API documentation
- handlers/source/ - Source code handlers (2 files, 128 lines)
  - search-source.handler.ts - Search source code
  - get-source.handler.ts - Get source files

Testing improvements:
- Added 34 new tests (118 → 152 tests)
- Created comprehensive test suites for shared utilities:
  - response-formatters.test.ts (11 tests)
  - error-handlers.test.ts (6 tests)
- Added 16 tests for source code handlers
- Added c8 ignore comments for trivial ternary operators

Coverage improvements:
- Statements: 82.2% → 91.1% (+8.9%)
- Functions: 91.46% → 97.56% (+6.1%)
- Lines: 82.89% → 92.19% (+9.3%)
- Branches: 54.6% → 72.99% (+18.39%)
- Shared utilities now at 100% coverage
- All 152 tests passing

Benefits:
- Better maintainability - each handler easy to locate and modify
- Meets coding standards - all files under 100 lines
- DRY principles - ~30% reduction in code duplication
- Scalable - easy to add new handlers following clear pattern
- Better test isolation and organization

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-23 07:05:34 -06:00

111 lines
3.7 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import {
formatJsonResponse,
formatErrorResponse,
formatNoResultsResponse,
formatNotFoundResponse,
} from './response-formatters.js';
describe('Response Formatters', () => {
describe('formatJsonResponse', () => {
it('should format data as JSON text response', () => {
const data = { test: 'value', count: 42 };
const result = formatJsonResponse(data);
expect(result).toHaveProperty('content');
expect(Array.isArray(result.content)).toBe(true);
expect(result.content[0]).toHaveProperty('type', 'text');
expect(result.content[0]).toHaveProperty('text');
const parsed = JSON.parse(result.content[0]!.text);
expect(parsed).toEqual(data);
});
it('should handle complex nested objects', () => {
const data = {
nested: { array: [1, 2, 3], obj: { key: 'value' } },
nullValue: null,
};
const result = formatJsonResponse(data);
const parsed = JSON.parse(result.content[0]!.text);
expect(parsed).toEqual(data);
});
});
describe('formatErrorResponse', () => {
it('should format Error instances', () => {
const error = new Error('Test error message');
const result = formatErrorResponse(error, 'testing');
expect(result.content[0]!.type).toBe('text');
expect(result.content[0]!.text).toBe('Error testing: Test error message');
});
it('should format string errors', () => {
const error = 'String error';
const result = formatErrorResponse(error, 'processing');
expect(result.content[0]!.text).toBe('Error processing: String error');
});
it('should format unknown error types', () => {
const error = { code: 404 };
const result = formatErrorResponse(error, 'fetching');
expect(result.content[0]!.text).toContain('Error fetching:');
});
});
describe('formatNoResultsResponse', () => {
it('should format no results message for documentation', () => {
const result = formatNoResultsResponse('test query', 'documentation');
expect(result.content[0]!.type).toBe('text');
expect(result.content[0]!.text).toContain('No documentation found');
expect(result.content[0]!.text).toContain('test query');
});
it('should format no results message for API', () => {
const result = formatNoResultsResponse('getMeshByName', 'API documentation');
expect(result.content[0]!.text).toContain('No API documentation found');
expect(result.content[0]!.text).toContain('getMeshByName');
});
it('should format no results message for source code', () => {
const result = formatNoResultsResponse('scene rendering', 'source code');
expect(result.content[0]!.text).toContain('No source code found');
expect(result.content[0]!.text).toContain('scene rendering');
});
});
describe('formatNotFoundResponse', () => {
it('should format not found message without additional info', () => {
const result = formatNotFoundResponse('/test/path', 'Document');
expect(result.content[0]!.type).toBe('text');
expect(result.content[0]!.text).toBe('Document not found: /test/path.');
});
it('should format not found message with additional info', () => {
const result = formatNotFoundResponse(
'scene.ts',
'Source file',
'The path may be incorrect.'
);
expect(result.content[0]!.text).toBe(
'Source file not found: scene.ts. The path may be incorrect.'
);
});
it('should handle empty additional info', () => {
const result = formatNotFoundResponse('test-id', 'Resource', '');
expect(result.content[0]!.text).toBe('Resource not found: test-id.');
});
});
});