Skip to main content
Glama
Derrbal
by Derrbal
getSections.test.ts10.9 kB
import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import nock from 'nock'; import { getSections } from '../src/services/testrailService'; import { config } from '../src/config'; describe('testrailService.getSections', () => { const baseApi = `${config.TESTRAIL_URL}`; const apiPath = '/index.php?/api/v2'; beforeEach(() => { nock.disableNetConnect(); }); afterEach(() => { nock.cleanAll(); nock.enableNetConnect(); }); it('returns normalized sections list with pagination', async () => { const projectId = 1; const suiteId = 2; const mockResponse = { offset: 0, limit: 250, size: 23, _links: { next: null, prev: null, }, sections: [ { depth: 0, display_order: 1, id: 1, name: 'Prerequisites', parent_id: null, suite_id: 2, }, { depth: 0, display_order: 2, id: 2, name: 'Documentation & Help', parent_id: null, suite_id: 2, }, { depth: 1, display_order: 3, id: 3, name: 'Licensing & Terms', parent_id: 2, suite_id: 2, }, ], }; const scope = nock(baseApi) .get(`${apiPath}/get_sections/${projectId}&suite_id=${suiteId}`) .reply(200, mockResponse); const result = await getSections({ project_id: projectId, suite_id: suiteId }); expect(result).toEqual({ offset: 0, limit: 250, size: 23, _links: { next: null, prev: null, }, sections: [ { depth: 0, display_order: 1, id: 1, name: 'Prerequisites', parent_id: null, suite_id: 2, }, { depth: 0, display_order: 2, id: 2, name: 'Documentation & Help', parent_id: null, suite_id: 2, }, { depth: 1, display_order: 3, id: 3, name: 'Licensing & Terms', parent_id: 2, suite_id: 2, }, ], }); scope.done(); }); it('handles sections with custom fields', async () => { const projectId = 1; const suiteId = 2; const mockResponse = { offset: 0, limit: 250, size: 1, _links: { next: null, prev: null, }, sections: [ { depth: 0, display_order: 1, id: 1, name: 'Test Section', parent_id: null, suite_id: 2, custom_section_type: 'functional', custom_priority: 'high', }, ], }; const scope = nock(baseApi) .get(`${apiPath}/get_sections/${projectId}&suite_id=${suiteId}`) .reply(200, mockResponse); const result = await getSections({ project_id: projectId, suite_id: suiteId }); expect(result.sections[0]).toEqual({ depth: 0, display_order: 1, id: 1, name: 'Test Section', parent_id: null, suite_id: 2, custom: { custom_section_type: 'functional', custom_priority: 'high', }, }); scope.done(); }); it('handles sections without custom fields', async () => { const projectId = 1; const suiteId = 2; const mockResponse = { offset: 0, limit: 250, size: 1, _links: { next: null, prev: null, }, sections: [ { depth: 0, display_order: 1, id: 1, name: 'Basic Section', parent_id: null, suite_id: 2, }, ], }; const scope = nock(baseApi) .get(`${apiPath}/get_sections/${projectId}&suite_id=${suiteId}`) .reply(200, mockResponse); const result = await getSections({ project_id: projectId, suite_id: suiteId }); expect(result.sections[0]).toEqual({ depth: 0, display_order: 1, id: 1, name: 'Basic Section', parent_id: null, suite_id: 2, }); scope.done(); }); it('handles pagination parameters', async () => { const projectId = 1; const suiteId = 2; const limit = 10; const offset = 20; const mockResponse = { offset: 20, limit: 10, size: 30, _links: { next: 'next_page_url', prev: 'prev_page_url', }, sections: [ { depth: 0, display_order: 21, id: 21, name: 'Paged Section', parent_id: null, suite_id: 2, }, ], }; const scope = nock(baseApi) .get(`${apiPath}/get_sections/${projectId}&suite_id=${suiteId}&limit=${limit}&offset=${offset}`) .reply(200, mockResponse); const result = await getSections({ project_id: projectId, suite_id: suiteId, limit, offset }); expect(result).toEqual({ offset: 20, limit: 10, size: 30, _links: { next: 'next_page_url', prev: 'prev_page_url', }, sections: [ { depth: 0, display_order: 21, id: 21, name: 'Paged Section', parent_id: null, suite_id: 2, }, ], }); scope.done(); }); it('handles project without suite_id (single suite mode)', async () => { const projectId = 1; const mockResponse = { offset: 0, limit: 250, size: 5, _links: { next: null, prev: null, }, sections: [ { depth: 0, display_order: 1, id: 1, name: 'Single Suite Section', parent_id: null, suite_id: 1, }, ], }; const scope = nock(baseApi) .get(`${apiPath}/get_sections/${projectId}`) .reply(200, mockResponse); const result = await getSections({ project_id: projectId }); expect(result.sections[0]).toEqual({ depth: 0, display_order: 1, id: 1, name: 'Single Suite Section', parent_id: null, suite_id: 1, }); scope.done(); }); it('throws on 404 - project not found', async () => { const projectId = 9999; const scope = nock(baseApi) .get(`${apiPath}/get_sections/${projectId}`) .reply(404, { error: 'not found' }); await expect(getSections({ project_id: projectId })).rejects.toMatchObject({ type: 'not_found' }); scope.done(); }); it('throws on authentication error', async () => { const projectId = 1; const scope = nock(baseApi) .get(`${apiPath}/get_sections/${projectId}`) .reply(401, { error: 'unauthorized' }); await expect(getSections({ project_id: projectId })).rejects.toMatchObject({ type: 'auth' }); scope.done(); }); it('throws on permission error', async () => { const projectId = 1; const scope = nock(baseApi) .get(`${apiPath}/get_sections/${projectId}`) .reply(403, { error: 'forbidden' }); await expect(getSections({ project_id: projectId })).rejects.toMatchObject({ type: 'auth' }); scope.done(); }); it('throws on rate limit error', async () => { const projectId = 1; const scope = nock(baseApi) .get(`${apiPath}/get_sections/${projectId}`) .reply(429, { error: 'too many requests' }); await expect(getSections({ project_id: projectId })).rejects.toMatchObject({ type: 'rate_limited' }); scope.done(); }); it('throws on server error', async () => { const projectId = 1; const scope = nock(baseApi) .get(`${apiPath}/get_sections/${projectId}`) .reply(500, { error: 'internal server error' }); await expect(getSections({ project_id: projectId })).rejects.toMatchObject({ type: 'server' }); scope.done(); }); it('throws on network error', async () => { const projectId = 1; const scope = nock(baseApi) .get(`${apiPath}/get_sections/${projectId}`) .replyWithError('Network error'); await expect(getSections({ project_id: projectId })).rejects.toMatchObject({ type: 'network' }); scope.done(); }); it('throws on invalid paginated response (sections not an array)', async () => { const projectId = 1; const invalidResponse = { offset: 0, limit: 250, size: 1, _links: { next: null, prev: null, }, sections: 'not an array', // Invalid response }; const scope = nock(baseApi) .get(`${apiPath}/get_sections/${projectId}`) .reply(200, invalidResponse); await expect(getSections({ project_id: projectId })).rejects.toThrow( 'API returned response with non-array sections field' ); scope.done(); }); it('throws on completely unexpected response format', async () => { const projectId = 1; const unexpectedResponse = 'not an object'; const scope = nock(baseApi) .get(`${apiPath}/get_sections/${projectId}`) .reply(200, unexpectedResponse); await expect(getSections({ project_id: projectId })).rejects.toThrow( 'API returned unexpected response format' ); scope.done(); }); it('handles nested sections with parent relationships', async () => { const projectId = 1; const suiteId = 2; const mockResponse = { offset: 0, limit: 250, size: 3, _links: { next: null, prev: null, }, sections: [ { depth: 0, display_order: 1, id: 1, name: 'Parent Section', parent_id: null, suite_id: 2, }, { depth: 1, display_order: 2, id: 2, name: 'Child Section 1', parent_id: 1, suite_id: 2, }, { depth: 1, display_order: 3, id: 3, name: 'Child Section 2', parent_id: 1, suite_id: 2, }, ], }; const scope = nock(baseApi) .get(`${apiPath}/get_sections/${projectId}&suite_id=${suiteId}`) .reply(200, mockResponse); const result = await getSections({ project_id: projectId, suite_id: suiteId }); expect(result.sections).toHaveLength(3); expect(result.sections[0]).toEqual({ depth: 0, display_order: 1, id: 1, name: 'Parent Section', parent_id: null, suite_id: 2, }); expect(result.sections[1]).toEqual({ depth: 1, display_order: 2, id: 2, name: 'Child Section 1', parent_id: 1, suite_id: 2, }); expect(result.sections[2]).toEqual({ depth: 1, display_order: 3, id: 3, name: 'Child Section 2', parent_id: 1, suite_id: 2, }); scope.done(); }); });

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

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