Skip to main content
Glama
changes-problems-admin.test.ts7.29 kB
import { type ToolDefinition, getRequiredTool } from '@/tools'; type PaginatedMock = jest.Mock<Promise<{ data: Record<string, unknown> }>, [string?]>; const createPaginatedMock = (items: unknown[], key: string): PaginatedMock => jest.fn(async (locator?: string) => { const startMatch = locator?.match(/start:(\d+)/); const countMatch = locator?.match(/count:(\d+)/); const start = startMatch?.[1] ? parseInt(startMatch[1], 10) : 0; const count = countMatch?.[1] ? parseInt(countMatch[1], 10) : 100; const slice = items.slice(start, start + count); const nextHref = start + count < items.length ? '/next' : undefined; const data: Record<string, unknown> = { count: items.length }; data[key] = slice; if (nextHref) data['nextHref'] = nextHref; return { data }; }); const getAllChanges = createPaginatedMock([{ id: 'c1' }, { id: 'c2' }, { id: 'c3' }], 'change'); const getAllBuildProblems = createPaginatedMock([{ id: 'p1' }, { id: 'p2' }], 'problem'); const getAllBuildProblemOccurrences = createPaginatedMock( [{ id: 'o1' }, { id: 'o2' }, { id: 'o3' }], 'problemOccurrence' ); const getAllInvestigations = createPaginatedMock([{ id: 'i1' }, { id: 'i2' }], 'investigation'); const getAllMutedTests = createPaginatedMock([{ id: 'm1' }, { id: 'm2' }], 'mute'); const getVersionedSettingsStatus = jest.fn(async () => ({ data: { status: 'UP_TO_DATE', revision: '123' }, })); const getAllUsers = createPaginatedMock( [{ id: 'u1' }, { id: 'u2' }, { id: 'u3' }, { id: 'u4' }], 'user' ); const getRoles = jest.fn(async () => ({ data: { role: [{ id: 'SYSTEM_ADMINISTRATOR' }] } })); const muteMultipleTests = jest.fn(async () => ({ data: { result: 'ok' } })); jest.mock('@/api-client', () => ({ TeamCityAPI: { getInstance: () => ({ changes: { getAllChanges }, problems: { getAllBuildProblems }, problemOccurrences: { getAllBuildProblemOccurrences }, investigations: { getAllInvestigations }, mutes: { getAllMutedTests, muteMultipleTests }, versionedSettings: { getVersionedSettingsStatus }, users: { getAllUsers }, roles: { getRoles }, }), }, })); describe('changes/problems/investigation tools', () => { beforeEach(() => { getAllChanges.mockClear(); getAllBuildProblems.mockClear(); getAllBuildProblemOccurrences.mockClear(); getAllInvestigations.mockClear(); getAllMutedTests.mockClear(); getAllUsers.mockClear(); getVersionedSettingsStatus.mockClear(); getRoles.mockClear(); muteMultipleTests.mockClear(); }); it('lists changes with helper filters and pagination', async () => { const res = await getRequiredTool('list_changes').handler({ projectId: 'Proj', buildId: 'B1', pageSize: 2, all: true, }); expect(getAllChanges).toHaveBeenCalled(); const locator = getAllChanges.mock.calls[0]?.[0]; expect(locator).toContain('project:(id:Proj)'); expect(locator).toContain('build:(id:B1)'); const payload = JSON.parse((res.content?.[0]?.text as string) ?? '{}'); expect(payload.items).toHaveLength(3); expect(payload.pagination.mode).toBe('all'); }); it('lists build problems with pagination helpers', async () => { const res = await getRequiredTool('list_problems').handler({ projectId: 'Proj', all: false, pageSize: 1, }); const locator = getAllBuildProblems.mock.calls[0]?.[0]; expect(locator).toContain('project:(id:Proj)'); const payload = JSON.parse((res.content?.[0]?.text as string) ?? '{}'); expect(payload.items).toHaveLength(1); expect(payload.pagination.page).toBe(1); }); it('lists problem occurrences and applies helper filters', async () => { await getRequiredTool('list_problem_occurrences').handler({ buildId: 'Build123', problemId: 'Problem42', pageSize: 3, }); const locator = getAllBuildProblemOccurrences.mock.calls[0]?.[0]; expect(locator).toContain('build:(id:Build123)'); expect(locator).toContain('problem:(id:Problem42)'); }); it('lists investigations including responsible username helper', async () => { await getRequiredTool('list_investigations').handler({ assigneeUsername: 'alice', buildTypeId: 'bt1', pageSize: 1, }); const locator = getAllInvestigations.mock.calls[0]?.[0]; expect(locator).toContain('buildType:(id:bt1)'); expect(locator).toContain('responsible:(user:(username:alice))'); }); it('lists muted tests with helper filters', async () => { await getRequiredTool('list_muted_tests').handler({ projectId: 'Proj', testNameId: 'Test123', }); const locator = getAllMutedTests.mock.calls[0]?.[0]; expect(locator).toContain('project:(id:Proj)'); expect(locator).toContain('test:(id:Test123)'); }); it('fetches Versioned Settings status', async () => { const res = await getRequiredTool('get_versioned_settings_status').handler({ locator: 'project:(id:Proj)', }); expect(getVersionedSettingsStatus).toHaveBeenCalledWith('project:(id:Proj)', undefined); const payload = JSON.parse((res.content?.[0]?.text as string) ?? '{}'); expect(payload.status).toBe('UP_TO_DATE'); }); it('lists users with helper filters and pagination', async () => { await getRequiredTool('list_users').handler({ groupId: 'devs', all: true, pageSize: 2 }); const locator = getAllUsers.mock.calls[0]?.[0]; expect(locator).toContain('group:(id:devs)'); expect(getAllUsers).toHaveBeenCalledTimes(3); }); it('lists roles and returns items/count', async () => { const res = await getRequiredTool('list_roles').handler({}); expect(getRoles).toHaveBeenCalled(); const payload = JSON.parse((res.content?.[0]?.text as string) ?? '{}'); expect(payload.items).toHaveLength(1); expect(payload.count).toBe(1); }); it('mutes tests with correct payload and scope helpers', async () => { let tool: ToolDefinition | undefined; const originalMode = process.env['MCP_MODE']; process.env['MCP_MODE'] = 'full'; jest.isolateModules(() => { // eslint-disable-next-line @typescript-eslint/no-var-requires const { getRequiredTool: getTool } = require('@/tools') as typeof import('@/tools'); tool = getTool('mute_tests'); }); if (originalMode === undefined) { delete process.env['MCP_MODE']; } else { process.env['MCP_MODE'] = originalMode; } if (!tool) { throw new Error('mute_tests tool not available'); } await tool.handler({ testNameIds: ['t1', 't2'], projectId: 'Proj', comment: 'Mute for investigation', }); const call = muteMultipleTests.mock.calls[0]; if (!call) { throw new Error('muteMultipleTests not called'); } const args = call as unknown[]; const fields = args[0] as string | undefined; const payload = args[1] as Record<string, unknown> | undefined; expect(fields).toBeUndefined(); expect(payload).toBeDefined(); expect(payload).toMatchObject({ mute: [ { scope: { project: { id: 'Proj' } }, target: { tests: { test: [{ id: 't1' }, { id: 't2' }] } }, assignment: { text: 'Mute for investigation' }, }, ], }); }); });

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/Daghis/teamcity-mcp'

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