Skip to main content
Glama
server-health-and-metrics.test.ts9.04 kB
// Force dev mode for read-only tools // Use required tool helper to avoid undefined checks jest.mock('@/config', () => ({ getTeamCityUrl: () => 'https://example.test', getTeamCityToken: () => 'token', // Metrics and health item tools are full-mode only getMCPMode: () => 'full', })); describe('tools: server health & metrics', () => { afterEach(() => { jest.resetModules(); jest.clearAllMocks(); }); it('get_server_info returns server info JSON', async () => { await new Promise<void>((resolve, reject) => { jest.isolateModules(() => { (async () => { const server = { getServerInfo: jest.fn(async () => ({ data: { version: '2024.1', buildNumber: '99999' }, })), }; jest.doMock('@/api-client', () => ({ TeamCityAPI: { getInstance: () => ({ server }) } })); // eslint-disable-next-line @typescript-eslint/no-var-requires const { getRequiredTool } = require('@/tools'); const res = await getRequiredTool('get_server_info').handler({}); const payload = JSON.parse((res.content?.[0]?.text as string) ?? '{}'); expect(payload).toMatchObject({ version: '2024.1', buildNumber: '99999' }); resolve(); })().catch(reject); }); }); }); it('get_server_metrics returns metrics JSON', async () => { await new Promise<void>((resolve, reject) => { jest.isolateModules(() => { (async () => { const server = { getAllMetrics: jest.fn(async () => ({ data: { metrics: [{ name: 'cpu.load', value: 0.5 }] }, })), }; jest.doMock('@/api-client', () => ({ TeamCityAPI: { getInstance: () => ({ server }) } })); // eslint-disable-next-line @typescript-eslint/no-var-requires const { getRequiredTool } = require('@/tools'); const res = await getRequiredTool('get_server_metrics').handler({}); const payload = JSON.parse((res.content?.[0]?.text as string) ?? '{}'); expect(payload).toMatchObject({ metrics: [{ name: 'cpu.load', value: 0.5 }] }); resolve(); })().catch(reject); }); }); }); it('list_server_health_items returns health items and get_server_health_item returns one item', async () => { await new Promise<void>((resolve, reject) => { jest.isolateModules(() => { (async () => { const health = { getHealthItems: jest.fn(async () => ({ data: { healthItem: [ { id: 'A', severity: 'INFO' }, { id: 'B', severity: 'ERROR' }, ], }, })), getSingleHealthItem: jest.fn(async () => ({ data: { id: 'B', severity: 'ERROR' } })), }; jest.doMock('@/api-client', () => ({ TeamCityAPI: { getInstance: () => ({ health }) } })); // eslint-disable-next-line @typescript-eslint/no-var-requires const { getRequiredTool } = require('@/tools'); let res = await getRequiredTool('list_server_health_items').handler({ locator: 'severity:ERROR', }); let payload = JSON.parse((res.content?.[0]?.text as string) ?? '{}'); expect(Array.isArray(payload.healthItem)).toBe(true); res = await getRequiredTool('get_server_health_item').handler({ locator: 'id:B' }); payload = JSON.parse((res.content?.[0]?.text as string) ?? '{}'); expect(payload).toMatchObject({ id: 'B', severity: 'ERROR' }); resolve(); })().catch(reject); }); }); }); it('list_server_health_items treats empty locator as no filter', async () => { await new Promise<void>((resolve, reject) => { jest.isolateModules(() => { (async () => { let receivedLocator: string | undefined; const health = { getHealthItems: jest.fn(async (locator?: string) => { receivedLocator = locator as string | undefined; return { data: { healthItem: [] } }; }), }; jest.doMock('@/api-client', () => ({ TeamCityAPI: { getInstance: () => ({ health }) } })); // eslint-disable-next-line @typescript-eslint/no-var-requires const { getRequiredTool } = require('@/tools'); await getRequiredTool('list_server_health_items').handler({ locator: '' }); expect(receivedLocator).toBeUndefined(); resolve(); })().catch(reject); }); }); }); it('list_server_health_items normalizes category:(ERROR) to category:ERROR', async () => { await new Promise<void>((resolve, reject) => { jest.isolateModules(() => { (async () => { let receivedLocator: string | undefined; const health = { getHealthItems: jest.fn(async (locator?: string) => { receivedLocator = locator as string | undefined; return { data: { healthItem: [] } }; }), }; jest.doMock('@/api-client', () => ({ TeamCityAPI: { getInstance: () => ({ health }) } })); // eslint-disable-next-line @typescript-eslint/no-var-requires const { getRequiredTool } = require('@/tools'); await getRequiredTool('list_server_health_items').handler({ locator: 'category:(ERROR),muted:false', }); expect(receivedLocator).toBe('category:ERROR,muted:false'); resolve(); })().catch(reject); }); }); }); it('list_server_health_items falls back to client-side filtering on HTTP 400', async () => { await new Promise<void>((resolve, reject) => { jest.isolateModules(() => { (async () => { const http400 = Object.assign(new Error('HTTP 400'), { statusCode: 400 }); const health = { getHealthItems: jest .fn() // First call with locator throws 400 .mockImplementationOnce(async () => { throw http400; }) // Fallback without locator returns full list .mockImplementationOnce(async () => ({ data: { healthItem: [ { id: 'A', severity: 'INFO', category: 'misc' }, { id: 'B', severity: 'ERROR', category: 'build' }, ], }, })), }; jest.doMock('@/api-client', () => ({ TeamCityAPI: { getInstance: () => ({ health }) } })); // eslint-disable-next-line @typescript-eslint/no-var-requires const { getRequiredTool } = require('@/tools'); const res = await getRequiredTool('list_server_health_items').handler({ locator: 'severity:ERROR', }); const payload = JSON.parse((res.content?.[0]?.text as string) ?? '{}'); expect(payload.count).toBe(1); expect(Array.isArray(payload.healthItem)).toBe(true); expect(payload.healthItem[0]).toMatchObject({ id: 'B', severity: 'ERROR' }); resolve(); })().catch(reject); }); }); }); it('check_availability_guard returns ok=false on ERRORs and warnings when flagged', async () => { await new Promise<void>((resolve, reject) => { jest.isolateModules(() => { (async () => { const health = { getHealthItems: jest.fn(async () => ({ data: { healthItem: [ { id: 'B', severity: 'ERROR' }, { id: 'C', severity: 'WARNING' }, ], }, })), }; jest.doMock('@/api-client', () => ({ TeamCityAPI: { getInstance: () => ({ health }) } })); // eslint-disable-next-line @typescript-eslint/no-var-requires const { getRequiredTool } = require('@/tools'); let res = await getRequiredTool('check_availability_guard').handler({}); let payload = JSON.parse((res.content?.[0]?.text as string) ?? '{}'); expect(payload.ok).toBe(false); // has ERROR // Re-mock to only warnings jest.resetModules(); jest.isolateModules(() => {}); const healthWarn = { getHealthItems: jest.fn(async () => ({ data: { healthItem: [{ id: 'W', severity: 'WARNING' }] }, })), }; jest.doMock('@/api-client', () => ({ TeamCityAPI: { getInstance: () => ({ health: healthWarn }) }, })); // eslint-disable-next-line @typescript-eslint/no-var-requires const { getRequiredTool: loadRequired } = require('@/tools'); res = await loadRequired('check_availability_guard').handler({ failOnWarning: true }); payload = JSON.parse((res.content?.[0]?.text as string) ?? '{}'); expect(payload.ok).toBe(false); // fail on warnings 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