Skip to main content
Glama
api-client.test.tsโ€ข11.2 kB
import { describe, it, beforeEach } from 'node:test'; import { strict as assert } from 'node:assert'; import { EnhancedZebrunnerClient } from '../../src/api/enhanced-client.js'; import { ZebrunnerConfig } from '../../src/types/api.js'; import { testConfig } from '../fixtures/api-responses.js'; import { requireCredentials } from '../helpers/credentials.js'; import 'dotenv/config'; /** * Integration tests for EnhancedZebrunnerClient * These tests make real API calls to Zebrunner * * Prerequisites: * - Valid .env file with Zebrunner credentials * - Access to MCP project * - Test case MCP-1 should exist */ describe('EnhancedZebrunnerClient Integration Tests', () => { let client: EnhancedZebrunnerClient; let config: ZebrunnerConfig; beforeEach(() => { // Require valid credentials for integration tests const credentials = requireCredentials('EnhancedZebrunnerClient Integration Tests'); config = { baseUrl: credentials.baseUrl, username: credentials.login, token: credentials.token, debug: process.env.DEBUG === 'true', timeout: 30000, retryAttempts: 2, retryDelay: 1000 }; client = new EnhancedZebrunnerClient(config); }); describe('Connection and Health', () => { it('should successfully test connection to Zebrunner API', async () => { const result = await client.testConnection(); assert.equal(result.success, true); assert.ok(result.message.includes('Connection successful')); assert.ok(result.details); assert.equal(result.details.baseUrl, config.baseUrl); }); it('should track endpoint health', () => { const health = client.getEndpointHealth(); assert.equal(typeof health, 'object'); }); }); describe('Test Suites API', () => { it('should list test suites for MCP project', async () => { const response = await client.getTestSuites('MCP'); assert.ok(response); assert.ok(response.items); assert.ok(Array.isArray(response.items)); assert.ok(response.items.length > 0); // Verify structure of first suite const firstSuite = response.items[0]; assert.ok(firstSuite.id); assert.equal(typeof firstSuite.id, 'number'); assert.ok(firstSuite.title || firstSuite.name); }); it('should get all test suites with pagination', async () => { const allSuites = await client.getAllTestSuites('MCP'); assert.ok(Array.isArray(allSuites)); assert.ok(allSuites.length > 0); // Should have more or equal items than single page const singlePage = await client.getTestSuites('MCP', { size: 5 }); assert.ok(allSuites.length >= singlePage.items.length); }); it('should handle pagination parameters correctly', async () => { const page0 = await client.getTestSuites('MCP', { page: 0, size: 2 }); const page1 = await client.getTestSuites('MCP', { page: 1, size: 2 }); assert.ok(page0.items.length <= 2); assert.ok(page1.items.length <= 2); // Check pagination metadata if available if (page0._meta) { // Some APIs might not return currentPage, so check if it exists if (page0._meta.currentPage !== undefined) { assert.equal(page0._meta.currentPage, 0); } if (page0._meta.pageSize !== undefined) { assert.equal(page0._meta.pageSize, 2); } } if (page1._meta) { // Some APIs might not return currentPage, so check if it exists if (page1._meta.currentPage !== undefined) { assert.equal(page1._meta.currentPage, 1); } if (page1._meta.pageSize !== undefined) { assert.equal(page1._meta.pageSize, 2); } } // Pages should be different (if there are enough items and pagination works) // Only check if we have full pages and different content is expected if (page0.items.length === 2 && page1.items.length > 0 && page0._meta?.totalElements !== undefined && page0._meta.totalElements > 2) { assert.notEqual(page0.items[0].id, page1.items[0].id, 'Pages should contain different items when pagination is working'); } }); it('should validate project key format', async () => { try { await client.getTestSuites('invalid-key'); assert.fail('Should have thrown an error'); } catch (error: any) { assert.ok(error.message.includes('Invalid project key format')); } }); it('should handle empty project key', async () => { try { await client.getTestSuites(''); assert.fail('Should have thrown an error'); } catch (error: any) { assert.ok(error.message.includes('Project key is required')); } }); }); describe('Test Cases API', () => { it('should get test case by key MCP-1', async () => { const testCase = await client.getTestCaseByKey('MCP', 'MCP-1'); assert.ok(testCase); assert.ok(testCase.id); assert.equal(testCase.key, 'MCP-1'); assert.ok(testCase.title); assert.ok(testCase.priority); assert.ok(testCase.automationState); // Verify custom fields are parsed if (testCase.customField) { assert.equal(typeof testCase.customField, 'object'); assert.ok(Object.keys(testCase.customField).length > 0); } }); it('should list test cases with pagination', async () => { const response = await client.getTestCases('MCP', { size: 10 }); assert.ok(response); assert.ok(response.items); assert.ok(Array.isArray(response.items)); assert.ok(response.items.length <= 10); if (response.items.length > 0) { const firstCase = response.items[0]; assert.ok(firstCase.id); assert.equal(typeof firstCase.id, 'number'); } }); it('should handle invalid test case key', async () => { try { await client.getTestCaseByKey('MCP', 'INVALID-KEY-999'); assert.fail('Should have thrown an error'); } catch (error: any) { assert.ok(error); } }); it('should validate required parameters', async () => { try { await client.getTestCaseByKey('', 'MCP-1'); assert.fail('Should have thrown an error'); } catch (error: any) { assert.ok(error.message.includes('Project key is required')); } try { await client.getTestCaseByKey('MCP', ''); assert.fail('Should have thrown an error'); } catch (error: any) { assert.ok(error.message.includes('Test case key is required')); } }); }); describe('Error Handling', () => { it('should handle 404 errors gracefully', async () => { try { await client.getTestSuite(999999); // Non-existent suite ID assert.fail('Should have thrown an error'); } catch (error: any) { assert.equal(error.name, 'ZebrunnerNotFoundError'); assert.equal(error.statusCode, 404); } }); it('should handle invalid authentication', async () => { const invalidClient = new EnhancedZebrunnerClient({ ...config, token: 'invalid-token' }); try { await invalidClient.getTestSuites('MCP'); assert.fail('Should have thrown an error'); } catch (error: any) { assert.equal(error.name, 'ZebrunnerAuthError'); assert.equal(error.statusCode, 401); } }); it('should validate pagination parameters', async () => { // Test with negative page - API might accept it or handle gracefully const result = await client.getTestSuites('MCP', { page: -1 }); // Just verify we get a valid response structure assert.ok(result); assert.ok(result.items); assert.ok(Array.isArray(result.items)); // Test with size 0 - API might accept it or handle gracefully const resultSize0 = await client.getTestSuites('MCP', { size: 0 }); // Just verify we get a valid response structure assert.ok(resultSize0); assert.ok(resultSize0.items); assert.ok(Array.isArray(resultSize0.items)); // Test with large size - API might accept it or handle gracefully const resultLargeSize = await client.getTestSuites('MCP', { size: 1000 }); // Just verify we get a valid response structure assert.ok(resultLargeSize); assert.ok(resultLargeSize.items); assert.ok(Array.isArray(resultLargeSize.items)); }); }); describe('Experimental Features', () => { it('should attempt to get test cases by suite (experimental)', async () => { // Get a suite ID first const suites = await client.getTestSuites('MCP', { size: 1 }); if (suites.items.length > 0) { const suiteId = suites.items[0].id; try { const testCases = await client.getTestCasesBySuite('MCP', suiteId); assert.ok(Array.isArray(testCases)); } catch (error: any) { // Expected to fail on some instances assert.ok([404, 400].includes(error.statusCode)); } } }); it('should validate suite ID parameter', async () => { try { await client.getTestCasesBySuite('MCP', 0); assert.fail('Should have thrown an error'); } catch (error: any) { assert.ok(error.message.includes('Valid suite ID is required')); } try { await client.getTestCasesBySuite('MCP', -1); assert.fail('Should have thrown an error'); } catch (error: any) { assert.ok(error.message.includes('Valid suite ID is required')); } }); }); describe('Performance and Reliability', () => { it('should handle multiple concurrent requests', async () => { const promises = [ client.getTestSuites('MCP', { size: 2 }), client.getTestCaseByKey('MCP', 'MCP-1'), // Remove searchTestCases as it might not be available in all environments ]; const results = await Promise.allSettled(promises); // Both should succeed (known working endpoints) assert.equal(results[0].status, 'fulfilled', `First request failed: ${results[0].status === 'rejected' ? results[0].reason : ''}`); assert.equal(results[1].status, 'fulfilled', `Second request failed: ${results[1].status === 'rejected' ? results[1].reason : ''}`); }); it('should retry failed requests', async () => { // This test verifies retry mechanism by checking debug logs // In a real scenario, we'd need to simulate network failures const response = await client.getTestSuites('MCP'); assert.ok(response); }); it('should respect timeout settings', async () => { const fastClient = new EnhancedZebrunnerClient({ ...config, timeout: 1 // Very short timeout }); try { await fastClient.getTestSuites('MCP'); // If it succeeds, the API is very fast assert.ok(true); } catch (error: any) { // Should timeout assert.ok(error.message.includes('timeout')); } }); }); });

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