Skip to main content
Glama
Derrbal
by Derrbal
getProjects.test.ts7.25 kB
import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import nock from 'nock'; import { getProjects } from '../src/services/testrailService'; import { config } from '../src/config'; describe('testrailService.getProjects', () => { const baseApi = `${config.TESTRAIL_URL}`; const apiPath = '/index.php?/api/v2'; beforeEach(() => { nock.disableNetConnect(); }); afterEach(() => { nock.cleanAll(); nock.enableNetConnect(); }); it('returns normalized projects list (direct array format)', async () => { const mockProjects = [ { id: 1, name: 'Project Alpha', announcement: 'Welcome to Alpha', show_announcement: true, is_completed: false, completed_on: null, suite_mode: 1, url: 'https://test.testrail.com/index.php?/projects/overview/1', created_on: 1640995200, created_by: 1, custom_field_example: 'test_value', }, { id: 2, name: 'Project Beta', is_completed: true, completed_on: 1672531199, suite_mode: 2, url: 'https://test.testrail.com/index.php?/projects/overview/2', created_on: 1641081600, created_by: 2, }, ]; const scope = nock(baseApi).get(`${apiPath}/get_projects`).reply(200, mockProjects); const result = await getProjects(); expect(result).toHaveLength(2); expect(result[0]).toEqual( expect.objectContaining({ id: 1, name: 'Project Alpha', announcement: 'Welcome to Alpha', show_announcement: true, is_completed: false, completed_on: null, suite_mode: 1, url: 'https://test.testrail.com/index.php?/projects/overview/1', created_on: 1640995200, created_by: 1, custom: { custom_field_example: 'test_value' }, }), ); expect(result[1]).toEqual( expect.objectContaining({ id: 2, name: 'Project Beta', is_completed: true, completed_on: 1672531199, suite_mode: 2, url: 'https://test.testrail.com/index.php?/projects/overview/2', created_on: 1641081600, created_by: 2, custom: undefined, // No custom fields }), ); scope.done(); }); it('returns normalized projects list (paginated format)', async () => { const mockProjectsResponse = { offset: 0, limit: 250, size: 2, _links: { next: null, prev: null, }, projects: [ { id: 3, name: 'Project Gamma', announcement: 'Test announcement', show_announcement: true, is_completed: false, completed_on: null, suite_mode: 3, url: 'https://test.testrail.com/index.php?/projects/overview/3', custom_priority: 'high', }, { id: 51, name: 'Project Delta', announcement: null, show_announcement: false, is_completed: false, completed_on: null, suite_mode: 3, url: 'https://test.testrail.com/index.php?/projects/overview/51', }, ], }; const scope = nock(baseApi).get(`${apiPath}/get_projects`).reply(200, mockProjectsResponse); const result = await getProjects(); expect(result).toHaveLength(2); expect(result[0]).toEqual( expect.objectContaining({ id: 3, name: 'Project Gamma', announcement: 'Test announcement', show_announcement: true, is_completed: false, completed_on: null, suite_mode: 3, url: 'https://test.testrail.com/index.php?/projects/overview/3', custom: { custom_priority: 'high' }, }), ); expect(result[1]).toEqual( expect.objectContaining({ id: 51, name: 'Project Delta', announcement: null, show_announcement: false, is_completed: false, completed_on: null, suite_mode: 3, url: 'https://test.testrail.com/index.php?/projects/overview/51', custom: undefined, }), ); scope.done(); }); it('handles projects with only custom fields (paginated format)', async () => { const mockProjectsResponse = { offset: 0, limit: 250, size: 1, _links: { next: null, prev: null }, projects: [ { id: 3, name: 'Project Custom', is_completed: false, suite_mode: 1, url: 'https://test.testrail.com/index.php?/projects/overview/3', custom_priority: 'high', custom_team: 'QA Team', custom_budget: 50000, }, ], }; const scope = nock(baseApi).get(`${apiPath}/get_projects`).reply(200, mockProjectsResponse); const result = await getProjects(); expect(result).toHaveLength(1); expect(result[0].custom).toEqual({ custom_priority: 'high', custom_team: 'QA Team', custom_budget: 50000, }); scope.done(); }); it('returns empty array when no projects exist (direct array)', async () => { const scope = nock(baseApi).get(`${apiPath}/get_projects`).reply(200, []); const result = await getProjects(); expect(result).toEqual([]); scope.done(); }); it('returns empty array when no projects exist (paginated format)', async () => { const mockResponse = { offset: 0, limit: 250, size: 0, _links: { next: null, prev: null }, projects: [], }; const scope = nock(baseApi).get(`${apiPath}/get_projects`).reply(200, mockResponse); const result = await getProjects(); expect(result).toEqual([]); scope.done(); }); it('throws on authentication error', async () => { const scope = nock(baseApi).get(`${apiPath}/get_projects`).reply(401, { error: 'unauthorized' }); await expect(getProjects()).rejects.toMatchObject({ type: 'auth' }); scope.done(); }); it('throws on server error', async () => { const scope = nock(baseApi).get(`${apiPath}/get_projects`).reply(500, { error: 'server error' }); await expect(getProjects()).rejects.toMatchObject({ type: 'server' }); scope.done(); }); it('throws on rate limiting', async () => { const scope = nock(baseApi).get(`${apiPath}/get_projects`).reply(429, { error: 'rate limited' }); await expect(getProjects()).rejects.toMatchObject({ type: 'rate_limited' }); scope.done(); }); it('throws on invalid paginated response (projects not an array)', async () => { const invalidResponse = { offset: 0, limit: 250, size: 1, _links: { next: null, prev: null }, projects: "invalid", // Should be an array }; const scope = nock(baseApi).get(`${apiPath}/get_projects`).reply(200, invalidResponse); await expect(getProjects()).rejects.toMatchObject({ type: 'unknown' }); scope.done(); }); it('throws on completely unexpected response format', async () => { const invalidResponse = { unexpected: 'format' }; const scope = nock(baseApi).get(`${apiPath}/get_projects`).reply(200, invalidResponse); await expect(getProjects()).rejects.toMatchObject({ type: 'unknown' }); 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