Skip to main content
Glama
fetch-build-log-ambiguity.test.ts5.43 kB
import { getRequiredTool } from '@/tools'; // Mock TeamCity API client to simulate ambiguous and fallback scenarios jest.mock('@/api-client', () => { const listBuilds = jest.fn(async (locator?: string) => { if (locator?.includes('number:77')) { // Ambiguous: two builds, no buildTypeId filter provided return { build: [{ id: 1001 }, { id: 1002 }] }; } if (locator?.includes('buildType:(id:BT_FALLBACK)') && locator.includes('number:56')) { // Force fallback by returning empty for the direct locator return { build: [] }; } if (locator === 'buildType:(id:BT_FALLBACK),branch:default:any,count:100') { // Fallback recent list contains the matching number return { build: [{ id: 2002, number: '56' }] }; } return { build: [] }; }); const getBuildLogChunk = jest.fn( (buildId?: string, opts?: { startLine?: number; lineCount?: number }) => { return Promise.resolve({ lines: [`resolved ${buildId}`], startLine: opts?.startLine ?? 0, nextStartLine: undefined, totalLines: 1, }); } ); return { TeamCityAPI: { getInstance: () => ({ listBuilds, getBuildLogChunk, }), }, }; }); describe('fetch_build_log buildNumber resolution edge cases', () => { it('errors when multiple builds match and buildTypeId is missing', async () => { const res = await getRequiredTool('fetch_build_log').handler({ buildNumber: '77', page: 1, pageSize: 10, }); const payload = JSON.parse((res.content?.[0]?.text as string) ?? '{}'); // runTool wraps errors in a { success: false, error: { message } } shape expect(payload.success).toBe(false); const errObj = (payload as { error?: { message?: unknown } }).error; const message = typeof errObj?.message === 'string' ? errObj.message : String(errObj?.message ?? ''); expect(message).toContain('Multiple builds match number 77'); }); it('falls back to recent list when direct locator returns none', async () => { const res = await getRequiredTool('fetch_build_log').handler({ buildNumber: 56, buildTypeId: 'BT_FALLBACK', page: 1, pageSize: 10, }); const payload = JSON.parse((res.content?.[0]?.text as string) ?? '{}'); expect(payload.meta.buildId).toBe('2002'); expect(payload.meta.buildNumber).toBe('56'); expect(payload.lines[0]).toBe('resolved 2002'); }); }); describe('fetch_build_log buildNumber resolution - no build found scenarios', () => { beforeEach(() => { jest.resetModules(); }); it('errors when no build found without buildTypeId', async () => { jest.doMock('@/api-client', () => ({ TeamCityAPI: { getInstance: () => ({ listBuilds: jest.fn().mockResolvedValue({ build: [] }), getBuildLogChunk: jest.fn(), }), }, })); jest.isolateModules(() => { // eslint-disable-next-line @typescript-eslint/no-var-requires const { getRequiredTool } = require('@/tools'); return getRequiredTool('fetch_build_log') .handler({ buildNumber: '999', page: 1, pageSize: 10, }) .then((res: { content?: Array<{ text?: string }> }) => { const payload = JSON.parse((res.content?.[0]?.text as string) ?? '{}'); expect(payload.success).toBe(false); const errObj = (payload as { error?: { message?: unknown } }).error; const message = typeof errObj?.message === 'string' ? errObj.message : String(errObj?.message ?? ''); expect(message).toContain('No build found with number 999'); }); }); }); it('errors when fallback search also returns no match', async () => { jest.doMock('@/api-client', () => ({ TeamCityAPI: { getInstance: () => ({ listBuilds: jest.fn().mockImplementation(async (locator?: string) => { // Direct search returns empty if (locator?.includes('number:888')) { return { build: [] }; } // Fallback search also returns builds but none match if (locator?.includes('branch:default:any,count:100')) { return { build: [ { id: 5001, number: '100' }, { id: 5002, number: '200' }, ], }; } return { build: [] }; }), getBuildLogChunk: jest.fn(), }), }, })); jest.isolateModules(() => { // eslint-disable-next-line @typescript-eslint/no-var-requires const { getRequiredTool } = require('@/tools'); return getRequiredTool('fetch_build_log') .handler({ buildNumber: '888', buildTypeId: 'BT_NO_MATCH', page: 1, pageSize: 10, }) .then((res: { content?: Array<{ text?: string }> }) => { const payload = JSON.parse((res.content?.[0]?.text as string) ?? '{}'); expect(payload.success).toBe(false); const errObj = (payload as { error?: { message?: unknown } }).error; const message = typeof errObj?.message === 'string' ? errObj.message : String(errObj?.message ?? ''); expect(message).toContain('No build found with number 888'); expect(message).toContain('BT_NO_MATCH'); }); }); }); });

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