Skip to main content
Glama

Google Cloud MCP Server

by krzko
tools.test.ts15.4 kB
/** * Tests for Billing service tools */ import { describe, it, expect, vi, beforeEach } from 'vitest'; // Import mocks first import '../../../mocks/google-cloud-mocks.js'; import { mockBillingClient, mockCatalogClient } from '../../../mocks/google-cloud-mocks.js'; import { createMockMcpServer, createMockBillingAccount, createMockCloudService, createMockSKU } from '../../../utils/test-helpers.js'; describe('Billing Tools', () => { let mockServer: ReturnType<typeof createMockMcpServer>; beforeEach(() => { vi.clearAllMocks(); mockServer = createMockMcpServer(); // Reset mock implementations mockBillingClient.listBillingAccounts.mockResolvedValue([[createMockBillingAccount()], null]); mockBillingClient.getBillingAccount.mockResolvedValue([createMockBillingAccount()]); mockBillingClient.listProjectBillingInfo.mockResolvedValue([[], null]); mockBillingClient.getProjectBillingInfo.mockResolvedValue([{ name: 'projects/test-project', billingEnabled: true, billingAccountName: 'billingAccounts/123456-789ABC-DEF012' }]); mockCatalogClient.listServices.mockResolvedValue([[createMockCloudService()], null]); mockCatalogClient.listSkus.mockResolvedValue([[createMockSKU()], null]); }); describe('registerBillingTools', () => { it('should register billing tools with MCP server', async () => { const { registerBillingTools } = await import('../../../../src/services/billing/tools.js'); registerBillingTools(mockServer as any); expect(mockServer.registerTool).toHaveBeenCalledWith( 'gcp-billing-list-accounts', expect.any(Object), expect.any(Function) ); expect(mockServer.registerTool).toHaveBeenCalledWith( 'gcp-billing-get-account-details', expect.any(Object), expect.any(Function) ); expect(mockServer.registerTool).toHaveBeenCalledWith( 'gcp-billing-list-projects', expect.any(Object), expect.any(Function) ); expect(mockServer.registerTool).toHaveBeenCalledWith( 'gcp-billing-get-project-info', expect.any(Object), expect.any(Function) ); expect(mockServer.registerTool).toHaveBeenCalledWith( 'gcp-billing-list-services', expect.any(Object), expect.any(Function) ); expect(mockServer.registerTool).toHaveBeenCalledWith( 'gcp-billing-list-skus', expect.any(Object), expect.any(Function) ); expect(mockServer.registerTool).toHaveBeenCalledWith( 'gcp-billing-analyse-costs', expect.any(Object), expect.any(Function) ); expect(mockServer.registerTool).toHaveBeenCalledWith( 'gcp-billing-detect-anomalies', expect.any(Object), expect.any(Function) ); expect(mockServer.registerTool).toHaveBeenCalledWith( 'gcp-billing-cost-recommendations', expect.any(Object), expect.any(Function) ); expect(mockServer.registerTool).toHaveBeenCalledWith( 'gcp-billing-service-breakdown', expect.any(Object), expect.any(Function) ); }); it('should handle list-billing-accounts tool execution', async () => { const { registerBillingTools } = await import('../../../../src/services/billing/tools.js'); registerBillingTools(mockServer as any); // Get the registered tool handler const toolCall = mockServer.registerTool.mock.calls.find( call => call[0] === 'gcp-billing-list-accounts' ); expect(toolCall).toBeDefined(); const toolHandler = toolCall![2]; const result = await toolHandler({ pageSize: 10 }); expect(result).toBeDefined(); expect(result.content).toBeDefined(); expect(result.content[0].text).toContain('Billing Accounts'); expect(mockBillingClient.listBillingAccounts).toHaveBeenCalledWith({ pageSize: 10, }); }); it('should handle get-billing-account-details tool execution', async () => { const { registerBillingTools } = await import('../../../../src/services/billing/tools.js'); registerBillingTools(mockServer as any); const toolCall = mockServer.registerTool.mock.calls.find( call => call[0] === 'gcp-billing-get-account-details' ); expect(toolCall).toBeDefined(); const toolHandler = toolCall![2]; const result = await toolHandler({ billingAccountName: 'billingAccounts/123456-789ABC-DEF012' }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Test Billing Account'); expect(mockBillingClient.getBillingAccount).toHaveBeenCalledWith({ name: 'billingAccounts/123456-789ABC-DEF012', }); }); it('should handle list-projects tool execution', async () => { const { registerBillingTools } = await import('../../../../src/services/billing/tools.js'); registerBillingTools(mockServer as any); const toolCall = mockServer.registerTool.mock.calls.find( call => call[0] === 'gcp-billing-list-projects' ); expect(toolCall).toBeDefined(); const toolHandler = toolCall![2]; const result = await toolHandler({ billingAccountName: 'billingAccounts/123456-789ABC-DEF012', pageSize: 50 }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('No projects found'); // Empty mock response expect(mockBillingClient.listProjectBillingInfo).toHaveBeenCalledWith({ name: 'billingAccounts/123456-789ABC-DEF012', pageSize: 50, }); }); it('should handle get-project-billing-info tool execution', async () => { const { registerBillingTools } = await import('../../../../src/services/billing/tools.js'); registerBillingTools(mockServer as any); const toolCall = mockServer.registerTool.mock.calls.find( call => call[0] === 'gcp-billing-get-project-info' ); expect(toolCall).toBeDefined(); const toolHandler = toolCall![2]; const result = await toolHandler({ projectId: 'test-project' }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Project Billing Information'); expect(result.content[0].text).toContain('test-project'); expect(mockBillingClient.getProjectBillingInfo).toHaveBeenCalledWith({ name: 'projects/test-project', }); }); it('should handle list-services tool execution', async () => { const { registerBillingTools } = await import('../../../../src/services/billing/tools.js'); registerBillingTools(mockServer as any); const toolCall = mockServer.registerTool.mock.calls.find( call => call[0] === 'gcp-billing-list-services' ); expect(toolCall).toBeDefined(); const toolHandler = toolCall![2]; const result = await toolHandler({ pageSize: 50 }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Google Cloud Services'); expect(result.content[0].text).toContain('Compute Engine API'); expect(mockCatalogClient.listServices).toHaveBeenCalledWith({ pageSize: 50, }); }); it('should handle list-skus tool execution', async () => { const { registerBillingTools } = await import('../../../../src/services/billing/tools.js'); registerBillingTools(mockServer as any); const toolCall = mockServer.registerTool.mock.calls.find( call => call[0] === 'gcp-billing-list-skus' ); expect(toolCall).toBeDefined(); const toolHandler = toolCall![2]; const result = await toolHandler({ serviceId: 'services/compute', pageSize: 20, currencyCode: 'USD' }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('SKUs for Service'); expect(result.content[0].text).toContain('Test Compute Engine SKU'); expect(mockCatalogClient.listSkus).toHaveBeenCalledWith({ parent: 'services/compute', pageSize: 20, currencyCode: 'USD', }); }); it('should handle analyse-costs tool execution', async () => { const { registerBillingTools } = await import('../../../../src/services/billing/tools.js'); registerBillingTools(mockServer as any); const toolCall = mockServer.registerTool.mock.calls.find( call => call[0] === 'gcp-billing-analyse-costs' ); expect(toolCall).toBeDefined(); const toolHandler = toolCall![2]; const result = await toolHandler({ billingAccountName: 'billingAccounts/123456-789ABC-DEF012', startDate: '2024-01-01', endDate: '2024-01-31', groupBy: 'service' }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Cost Analysis'); expect(result.content[0].text).toContain('demonstration with mock data'); }); it('should handle detect-anomalies tool execution', async () => { const { registerBillingTools } = await import('../../../../src/services/billing/tools.js'); registerBillingTools(mockServer as any); const toolCall = mockServer.registerTool.mock.calls.find( call => call[0] === 'gcp-billing-detect-anomalies' ); expect(toolCall).toBeDefined(); const toolHandler = toolCall![2]; const result = await toolHandler({ billingAccountName: 'billingAccounts/123456-789ABC-DEF012', lookbackDays: 30, thresholdPercentage: 50 }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Cost Anomaly Detection'); expect(result.content[0].text).toContain('demonstration with mock data'); }); it('should handle cost-recommendations tool execution', async () => { const { registerBillingTools } = await import('../../../../src/services/billing/tools.js'); registerBillingTools(mockServer as any); const toolCall = mockServer.registerTool.mock.calls.find( call => call[0] === 'gcp-billing-cost-recommendations' ); expect(toolCall).toBeDefined(); const toolHandler = toolCall![2]; const result = await toolHandler({ billingAccountName: 'billingAccounts/123456-789ABC-DEF012', minSavingsAmount: 10, priority: 'all' }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Cost Optimisation Recommendations'); expect(result.content[0].text).toContain('demonstration with mock recommendations'); }); it('should handle service-breakdown tool execution', async () => { const { registerBillingTools } = await import('../../../../src/services/billing/tools.js'); registerBillingTools(mockServer as any); const toolCall = mockServer.registerTool.mock.calls.find( call => call[0] === 'gcp-billing-service-breakdown' ); expect(toolCall).toBeDefined(); const toolHandler = toolCall![2]; const result = await toolHandler({ billingAccountName: 'billingAccounts/123456-789ABC-DEF012', timeRange: '30d' }); expect(result).toBeDefined(); expect(result.content[0].text).toContain('Service Cost Breakdown'); expect(result.content[0].text).toContain('demonstration data'); }); it('should handle empty billing accounts list', async () => { const { registerBillingTools } = await import('../../../../src/services/billing/tools.js'); // Mock empty response mockBillingClient.listBillingAccounts.mockResolvedValue([[], null]); registerBillingTools(mockServer as any); const toolCall = mockServer.registerTool.mock.calls.find( call => call[0] === 'gcp-billing-list-accounts' ); const toolHandler = toolCall![2]; const result = await toolHandler({ pageSize: 10 }); expect(result.content[0].text).toContain('No billing accounts found'); }); it('should handle billing account not found', async () => { const { registerBillingTools } = await import('../../../../src/services/billing/tools.js'); // Mock not found response mockBillingClient.getBillingAccount.mockResolvedValue([null]); registerBillingTools(mockServer as any); const toolCall = mockServer.registerTool.mock.calls.find( call => call[0] === 'gcp-billing-get-account-details' ); const toolHandler = toolCall![2]; const result = await toolHandler({ billingAccountName: 'billingAccounts/non-existent' }); expect(result.content[0].text).toContain('Billing account not found'); }); it('should handle errors gracefully', async () => { const { registerBillingTools } = await import('../../../../src/services/billing/tools.js'); // Mock error mockBillingClient.listBillingAccounts.mockRejectedValue(new Error('API Error')); registerBillingTools(mockServer as any); const toolCall = mockServer.registerTool.mock.calls.find( call => call[0] === 'gcp-billing-list-accounts' ); const toolHandler = toolCall![2]; await expect(toolHandler({ pageSize: 10 })).rejects.toThrow('Failed to list billing accounts: API Error'); }); it('should handle pagination tokens correctly', async () => { const { registerBillingTools } = await import('../../../../src/services/billing/tools.js'); // Mock response with next page token mockBillingClient.listBillingAccounts.mockResolvedValue([ [createMockBillingAccount()], 'next-page-token' ]); registerBillingTools(mockServer as any); const toolCall = mockServer.registerTool.mock.calls.find( call => call[0] === 'gcp-billing-list-accounts' ); const toolHandler = toolCall![2]; const result = await toolHandler({ pageSize: 10, pageToken: 'previous-token' }); expect(result.content[0].text).toContain('**Next Page Token:** next-page-token'); expect(mockBillingClient.listBillingAccounts).toHaveBeenCalledWith({ pageSize: 10, pageToken: 'previous-token', }); }); it('should handle filters in list-accounts', async () => { const { registerBillingTools } = await import('../../../../src/services/billing/tools.js'); registerBillingTools(mockServer as any); const toolCall = mockServer.registerTool.mock.calls.find( call => call[0] === 'gcp-billing-list-accounts' ); const toolHandler = toolCall![2]; await toolHandler({ pageSize: 10, filter: 'open=true' }); expect(mockBillingClient.listBillingAccounts).toHaveBeenCalledWith({ pageSize: 10, filter: 'open=true', }); }); }); });

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/krzko/google-cloud-mcp'

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