Skip to main content
Glama
agent-vcs-admin.test.ts12.9 kB
// Full mode for write tools jest.mock('@/config', () => ({ getTeamCityUrl: () => 'https://example.test', getTeamCityToken: () => 'token', getMCPMode: () => 'full', })); describe('tools: agent admin & VCS', () => { afterEach(() => { jest.resetModules(); jest.clearAllMocks(); }); it('authorize_agent sets authorized state via authorizedInfo and returns JSON', async () => { await new Promise<void>((resolve, reject) => { jest.isolateModules(() => { (async () => { const setAuthorizedInfo = jest.fn(async () => ({})); jest.doMock('@/api-client', () => ({ TeamCityAPI: { getInstance: () => ({ agents: { setAuthorizedInfo } }) }, })); // eslint-disable-next-line @typescript-eslint/no-var-requires const { getRequiredTool } = require('@/tools'); const res = await getRequiredTool('authorize_agent').handler({ agentId: 'A1', authorize: true, }); const payload = JSON.parse((res.content?.[0]?.text as string) ?? '{}'); expect(payload).toMatchObject({ success: true, action: 'authorize_agent', agentId: 'A1', authorized: true, }); resolve(); })().catch(reject); }); }); }); it('assign_agent_to_pool sets pool and returns JSON', async () => { await new Promise<void>((resolve, reject) => { jest.isolateModules(() => { (async () => { const setAgentPool = jest.fn(async () => ({})); jest.doMock('@/api-client', () => ({ TeamCityAPI: { getInstance: () => ({ agents: { setAgentPool } }) }, })); // eslint-disable-next-line @typescript-eslint/no-var-requires const { getRequiredTool } = require('@/tools'); const res = await getRequiredTool('assign_agent_to_pool').handler({ agentId: 'A1', poolId: '2', }); const payload = JSON.parse((res.content?.[0]?.text as string) ?? '{}'); expect(payload).toMatchObject({ success: true, action: 'assign_agent_to_pool', agentId: 'A1', poolId: '2', }); resolve(); })().catch(reject); }); }); }); it('add_vcs_root_to_build attaches root and returns JSON', async () => { await new Promise<void>((resolve, reject) => { jest.isolateModules(() => { (async () => { const addVcsRootToBuildType = jest.fn(async () => ({})); jest.doMock('@/api-client', () => ({ TeamCityAPI: { getInstance: () => ({ buildTypes: { addVcsRootToBuildType } }) }, })); // eslint-disable-next-line @typescript-eslint/no-var-requires const { getRequiredTool } = require('@/tools'); const res = await getRequiredTool('add_vcs_root_to_build').handler({ buildTypeId: 'BT1', vcsRootId: 'VCS1', checkoutRules: '+:.', }); const payload = JSON.parse((res.content?.[0]?.text as string) ?? '{}'); expect(payload).toMatchObject({ success: true, action: 'add_vcs_root_to_build', buildTypeId: 'BT1', vcsRootId: 'VCS1', }); resolve(); })().catch(reject); }); }); }); it('create_vcs_root creates root and returns JSON', async () => { await new Promise<void>((resolve, reject) => { jest.isolateModules(() => { (async () => { const addVcsRoot = jest.fn(async () => ({ data: { id: 'VCS1' } })); jest.doMock('@/api-client', () => ({ TeamCityAPI: { getInstance: () => ({ vcsRoots: { addVcsRoot } }) }, })); // eslint-disable-next-line @typescript-eslint/no-var-requires const { getRequiredTool } = require('@/tools'); const res = await getRequiredTool('create_vcs_root').handler({ projectId: 'P1', name: 'Root', id: 'VCS1', vcsName: 'jetbrains.git', url: 'https://git', branch: 'main', }); const payload = JSON.parse((res.content?.[0]?.text as string) ?? '{}'); expect(payload).toMatchObject({ success: true, action: 'create_vcs_root', id: 'VCS1' }); resolve(); })().catch(reject); }); }); }); it('set_vcs_root_property sets property and returns JSON', async () => { await new Promise<void>((resolve, reject) => { jest.isolateModules(() => { (async () => { const setVcsRootProperty = jest.fn(async () => ({})); jest.doMock('@/api-client', () => ({ TeamCityAPI: { getInstance: () => ({ vcsRoots: { setVcsRootProperty } }) }, })); // eslint-disable-next-line @typescript-eslint/no-var-requires const { getRequiredTool } = require('@/tools'); const res = await getRequiredTool('set_vcs_root_property').handler({ id: 'VCS1', name: 'branch', value: 'refs/heads/main', }); const payload = JSON.parse((res.content?.[0]?.text as string) ?? '{}'); expect(payload).toMatchObject({ success: true, action: 'set_vcs_root_property', id: 'VCS1', name: 'branch', }); expect(setVcsRootProperty).toHaveBeenCalledWith( 'VCS1', 'branch', 'refs/heads/main', expect.any(Object) ); resolve(); })().catch(reject); }); }); }); it('delete_vcs_root_property deletes property and returns JSON', async () => { await new Promise<void>((resolve, reject) => { jest.isolateModules(() => { (async () => { const deleteVcsRootProperty = jest.fn(async () => ({})); jest.doMock('@/api-client', () => ({ TeamCityAPI: { getInstance: () => ({ vcsRoots: { deleteVcsRootProperty } }) }, })); // eslint-disable-next-line @typescript-eslint/no-var-requires const { getRequiredTool } = require('@/tools'); const res = await getRequiredTool('delete_vcs_root_property').handler({ id: 'VCS1', name: 'branchSpec', }); const payload = JSON.parse((res.content?.[0]?.text as string) ?? '{}'); expect(payload).toMatchObject({ success: true, action: 'delete_vcs_root_property', id: 'VCS1', name: 'branchSpec', }); expect(deleteVcsRootProperty).toHaveBeenCalledWith('VCS1', 'branchSpec'); resolve(); })().catch(reject); }); }); }); it('update_vcs_root_properties updates multiple properties and returns JSON', async () => { await new Promise<void>((resolve, reject) => { jest.isolateModules(() => { (async () => { const setVcsRootProperties = jest.fn(async () => ({})); jest.doMock('@/api-client', () => ({ TeamCityAPI: { getInstance: () => ({ vcsRoots: { setVcsRootProperties } }) }, })); // eslint-disable-next-line @typescript-eslint/no-var-requires const { getRequiredTool } = require('@/tools'); const res = await getRequiredTool('update_vcs_root_properties').handler({ id: 'VCS1', branch: 'refs/heads/main', branchSpec: ['+:refs/heads/*', '+:refs/pull/*/head'], }); const payload = JSON.parse((res.content?.[0]?.text as string) ?? '{}'); expect(payload).toMatchObject({ success: true, action: 'update_vcs_root_properties', id: 'VCS1', updated: 2, }); expect(setVcsRootProperties).toHaveBeenCalled(); const [idArg, _fieldsArg, bodyArg] = (setVcsRootProperties.mock.calls[0] ?? []) as unknown[]; expect(idArg).toBe('VCS1'); expect(bodyArg).toMatchObject({ property: [ { name: 'branch', value: 'refs/heads/main' }, { name: 'branchSpec', value: '+:refs/heads/*\n+:refs/pull/*/head' }, ], }); resolve(); })().catch(reject); }); }); }); it('bulk_set_agents_enabled skips agents with no id', async () => { await new Promise<void>((resolve, reject) => { jest.isolateModules(() => { (async () => { const setEnabledInfo = jest.fn(async () => ({})); const getAllAgents = jest.fn(async () => ({ data: { agent: [ { id: 'agent1', name: 'Agent 1' }, { name: 'Agent No ID' }, // No id - should be skipped { id: '', name: 'Empty ID' }, // Empty id - should be skipped { id: 'agent2', name: 'Agent 2' }, ], }, })); jest.doMock('@/api-client', () => ({ TeamCityAPI: { getInstance: () => ({ agents: { getAllAgents, setEnabledInfo } }), }, })); // eslint-disable-next-line @typescript-eslint/no-var-requires const { getRequiredTool } = require('@/tools'); const res = await getRequiredTool('bulk_set_agents_enabled').handler({ poolId: '1', enabled: false, comment: 'Maintenance', }); const payload = JSON.parse((res.content?.[0]?.text as string) ?? '{}'); expect(payload.success).toBe(true); expect(payload.total).toBe(2); // Only agent1 and agent2 counted expect(payload.succeeded).toBe(2); expect(setEnabledInfo).toHaveBeenCalledTimes(2); resolve(); })().catch(reject); }); }); }); it('bulk_set_agents_enabled catches errors and reports failed agents', async () => { await new Promise<void>((resolve, reject) => { jest.isolateModules(() => { (async () => { const setEnabledInfo = jest.fn(async (agentId: string) => { if (agentId === 'agent2') { throw new Error('Permission denied'); } return {}; }); const getAllAgents = jest.fn(async () => ({ data: { agent: [ { id: 'agent1', name: 'Agent 1' }, { id: 'agent2', name: 'Agent 2' }, { id: 'agent3', name: 'Agent 3' }, ], }, })); jest.doMock('@/api-client', () => ({ TeamCityAPI: { getInstance: () => ({ agents: { getAllAgents, setEnabledInfo } }), }, })); // eslint-disable-next-line @typescript-eslint/no-var-requires const { getRequiredTool } = require('@/tools'); const res = await getRequiredTool('bulk_set_agents_enabled').handler({ locator: 'enabled:true', enabled: false, }); const payload = JSON.parse((res.content?.[0]?.text as string) ?? '{}'); expect(payload.success).toBe(true); expect(payload.total).toBe(3); expect(payload.succeeded).toBe(2); expect(payload.failed).toBe(1); const failedResult = payload.results.find((r: { id: string; ok: boolean }) => !r.ok); expect(failedResult.id).toBe('agent2'); expect(failedResult.error).toBe('Permission denied'); resolve(); })().catch(reject); }); }); }); it('bulk_set_agents_enabled handles non-Error exceptions', async () => { await new Promise<void>((resolve, reject) => { jest.isolateModules(() => { (async () => { const setEnabledInfo = jest.fn(async () => { // eslint-disable-next-line no-throw-literal throw 'string error'; // Non-Error thrown }); const getAllAgents = jest.fn(async () => ({ data: { agent: [{ id: 'agent1', name: 'Agent 1' }] }, })); jest.doMock('@/api-client', () => ({ TeamCityAPI: { getInstance: () => ({ agents: { getAllAgents, setEnabledInfo } }), }, })); // eslint-disable-next-line @typescript-eslint/no-var-requires const { getRequiredTool } = require('@/tools'); const res = await getRequiredTool('bulk_set_agents_enabled').handler({ poolId: '1', enabled: true, }); const payload = JSON.parse((res.content?.[0]?.text as string) ?? '{}'); expect(payload.success).toBe(true); expect(payload.failed).toBe(1); expect(payload.results[0].error).toBe('Unknown error'); resolve(); })().catch(reject); }); }); }); });

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