Skip to main content
Glama
project-suite-manager.test.ts11.3 kB
/** * Unit Tests for ProjectSuiteManager */ import { describe, test, expect, beforeEach, jest } from '@jest/globals'; import { ProjectSuiteManager } from '../../src/tools/project-suite-manager'; import { TestRailService } from '../../src/utils/testrail-service'; // Mock TestRailService jest.mock('../../src/utils/testrail-service'); const MockedTestRailService = TestRailService as jest.MockedClass<typeof TestRailService>; describe('ProjectSuiteManager', () => { let projectSuiteManager: ProjectSuiteManager; let mockTestRailService: jest.Mocked<TestRailService>; beforeEach(() => { jest.clearAllMocks(); mockTestRailService = new MockedTestRailService({ baseUrl: 'https://test.testrail.io', username: 'test@example.com', apiKey: 'test-key' }) as jest.Mocked<TestRailService>; projectSuiteManager = new ProjectSuiteManager(mockTestRailService); }); describe('createAdvancedProject', () => { test('should create project with basic configuration', async () => { const mockProject = { id: 1, name: 'Test Project', suite_mode: 1 }; mockTestRailService.addProject.mockResolvedValue(mockProject); const result = await projectSuiteManager.createAdvancedProject({ name: 'Test Project', suiteMode: 1 }); expect(result.content[0].text).toContain('"success": true'); expect(mockTestRailService.addProject).toHaveBeenCalledWith({ name: 'Test Project', announcement: undefined, show_announcement: undefined, suite_mode: 1 }); }); test('should create project with default suites when template provided', async () => { const mockProject = { id: 1, name: 'Test Project', suite_mode: 3 }; const mockSuite = { id: 1, name: 'Functional Tests', project_id: 1 }; mockTestRailService.addProject.mockResolvedValue(mockProject); mockTestRailService.addSuite.mockResolvedValue(mockSuite); const result = await projectSuiteManager.createAdvancedProject({ name: 'Test Project', suiteMode: 3, template: { createDefaultSuites: true, suiteNames: ['Functional Tests', 'API Tests'] } }); expect(result.content[0].text).toContain('"success": true'); expect(mockTestRailService.addSuite).toHaveBeenCalledTimes(2); }); test('should handle API errors gracefully', async () => { mockTestRailService.addProject.mockRejectedValue(new Error('API Error')); const result = await projectSuiteManager.createAdvancedProject({ name: 'Test Project' }); expect(result.content[0].text).toContain('"success": false'); expect(result.content[0].text).toContain('API Error'); }); }); describe('analyzeProjectStructure', () => { test('should analyze project structure successfully', async () => { const mockProject = { id: 1, name: 'Test Project', suite_mode: 1 }; const mockSuites = [ { id: 1, name: 'Suite 1', project_id: 1 }, { id: 2, name: 'Suite 2', project_id: 1 } ]; const mockSections = [ { id: 1, name: 'Section 1', suite_id: 1 }, { id: 2, name: 'Section 2', suite_id: 1 } ]; const mockCases = [ { id: 1, title: 'Test Case 1', section_id: 1 }, { id: 2, title: 'Test Case 2', section_id: 1 } ]; mockTestRailService.getProject.mockResolvedValue(mockProject); mockTestRailService.getSuites.mockResolvedValue(mockSuites); mockTestRailService.getSections.mockResolvedValue(mockSections); mockTestRailService.getCases.mockResolvedValue(mockCases); const result = await projectSuiteManager.analyzeProjectStructure({ projectId: 1, includeStatistics: true, includeRecommendations: true }); expect(result.content[0].text).toContain('"success": true'); expect(result.content[0].text).toContain('"suites": 2'); expect(result.content[0].text).toContain('"sections": 4'); // 2 sections per suite expect(result.content[0].text).toContain('"cases": 4'); // 2 cases per suite }); test('should include recommendations when requested', async () => { const mockProject = { id: 1, name: 'Test Project', suite_mode: 1 }; const mockSuites = [{ id: 1, name: 'Empty Suite', project_id: 1 }]; mockTestRailService.getProject.mockResolvedValue(mockProject); mockTestRailService.getSuites.mockResolvedValue(mockSuites); mockTestRailService.getSections.mockResolvedValue([]); mockTestRailService.getCases.mockResolvedValue([]); const result = await projectSuiteManager.analyzeProjectStructure({ projectId: 1, includeRecommendations: true }); expect(result.content[0].text).toContain('"success": true'); expect(result.content[0].text).toContain('recommendations'); }); }); describe('bulkManageSuites', () => { test('should execute create operations successfully', async () => { const mockSuite = { id: 1, name: 'New Suite', project_id: 1 }; mockTestRailService.addSuite.mockResolvedValue(mockSuite); const result = await projectSuiteManager.bulkManageSuites({ projectId: 1, operations: [ { type: 'create', data: { name: 'New Suite', description: 'Test suite' } } ] }); expect(result.content[0].text).toContain('"success": true'); expect(result.content[0].text).toContain('"executed": 1'); expect(mockTestRailService.addSuite).toHaveBeenCalledWith(1, { name: 'New Suite', description: 'Test suite' }); }); test('should validate operations when requested', async () => { const result = await projectSuiteManager.bulkManageSuites({ projectId: 1, operations: [ { type: 'update', // Missing suiteId - should fail validation data: { name: 'Updated Suite' } } ], validateBefore: true }); expect(result.content[0].text).toContain('"success": false'); expect(result.content[0].text).toContain('Suite ID required'); }); test('should perform dry run without actual changes', async () => { const result = await projectSuiteManager.bulkManageSuites({ projectId: 1, operations: [ { type: 'create', data: { name: 'Test Suite' } } ], dryRun: true }); expect(result.content[0].text).toContain('"success": true'); expect(result.content[0].text).toContain('"skipped": 1'); expect(mockTestRailService.addSuite).not.toHaveBeenCalled(); }); }); describe('createAdvancedSuite', () => { test('should create suite with functional template', async () => { const mockSuite = { id: 1, name: 'Functional Tests', project_id: 1 }; const mockSection = { id: 1, name: 'Authentication', suite_id: 1 }; mockTestRailService.addSuite.mockResolvedValue(mockSuite); mockTestRailService.addSection.mockResolvedValue(mockSection); const result = await projectSuiteManager.createAdvancedSuite({ projectId: 1, name: 'Functional Tests', template: 'functional' }); expect(result.content[0].text).toContain('"success": true'); expect(mockTestRailService.addSuite).toHaveBeenCalledWith(1, { name: 'Functional Tests', description: 'Comprehensive functional testing suite covering core application features' }); }); test('should create suite with custom structure', async () => { const mockSuite = { id: 1, name: 'Custom Suite', project_id: 1 }; const mockSection = { id: 1, name: 'Custom Section', suite_id: 1 }; mockTestRailService.addSuite.mockResolvedValue(mockSuite); mockTestRailService.addSection.mockResolvedValue(mockSection); const result = await projectSuiteManager.createAdvancedSuite({ projectId: 1, name: 'Custom Suite', structure: { sections: [ { name: 'Custom Section', description: 'Custom section description' } ], sampleCases: false } }); expect(result.content[0].text).toContain('"success": true'); expect(mockTestRailService.addSection).toHaveBeenCalledWith(1, { name: 'Custom Section', description: 'Custom section description', suite_id: 1 }); }); test('should handle template not found', async () => { const mockSuite = { id: 1, name: 'Unknown Template Suite', project_id: 1 }; mockTestRailService.addSuite.mockResolvedValue(mockSuite); const result = await projectSuiteManager.createAdvancedSuite({ projectId: 1, name: 'Unknown Template Suite', template: 'nonexistent' as any }); expect(result.content[0].text).toContain('"success": true'); // Should fall back to default template }); }); describe('error handling', () => { test('should handle network errors', async () => { mockTestRailService.getProject.mockRejectedValue(new Error('Network error')); const result = await projectSuiteManager.analyzeProjectStructure({ projectId: 1 }); expect(result.content[0].text).toContain('"success": false'); expect(result.content[0].text).toContain('Network error'); expect(result.isError).toBe(true); }); test('should handle validation errors', async () => { const result = await projectSuiteManager.bulkManageSuites({ projectId: 1, operations: [ { type: 'delete', // Missing suiteId data: {} } ], validateBefore: true }); expect(result.content[0].text).toContain('"success": false'); expect(result.content[0].text).toContain('validation'); }); }); describe('helper methods', () => { test('should calculate section depth correctly', async () => { // This would test private helper methods if they were exposed // For now, we test through public methods that use them const mockProject = { id: 1, name: 'Test Project', suite_mode: 1 }; const mockSuites = [{ id: 1, name: 'Suite 1', project_id: 1 }]; const mockSections = [ { id: 1, name: 'Parent Section', suite_id: 1, parent_id: null, depth: 1 }, { id: 2, name: 'Child Section', suite_id: 1, parent_id: 1, depth: 2 }, { id: 3, name: 'Grandchild Section', suite_id: 1, parent_id: 2, depth: 3 } ]; mockTestRailService.getProject.mockResolvedValue(mockProject); mockTestRailService.getSuites.mockResolvedValue(mockSuites); mockTestRailService.getSections.mockResolvedValue(mockSections); mockTestRailService.getCases.mockResolvedValue([]); const result = await projectSuiteManager.analyzeProjectStructure({ projectId: 1, includeRecommendations: true }); expect(result.content[0].text).toContain('"success": true'); // The recommendations should detect deep nesting expect(result.content[0].text).toContain('recommendations'); }); }); });

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/samuelvinay91/testrail-mcp'

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