UNPKG

@boundless-oss/atlas

Version:

Atlas - MCP Server for comprehensive startup project management

385 lines (329 loc) 12.3 kB
import { describe, it, expect, beforeEach, vi } from 'vitest'; import { setupDocumentationTools } from '../tools.js'; import { SqliteManager } from '../../../storage/sqlite-manager.js'; // Mock SqliteManager vi.mock('../../../storage/sqlite-manager.js', () => ({ SqliteManager: { getInstance: vi.fn() } })); describe('Documentation Module', () => { let tools: any; let mockDb: vi.Mocked<SqliteManager>; beforeEach(async () => { vi.clearAllMocks(); // Mock database operations mockDb = { run: vi.fn().mockResolvedValue({ success: true, changes: 1 }), get: vi.fn().mockResolvedValue({ success: true, data: null }), query: vi.fn().mockResolvedValue({ success: true, data: [] }), transaction: vi.fn().mockImplementation(async (callback) => { return await callback(mockDb); }) } as any; vi.mocked(SqliteManager.getInstance).mockReturnValue(mockDb); // Setup tools const toolRegistration = await setupDocumentationTools(); tools = {}; for (const tool of toolRegistration.tools) { tools[tool.name] = tool; } }); const createContext = (toolName: string) => ({ toolName, requestId: 'test-req-1', projectId: 'test-project', userId: 'test-user', timestamp: Date.now(), db: mockDb }); describe('generate_readme', () => { it('should generate README document in database', async () => { const input = { projectName: 'Test Project', description: 'A test project', features: ['Feature 1', 'Feature 2'], techStack: ['TypeScript', 'Node.js'], }; const result = await tools.generate_readme.execute(input, createContext('generate_readme')); expect(result.success).toBe(true); expect(result.data.message).toContain('README.md created for project "Test Project"'); expect(result.data.document.title).toBe('README.md'); expect(result.data.document.type).toBe('readme'); expect(result.data.document.created).toBe(true); expect(mockDb.run).toHaveBeenCalled(); }); it('should update existing README document', async () => { // Mock existing README mockDb.get.mockResolvedValueOnce({ success: true, data: { id: 'existing-readme-id', title: 'README.md', type: 'readme' } }); const input = { projectName: 'Updated Project', description: 'An updated test project', repository: 'frontend-repo', }; const result = await tools.generate_readme.execute(input, createContext('generate_readme')); expect(result.success).toBe(true); expect(result.data.message).toContain('README.md updated for project "Updated Project"'); expect(result.data.document.updated).toBe(true); expect(mockDb.run).toHaveBeenCalledWith( expect.stringContaining('UPDATE documents'), expect.any(Array) ); }); it('should handle database errors gracefully', async () => { mockDb.run.mockResolvedValueOnce({ success: false, error: 'Database error' }); const input = { projectName: 'Test Project', description: 'A test project', }; const result = await tools.generate_readme.execute(input, createContext('generate_readme')); expect(result.success).toBe(false); expect(result.error).toBeDefined(); expect(result.error?.code).toBe('DATABASE_ERROR'); }); }); describe('generate_claude_config', () => { it('should generate CLAUDE.md configuration', async () => { const input = { tddMode: 'strict' as const, testCommand: 'npm test', lintCommand: 'npm run lint', customInstructions: ['Use TypeScript', 'Follow TDD'], }; const result = await tools.generate_claude_config.execute(input, createContext('generate_claude_config')); expect(result.success).toBe(true); expect(result.data.message).toBe('CLAUDE.md configuration created'); expect(result.data.config.tddMode).toBe('strict'); expect(result.data.document.created).toBe(true); expect(mockDb.run).toHaveBeenCalled(); }); it('should update existing CLAUDE.md configuration', async () => { // Mock existing CLAUDE.md mockDb.get.mockResolvedValueOnce({ success: true, data: { id: 'existing-claude-id', title: 'CLAUDE.md', type: 'claude-config' } }); const input = { tddMode: 'moderate' as const, buildCommand: 'npm run build:prod', }; const result = await tools.generate_claude_config.execute(input, createContext('generate_claude_config')); expect(result.success).toBe(true); expect(result.data.message).toBe('CLAUDE.md configuration updated'); expect(result.data.document.updated).toBe(true); expect(mockDb.run).toHaveBeenCalledWith( expect.stringContaining('UPDATE documents'), expect.any(Array) ); }); }); describe('create_documentation', () => { it('should create API documentation', async () => { const input = { type: 'api' as const, title: 'REST API', content: 'API endpoints documentation...', }; const result = await tools.create_documentation.execute(input, createContext('create_documentation')); expect(result.success).toBe(true); expect(result.data.message).toBe('Documentation created: API.md'); expect(result.data.document.type).toBe('api'); expect(result.data.document.path).toBe('docs/API.md'); expect(mockDb.run).toHaveBeenCalled(); }); it('should create custom documentation with repository context', async () => { const input = { type: 'custom' as const, title: 'Mobile Setup Guide', content: 'How to set up the mobile development environment...', repository: 'mobile-repo', }; const result = await tools.create_documentation.execute(input, createContext('create_documentation')); expect(result.success).toBe(true); expect(result.data.message).toBe('Documentation created: mobile-setup-guide.md'); expect(result.data.document.path).toBe('mobile-repo/docs/mobile-setup-guide.md'); expect(mockDb.run).toHaveBeenCalled(); }); }); describe('list_documents', () => { it('should list documentation files', async () => { mockDb.query.mockResolvedValueOnce({ success: true, data: [ { id: 'doc-1', title: 'README.md', type: 'readme', path: 'README.md', status: 'published', author: 'test-user', version: 1, tags: '["readme", "documentation"]', created_at: Date.now(), updated_at: Date.now(), content: '# Test Project\n\nThis is a test project...' }, { id: 'doc-2', title: 'API Documentation', type: 'api', path: 'docs/API.md', status: 'draft', author: 'test-user', version: 2, tags: '["api", "documentation"]', created_at: Date.now(), updated_at: Date.now(), content: '# API\n\nAPI documentation...' } ] }); const result = await tools.list_documents.execute({}, createContext('list_documents')); expect(result.success).toBe(true); expect(result.data.documents).toHaveLength(2); expect(result.data.documents[0].title).toBe('README.md'); expect(result.data.documents[1].title).toBe('API Documentation'); expect(mockDb.query).toHaveBeenCalled(); }); it('should filter documents by type', async () => { mockDb.query.mockResolvedValueOnce({ success: true, data: [] }); const result = await tools.list_documents.execute({ type: 'api' }, createContext('list_documents')); expect(result.success).toBe(true); expect(mockDb.query).toHaveBeenCalledWith( expect.stringContaining('AND type = ?'), expect.arrayContaining(['api']) ); }); }); describe('update_document', () => { it('should update an existing document', async () => { // Mock document check mockDb.get.mockResolvedValueOnce({ success: true, data: { id: 'doc-1', title: 'Old Title', type: 'api' } }); const input = { documentId: 'doc-1', title: 'Updated API Documentation', status: 'published' as const, tags: ['api', 'v2', 'rest'] }; const result = await tools.update_document.execute(input, createContext('update_document')); expect(result.success).toBe(true); expect(result.data.message).toBe('Document "Updated API Documentation" updated successfully'); expect(result.data.changes).toContain('title'); expect(result.data.changes).toContain('status'); expect(result.data.changes).toContain('tags'); expect(mockDb.run).toHaveBeenCalled(); }); it('should handle document not found', async () => { mockDb.get.mockResolvedValueOnce({ success: true, data: null }); const result = await tools.update_document.execute({ documentId: 'non-existent', title: 'New Title' }, createContext('update_document')); expect(result.success).toBe(false); expect(result.error?.code).toBe('RESOURCE_NOT_FOUND'); }); }); describe('search_documents', () => { it('should search documents by content or title', async () => { mockDb.query.mockResolvedValueOnce({ success: true, data: [ { id: 'doc-1', title: 'API Reference', type: 'api', path: 'docs/api.md', content: 'This is the API reference guide for our REST endpoints...', updated_at: Date.now() }, { id: 'doc-2', title: 'Getting Started', type: 'guide', path: 'docs/getting-started.md', content: 'Welcome to our API. This guide will help you...', updated_at: Date.now() } ] }); const result = await tools.search_documents.execute({ query: 'API' }, createContext('search_documents')); expect(result.success).toBe(true); expect(result.data.results).toHaveLength(2); expect(result.data.results[0].title).toBe('API Reference'); expect(result.data.results[0].score).toBe(1); // Title match expect(mockDb.query).toHaveBeenCalledWith( expect.stringContaining('LIKE ?'), expect.arrayContaining(['%API%']) ); }); it('should filter search by document type', async () => { mockDb.query.mockResolvedValueOnce({ success: true, data: [] }); const result = await tools.search_documents.execute({ query: 'test', type: 'api' }, createContext('search_documents')); expect(result.success).toBe(true); expect(mockDb.query).toHaveBeenCalledWith( expect.stringContaining('AND type = ?'), expect.arrayContaining(['api']) ); }); }); describe('tool registration', () => { it('should register all expected tools', () => { const expectedTools = [ 'generate_readme', 'generate_claude_config', 'create_documentation', 'list_documents', 'update_document', 'search_documents' ]; for (const toolName of expectedTools) { expect(tools[toolName]).toBeDefined(); expect(typeof tools[toolName].execute).toBe('function'); } }); }); describe('error handling', () => { it('should handle database errors gracefully', async () => { mockDb.run.mockResolvedValueOnce({ success: false, error: 'Database error' }); const input = { projectName: 'Test Project', description: 'A test project', }; const result = await tools.generate_readme.execute(input, createContext('generate_readme')); expect(result.success).toBe(false); expect(result.error).toBeDefined(); }); }); });