Skip to main content
Glama

mcp-adr-analysis-server

by tosin2013
deployment-history-resource.test.ts13.3 kB
/** * Unit tests for deployment-history-resource.ts * Tests deployment history resource generation, query parameter handling, and caching */ import { URLSearchParams } from 'url'; import { describe, it, expect, beforeAll, beforeEach, afterEach, jest } from '@jest/globals'; // Mock ResourceCache const mockCacheGet = jest.fn(); const mockCacheSet = jest.fn(); jest.unstable_mockModule('../../src/resources/resource-cache.js', () => ({ ResourceCache: jest.fn().mockImplementation(() => ({ get: mockCacheGet, set: mockCacheSet, })), })); // Mock deployment-readiness-tool const mockDeploymentReadiness = jest.fn(); jest.unstable_mockModule('../../src/tools/deployment-readiness-tool.js', () => ({ deploymentReadiness: mockDeploymentReadiness, })); // Mock file system for fallback const mockReadFile = jest.fn(); jest.unstable_mockModule('fs', () => ({ promises: { readFile: mockReadFile, readdir: jest.fn().mockResolvedValue([]), }, })); describe('Deployment History Resource', () => { let generateDeploymentHistoryResource: any; beforeAll(async () => { const module = await import('../../src/resources/deployment-history-resource.js'); generateDeploymentHistoryResource = module.generateDeploymentHistoryResource; }); beforeEach(() => { jest.clearAllMocks(); // Default: no cache hit mockCacheGet.mockResolvedValue(null); // Default: successful tool execution mockDeploymentReadiness.mockResolvedValue({ content: [ { text: ` Deployment History Analysis =========================== Period: Last 30 days Environment: all Summary ------- Total: 10 deployments Successful: 9 Failed: 1 Success rate: 90.0% Metrics ------- MTBF: 7.2 days MTTR: 15 minutes `, }, ], }); // Default: package.json for fallback mockReadFile.mockResolvedValue(JSON.stringify({ version: '1.0.0' })); }); afterEach(() => { jest.restoreAllMocks(); }); describe('Basic Resource Generation', () => { it('should generate deployment history with default parameters', async () => { const result = await generateDeploymentHistoryResource(); expect(result).toBeDefined(); expect(result.data).toBeDefined(); expect(result.data.period).toBe('30d'); expect(result.data.environment).toBe('all'); expect(result.data.summary).toBeDefined(); expect(result.contentType).toBe('application/json'); }); it('should include required metadata', async () => { const result = await generateDeploymentHistoryResource(); expect(result.data.metadata).toBeDefined(); expect(result.data.metadata.period).toBe('30d'); expect(result.data.metadata.environment).toBe('all'); expect(result.data.metadata.dataSource).toBe('comprehensive-tool'); expect(result.data.metadata.confidence).toBe(0.9); expect(result.data.metadata.timestamp).toBeDefined(); }); it('should include cache metadata', async () => { const result = await generateDeploymentHistoryResource(); expect(result.cacheKey).toBeDefined(); expect(result.cacheKey).toContain('deployment-history'); expect(result.ttl).toBe(300); // 5 minutes expect(result.etag).toBeDefined(); expect(result.lastModified).toBeDefined(); }); }); describe('Query Parameter Support', () => { it('should handle period parameter', async () => { const searchParams = new URLSearchParams({ period: '90d' }); const result = await generateDeploymentHistoryResource(undefined, searchParams); expect(result.data.period).toBe('90d'); expect(mockDeploymentReadiness).toHaveBeenCalled(); }); it('should handle environment parameter', async () => { const searchParams = new URLSearchParams({ environment: 'production' }); const result = await generateDeploymentHistoryResource(undefined, searchParams); expect(result.data.environment).toBe('production'); const toolCall = mockDeploymentReadiness.mock.calls[0][0]; expect(toolCall.targetEnvironment).toBe('production'); }); it('should handle multiple query parameters', async () => { const searchParams = new URLSearchParams({ period: '7d', environment: 'staging', includeFailures: 'true', includeMetrics: 'true', format: 'summary', }); const result = await generateDeploymentHistoryResource(undefined, searchParams); expect(result.data.period).toBe('7d'); expect(result.data.environment).toBe('staging'); expect(result.cacheKey).toContain('7d'); expect(result.cacheKey).toContain('staging'); }); it('should use default values when parameters are missing', async () => { const searchParams = new URLSearchParams({}); const result = await generateDeploymentHistoryResource(undefined, searchParams); expect(result.data.period).toBe('30d'); expect(result.data.environment).toBe('all'); }); }); describe('Data Extraction from Tool Output', () => { it('should extract deployment counts', async () => { mockDeploymentReadiness.mockResolvedValue({ content: [ { text: 'Total: 15 deployments\nSuccessful: 12\nFailed: 3\nSuccess rate: 80.0%', }, ], }); const result = await generateDeploymentHistoryResource(); expect(result.data.summary.totalDeployments).toBe(15); expect(result.data.summary.successfulDeployments).toBe(12); expect(result.data.summary.failedDeployments).toBe(3); expect(result.data.summary.successRate).toBe(80.0); }); it('should extract failure metrics', async () => { mockDeploymentReadiness.mockResolvedValue({ content: [ { text: 'MTBF: 10 days\nMTTR: 30 minutes', }, ], }); const result = await generateDeploymentHistoryResource(); expect(result.data.failureAnalysis).toBeDefined(); expect(result.data.failureAnalysis?.mtbf).toBe('10 days'); expect(result.data.failureAnalysis?.mttr).toBe('30 minutes'); }); it('should calculate success rate if not provided', async () => { mockDeploymentReadiness.mockResolvedValue({ content: [ { text: 'Total: 10\nSuccessful: 8\nFailed: 2', }, ], }); const result = await generateDeploymentHistoryResource(); expect(result.data.summary.successRate).toBe(80); // (8/10) * 100 }); }); describe('Caching Behavior', () => { it('should return cached result when available', async () => { const cachedResult = { data: { period: '30d', environment: 'all', timestamp: new Date().toISOString(), summary: { totalDeployments: 5, successfulDeployments: 5, failedDeployments: 0, successRate: 100, averageDeploymentTime: '5m', deploymentsPerWeek: 1, }, metadata: { period: '30d', environment: 'all', dataSource: 'comprehensive-tool' as const, confidence: 0.9, timestamp: new Date().toISOString(), }, }, contentType: 'application/json', lastModified: new Date().toISOString(), cacheKey: 'deployment-history:30d:all:true:true:detailed', ttl: 300, etag: '"cached-123"', }; mockCacheGet.mockResolvedValue(cachedResult); const result = await generateDeploymentHistoryResource(); expect(result).toBe(cachedResult); expect(mockDeploymentReadiness).not.toHaveBeenCalled(); expect(mockCacheSet).not.toHaveBeenCalled(); }); it('should cache new results', async () => { mockCacheGet.mockResolvedValue(null); const result = await generateDeploymentHistoryResource(); expect(mockCacheSet).toHaveBeenCalledWith( expect.stringContaining('deployment-history'), result, 300 ); }); it('should use granular cache keys based on query parameters', async () => { const searchParams = new URLSearchParams({ period: '7d', environment: 'production', includeFailures: 'false', includeMetrics: 'false', format: 'summary', }); await generateDeploymentHistoryResource(undefined, searchParams); expect(mockCacheSet).toHaveBeenCalledWith( 'deployment-history:7d:production:false:false:summary', expect.any(Object), 300 ); }); }); describe('Graceful Fallback', () => { it('should fall back to basic analysis when tool fails', async () => { mockDeploymentReadiness.mockRejectedValue(new Error('Tool unavailable')); const result = await generateDeploymentHistoryResource(); expect(result).toBeDefined(); expect(result.data.metadata.dataSource).toBe('basic-analysis'); expect(result.data.metadata.confidence).toBe(0.5); expect(result.data.summary.totalDeployments).toBe(1); }); it('should include version from package.json in fallback', async () => { mockDeploymentReadiness.mockRejectedValue(new Error('Tool unavailable')); mockReadFile.mockResolvedValue(JSON.stringify({ version: '2.3.4' })); const result = await generateDeploymentHistoryResource(); expect(result.data.recentDeployments).toBeDefined(); expect(result.data.recentDeployments?.[0]?.version).toBe('2.3.4'); }); it('should handle package.json read errors in fallback', async () => { mockDeploymentReadiness.mockRejectedValue(new Error('Tool unavailable')); mockReadFile.mockRejectedValue(new Error('File not found')); const result = await generateDeploymentHistoryResource(); expect(result.data.recentDeployments?.[0]?.version).toBe('0.0.0'); }); }); describe('Bridge Integration', () => { it('should call deployment-readiness-tool with correct operation', async () => { await generateDeploymentHistoryResource(); expect(mockDeploymentReadiness).toHaveBeenCalledWith( expect.objectContaining({ operation: 'deployment_history', projectPath: process.cwd(), strictMode: false, enableMemoryIntegration: true, }) ); }); it('should pass environment to tool', async () => { const searchParams = new URLSearchParams({ environment: 'staging' }); await generateDeploymentHistoryResource(undefined, searchParams); expect(mockDeploymentReadiness).toHaveBeenCalledWith( expect.objectContaining({ targetEnvironment: 'staging', }) ); }); }); describe('Response Structure Validation', () => { it('should have required summary fields', async () => { const result = await generateDeploymentHistoryResource(); expect(result.data.summary).toHaveProperty('totalDeployments'); expect(result.data.summary).toHaveProperty('successfulDeployments'); expect(result.data.summary).toHaveProperty('failedDeployments'); expect(result.data.summary).toHaveProperty('successRate'); expect(result.data.summary).toHaveProperty('averageDeploymentTime'); expect(result.data.summary).toHaveProperty('deploymentsPerWeek'); }); it('should have ISO timestamp format', async () => { const result = await generateDeploymentHistoryResource(); const timestamp = result.data.timestamp; expect(timestamp).toMatch(/^\d{4}-\d{2}-\d{2}T/); // ISO 8601 format expect(new Date(timestamp)).toBeInstanceOf(Date); expect(new Date(timestamp).toString()).not.toBe('Invalid Date'); }); it('should have valid metadata structure', async () => { const result = await generateDeploymentHistoryResource(); expect(result.data.metadata).toMatchObject({ period: expect.any(String), environment: expect.any(String), dataSource: expect.stringMatching(/^(comprehensive-tool|basic-analysis)$/), confidence: expect.any(Number), timestamp: expect.any(String), }); }); }); describe('Edge Cases', () => { it('should handle zero deployments', async () => { mockDeploymentReadiness.mockResolvedValue({ content: [{ text: 'Total: 0\nSuccessful: 0\nFailed: 0' }], }); const result = await generateDeploymentHistoryResource(); expect(result.data.summary.totalDeployments).toBe(0); expect(result.data.summary.successRate).toBe(0); }); it('should handle missing tool output', async () => { mockDeploymentReadiness.mockResolvedValue({ content: [] }); const result = await generateDeploymentHistoryResource(); // Empty output still uses comprehensive tool, just with zero data expect(result.data.metadata.dataSource).toBe('comprehensive-tool'); expect(result.data.summary.totalDeployments).toBe(0); }); it('should handle malformed tool output', async () => { mockDeploymentReadiness.mockResolvedValue({ content: [{ text: 'Invalid data format' }], }); const result = await generateDeploymentHistoryResource(); expect(result.data.summary.totalDeployments).toBe(0); }); }); });

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/tosin2013/mcp-adr-analysis-server'

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