Skip to main content
Glama
automation-state-tools.test.tsโ€ข17.3 kB
import { describe, it, beforeEach, afterEach } from 'node:test'; import assert from 'node:assert'; import { ZebrunnerReportingClient } from '../../dist/api/reporting-client.js'; import { ZebrunnerReportingError } from '../../dist/types/reporting.js'; import { EnhancedZebrunnerClient } from '../../dist/api/enhanced-client.js'; describe('Automation State Tools', () => { let mockReportingClient: any; let mockEnhancedClient: any; let originalConsoleWarn: typeof console.warn; beforeEach(() => { // Mock console.warn to capture fallback warnings originalConsoleWarn = console.warn; console.warn = () => {}; // Mock ZebrunnerReportingClient mockReportingClient = { getAutomationStates: async (projectId: number) => { if (projectId === 999) { // Simulate the actual behavior: return default mapping on error return [ { id: 10, name: 'Not Automated' }, { id: 11, name: 'To Be Automated' }, { id: 12, name: 'Automated' } ]; } return [ { id: 10, name: 'Not Automated' }, { id: 11, name: 'To Be Automated' }, { id: 12, name: 'Automated' } ]; }, makeAuthenticatedRequest: async (method: string, url: string) => { if (url.includes('projectId=999')) { throw new Error('Network error'); } return { data: [ { id: 10, name: 'Not Automated' }, { id: 11, name: 'To Be Automated' }, { id: 12, name: 'Automated' } ] }; } }; // Mock EnhancedZebrunnerClient mockEnhancedClient = { getTestCases: async (projectKey: string, options: any) => { const mockTestCases = [ { id: 1, key: 'TEST-1', title: 'Test Case 1', automationState: { id: 10, name: 'Not Automated' }, createdAt: '2024-01-15T10:00:00Z', priority: { id: 1, name: 'High' } }, { id: 2, key: 'TEST-2', title: 'Test Case 2', automationState: { id: 11, name: 'To Be Automated' }, createdAt: '2024-06-15T10:00:00Z', priority: { id: 2, name: 'Medium' } }, { id: 3, key: 'TEST-3', title: 'Test Case 3', automationState: { id: 12, name: 'Automated' }, createdAt: '2024-09-01T10:00:00Z', priority: { id: 3, name: 'Low' } } ]; // Filter based on automation state if provided let filteredCases = mockTestCases; if (options.automationState !== undefined && options.automationState !== null) { const states = Array.isArray(options.automationState) ? options.automationState : [options.automationState]; if (states.length > 0) { filteredCases = mockTestCases.filter(testCase => { return states.some((state: any) => { if (typeof state === 'number') { return testCase.automationState.id === state; } else { return testCase.automationState.name === state; } }); }); } } return { items: filteredCases, _meta: { total: filteredCases.length, totalPages: 1 } }; }, buildRQLFilter: (options: any) => { const filters: string[] = []; if (options.automationState) { if (Array.isArray(options.automationState)) { const allNumbers = options.automationState.every((state: any) => typeof state === 'number'); const allStrings = options.automationState.every((state: any) => typeof state === 'string'); if (allNumbers) { filters.push(`automationState.id IN [${options.automationState.join(', ')}]`); } else if (allStrings) { const quotedNames = options.automationState.map((state: any) => `'${String(state).replace(/'/g, "\\'")}'`); filters.push(`automationState.name IN [${quotedNames.join(', ')}]`); } } else { if (typeof options.automationState === 'number') { filters.push(`automationState.id = ${options.automationState}`); } else { filters.push(`automationState.name = '${String(options.automationState).replace(/'/g, "\\'")}'`); } } } if (options.createdAfter) { filters.push(`createdAt >= '${options.createdAfter}'`); } return filters.join(' AND '); } }; }); afterEach(() => { console.warn = originalConsoleWarn; }); describe('ZebrunnerReportingClient.getAutomationStates', () => { it('should fetch automation states from API successfully', async () => { const states = await mockReportingClient.getAutomationStates(7); assert.strictEqual(states.length, 3); assert.deepStrictEqual(states[0], { id: 10, name: 'Not Automated' }); assert.deepStrictEqual(states[1], { id: 11, name: 'To Be Automated' }); assert.deepStrictEqual(states[2], { id: 12, name: 'Automated' }); }); it('should return default mapping when API fails', async () => { const states = await mockReportingClient.getAutomationStates(999); assert.strictEqual(states.length, 3); assert.deepStrictEqual(states[0], { id: 10, name: 'Not Automated' }); assert.deepStrictEqual(states[1], { id: 11, name: 'To Be Automated' }); assert.deepStrictEqual(states[2], { id: 12, name: 'Automated' }); }); it('should handle network errors gracefully', async () => { // Mock a network error scenario const originalMethod = mockReportingClient.makeAuthenticatedRequest; mockReportingClient.makeAuthenticatedRequest = async () => { throw new Error('Network timeout'); }; const states = await mockReportingClient.getAutomationStates(7); assert.strictEqual(states.length, 3); assert.strictEqual(states[0].name, 'Not Automated'); // Restore original method mockReportingClient.makeAuthenticatedRequest = originalMethod; }); it('should handle malformed API response', async () => { // Mock malformed response const originalMethod = mockReportingClient.makeAuthenticatedRequest; mockReportingClient.makeAuthenticatedRequest = async () => { return { data: 'invalid response' }; }; const states = await mockReportingClient.getAutomationStates(7); assert.strictEqual(states.length, 3); assert.strictEqual(states[0].name, 'Not Automated'); // Restore original method mockReportingClient.makeAuthenticatedRequest = originalMethod; }); }); describe('RQL Filter Generation', () => { it('should generate correct RQL for single automation state by name', () => { const filter = mockEnhancedClient.buildRQLFilter({ automationState: 'Not Automated' }); assert.strictEqual(filter, "automationState.name = 'Not Automated'"); }); it('should generate correct RQL for single automation state by ID', () => { const filter = mockEnhancedClient.buildRQLFilter({ automationState: 10 }); assert.strictEqual(filter, 'automationState.id = 10'); }); it('should generate correct RQL for multiple automation states by names', () => { const filter = mockEnhancedClient.buildRQLFilter({ automationState: ['Not Automated', 'To Be Automated'] }); assert.strictEqual(filter, "automationState.name IN ['Not Automated', 'To Be Automated']"); }); it('should generate correct RQL for multiple automation states by IDs', () => { const filter = mockEnhancedClient.buildRQLFilter({ automationState: [10, 11] }); assert.strictEqual(filter, 'automationState.id IN [10, 11]'); }); it('should generate correct RQL for combined filters', () => { const filter = mockEnhancedClient.buildRQLFilter({ automationState: 'To Be Automated', createdAfter: '2024-01-01' }); assert.strictEqual(filter, "automationState.name = 'To Be Automated' AND createdAt >= '2024-01-01'"); }); it('should handle special characters in automation state names', () => { const filter = mockEnhancedClient.buildRQLFilter({ automationState: "State with 'quotes'" }); assert.strictEqual(filter, "automationState.name = 'State with \\'quotes\\''"); }); }); describe('Test Case Filtering by Automation State', () => { it('should filter test cases by single automation state name', async () => { const result = await mockEnhancedClient.getTestCases('MCP', { automationState: 'Not Automated' }); assert.strictEqual(result.items.length, 1); assert.strictEqual(result.items[0].key, 'TEST-1'); assert.strictEqual(result.items[0].automationState.name, 'Not Automated'); }); it('should filter test cases by single automation state ID', async () => { const result = await mockEnhancedClient.getTestCases('MCP', { automationState: 11 }); assert.strictEqual(result.items.length, 1); assert.strictEqual(result.items[0].key, 'TEST-2'); assert.strictEqual(result.items[0].automationState.id, 11); }); it('should filter test cases by multiple automation states', async () => { const result = await mockEnhancedClient.getTestCases('MCP', { automationState: ['Not Automated', 'To Be Automated'] }); assert.strictEqual(result.items.length, 2); assert.strictEqual(result.items[0].automationState.name, 'Not Automated'); assert.strictEqual(result.items[1].automationState.name, 'To Be Automated'); }); it('should return empty results for non-matching automation state', async () => { const result = await mockEnhancedClient.getTestCases('MCP', { automationState: 'Non-existent State' }); assert.strictEqual(result.items.length, 0); }); it('should handle mixed automation state types', async () => { const result = await mockEnhancedClient.getTestCases('MCP', { automationState: ['Not Automated', 12] // Mix of name and ID }); assert.strictEqual(result.items.length, 2); const stateNames = result.items.map(item => item.automationState.name); assert(stateNames.includes('Not Automated')); assert(stateNames.includes('Automated')); }); }); describe('Tool Parameter Validation', () => { it('should accept string automation state', () => { const testState = 'Not Automated'; assert.strictEqual(typeof testState, 'string'); assert(testState.length > 0); }); it('should accept number automation state', () => { const testState = 10; assert.strictEqual(typeof testState, 'number'); assert(testState > 0); }); it('should accept array of mixed automation states', () => { const testStates = ['Not Automated', 11, 'Automated']; assert(Array.isArray(testStates)); assert.strictEqual(testStates.length, 3); assert.strictEqual(typeof testStates[0], 'string'); assert.strictEqual(typeof testStates[1], 'number'); assert.strictEqual(typeof testStates[2], 'string'); }); }); describe('Error Handling', () => { it('should handle API errors gracefully in automation state discovery', async () => { const states = await mockReportingClient.getAutomationStates(999); assert.strictEqual(states.length, 3); // Should return default mapping assert.strictEqual(states[0].name, 'Not Automated'); assert.strictEqual(states[1].name, 'To Be Automated'); assert.strictEqual(states[2].name, 'Automated'); }); it('should handle invalid project IDs', async () => { const states = await mockReportingClient.getAutomationStates(-1); assert.strictEqual(states.length, 3); // Should return default mapping }); it('should provide meaningful default automation states', () => { const defaultStates = [ { id: 10, name: 'Not Automated' }, { id: 11, name: 'To Be Automated' }, { id: 12, name: 'Automated' } ]; assert.strictEqual(defaultStates.length, 3); assert(defaultStates.every(state => state.id > 0)); assert(defaultStates.every(state => state.name.length > 0)); assert(defaultStates.every(state => typeof state.id === 'number')); assert(defaultStates.every(state => typeof state.name === 'string')); }); }); describe('Integration Scenarios', () => { it('should work with project resolution', async () => { // Mock project resolution const mockResolveProjectId = async (project: any) => { if (project === 'MCP' || project === 'android') { return { projectId: 7 }; } throw new Error('Project not found'); }; const result = await mockResolveProjectId('MCP'); assert.strictEqual(result.projectId, 7); const states = await mockReportingClient.getAutomationStates(result.projectId); assert.strictEqual(states.length, 3); }); it('should support pagination with automation state filtering', async () => { const result = await mockEnhancedClient.getTestCases('MCP', { automationState: 'Not Automated', page: 0, size: 10 }); assert.strictEqual(result.items.length, 1); assert(result._meta); assert.strictEqual(typeof result._meta.total, 'number'); }); it('should combine automation state with other filters', async () => { const result = await mockEnhancedClient.getTestCases('MCP', { automationState: 'To Be Automated', suiteId: 123, createdAfter: '2024-01-01' }); // Should apply all filters assert(Array.isArray(result.items)); assert(result._meta); }); }); describe('Output Format Validation', () => { it('should generate valid JSON output for automation states', () => { const mockStates = [ { id: 10, name: 'Not Automated' }, { id: 11, name: 'To Be Automated' }, { id: 12, name: 'Automated' } ]; const result = { project: 'MCP', projectId: 7, automationStates: mockStates, mapping: mockStates.reduce((acc, state) => { acc[state.name] = state.id; return acc; }, {} as Record<string, number>) }; assert.strictEqual(result.project, 'MCP'); assert.strictEqual(result.projectId, 7); assert.strictEqual(result.automationStates.length, 3); assert.strictEqual(result.mapping['Not Automated'], 10); assert.strictEqual(result.mapping['To Be Automated'], 11); assert.strictEqual(result.mapping['Automated'], 12); }); it('should generate valid markdown output structure', () => { const mockStates = [ { id: 10, name: 'Not Automated' }, { id: 11, name: 'To Be Automated' }, { id: 12, name: 'Automated' } ]; let markdown = '# Automation States for Project MCP\n\n'; markdown += '**Project ID:** 7\n'; markdown += `**Total States:** ${mockStates.length}\n\n`; assert(markdown.includes('# Automation States')); assert(markdown.includes('Project ID')); assert(markdown.includes(`Total States:** ${mockStates.length}`)); }); }); describe('Performance and Edge Cases', () => { it('should handle empty automation state arrays', async () => { const result = await mockEnhancedClient.getTestCases('MCP', { automationState: [] }); // Empty array should return all test cases (no filtering) assert.strictEqual(result.items.length, 3); }); it('should handle null/undefined automation states', async () => { const result1 = await mockEnhancedClient.getTestCases('MCP', { automationState: null }); const result2 = await mockEnhancedClient.getTestCases('MCP', { automationState: undefined }); // Should return all test cases when no filtering is applied assert.strictEqual(result1.items.length, 3); assert.strictEqual(result2.items.length, 3); }); it('should handle large numbers of automation states', () => { const largeStateArray = Array.from({ length: 100 }, (_, i) => i + 1); const filter = mockEnhancedClient.buildRQLFilter({ automationState: largeStateArray }); assert(filter.includes('automationState.id IN')); assert(filter.includes('100')); }); it('should validate automation state data structure', () => { const validState = { id: 10, name: 'Not Automated' }; assert(typeof validState.id === 'number'); assert(typeof validState.name === 'string'); assert(validState.id > 0); assert(validState.name.length > 0); }); }); });

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/maksimsarychau/mcp-zebrunner'

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