Skip to main content
Glama

mcp-adr-analysis-server

by tosin2013
smart-git-push-release-readiness.test.ts16.8 kB
/** * Test suite for smart git push tool with release readiness integration */ import { describe, it, expect, beforeEach, afterEach, jest } from '@jest/globals'; // Mock dependencies const mockExecSync = jest.fn(); jest.unstable_mockModule('child_process', () => ({ execSync: mockExecSync })); const mockReadFileSync = jest.fn(); const mockExistsSync = jest.fn(); const mockStatSync = jest.fn(); const mockWriteFileSync = jest.fn(); const mockMkdirSync = jest.fn(); jest.unstable_mockModule('fs', () => ({ readFileSync: mockReadFileSync, writeFileSync: mockWriteFileSync, existsSync: mockExistsSync, statSync: mockStatSync, mkdirSync: mockMkdirSync })); describe('Smart Git Push with Release Readiness', () => { let testDir: string; beforeEach(() => { testDir = '/tmp/test-smart-push'; jest.clearAllMocks(); // Setup default mocks mockExecSync.mockReturnValue(''); mockExistsSync.mockReturnValue(true); mockStatSync.mockReturnValue({ size: 1024 } as any); mockReadFileSync.mockReturnValue('test content'); }); afterEach(() => { jest.restoreAllMocks(); }); describe('Release Readiness Integration', () => { it('should include release readiness analysis in dry run', async () => { // Mock staged files mockExecSync.mockReturnValueOnce('A\tsrc/feature.ts\nM\tREADME.md'); // Mock file reading mockExistsSync.mockImplementation((path: any) => { if (String(path).includes('TODO.md')) return true; if (String(path).includes('feature.ts')) return true; if (String(path).includes('README.md')) return true; return false; }); mockReadFileSync.mockImplementation((path: any, _encoding?: any): any => { if (String(path).includes('TODO.md')) { return `# Project TODO ## Core Features - [x] Implement authentication - [x] Add database layer - [x] Create API endpoints - [ ] Add error handling (low priority) ## Testing - [x] Unit tests - [x] Integration tests - [x] E2E tests `; } if (String(path).includes('feature.ts')) { return 'export function newFeature() { return "Hello World"; }'; } if (String(path).includes('README.md')) { return '# Project\n\nThis is a test project.'; } return ''; }); mockStatSync.mockReturnValue({ size: 1024 } as any); const { smartGitPush } = await import('../src/tools/smart-git-push-tool.js'); const result = await smartGitPush({ projectPath: testDir, dryRun: true, checkReleaseReadiness: true, releaseType: 'minor' }); expect(result.content[0].text).toContain('Release Readiness Analysis'); expect(result.content[0].text).toContain('Core Features'); expect(result.content[0].text).toContain('Testing'); expect(result.content[0].text).toContain('NOT READY'); }); it('should block push when critical TODOs exist', async () => { // Mock staged files mockExecSync.mockReturnValueOnce('A\tsrc/feature.ts'); // Mock file reading with critical TODOs mockExistsSync.mockImplementation((path: any) => { if (String(path).includes('TODO.md')) return true; if (String(path).includes('feature.ts')) return true; return false; }); mockReadFileSync.mockImplementation((path: any, _encoding?: any): any => { if (String(path).includes('TODO.md')) { return `# Project TODO ## Critical Issues - [ ] Fix security vulnerability (critical) - [ ] Resolve database connection leak (blocker) - [ ] Update authentication system (high priority) ## Features - [x] Basic functionality - [ ] Enhanced features `; } if (String(path).includes('feature.ts')) { return 'export function newFeature() { return "Hello World"; }'; } return ''; }); mockStatSync.mockReturnValue({ size: 1024 } as any); const { smartGitPush } = await import('../src/tools/smart-git-push-tool.js'); const result = await smartGitPush({ projectPath: testDir, dryRun: true, checkReleaseReadiness: true, releaseType: 'minor' }); expect(result.content[0].text).toContain('Release Readiness Analysis'); expect(result.content[0].text).toContain('❌ NOT READY'); expect(result.content[0].text).toContain('Release Blockers'); expect(result.content[0].text).toContain('critical-todos'); }); it('should show success message when release ready', async () => { // Mock different git commands appropriately mockExecSync.mockReset(); mockExecSync.mockImplementation((command: unknown) => { const cmd = command as string; if (cmd.includes('git diff --cached --name-status')) { return 'A\tsrc/feature.ts'; // Staged files } if (cmd.includes('git status --porcelain')) { return ''; // Clean working directory } if (cmd.includes('git log --oneline')) { return 'abc123 feat: add new feature'; // Clean commit history } return 'git command executed'; // Default for other git commands }); // Mock file reading with completed TODOs mockExistsSync.mockImplementation((path: any) => { if (String(path).includes('TODO.md')) return true; if (String(path).includes('feature.ts')) return true; return false; }); mockReadFileSync.mockImplementation((path: any, _encoding?: any): any => { if (String(path).includes('TODO.md')) { return `# Project TODO ## Release 1.0 - [x] Core features implemented - [x] All tests passing - [x] Documentation updated - [x] Security review completed `; } if (String(path).includes('feature.ts')) { return 'export function newFeature() { return "Hello World"; }'; } return ''; }); mockStatSync.mockReturnValue({ size: 1024 } as any); const { smartGitPush } = await import('../src/tools/smart-git-push-tool.js'); const result = await smartGitPush({ projectPath: testDir, dryRun: false, checkReleaseReadiness: true, releaseType: 'minor', message: 'Release v1.0' }); expect(result.content[0].text).toContain('Smart Git Push - Success ✅'); expect(result.content[0].text).toContain('**Release Readiness**: ✅ Ready'); expect(result.content[0].text).toContain('Release Readiness Analysis'); expect(result.content[0].text).toContain('🎉 **Congratulations!**'); expect(result.content[0].text).toContain('release-ready state'); }); it('should handle no staged files with release readiness check', async () => { // Mock no staged files mockExecSync.mockReturnValueOnce(''); // Mock TODO.md exists mockExistsSync.mockReturnValue(true); mockReadFileSync.mockReturnValue(`# Project TODO ## Current Sprint - [x] Feature A - [x] Feature B - [ ] Feature C (in progress) ## Next Sprint - [ ] Feature D - [ ] Feature E `); const { smartGitPush } = await import('../src/tools/smart-git-push-tool.js'); const result = await smartGitPush({ projectPath: testDir, checkReleaseReadiness: true, releaseType: 'patch' }); expect(result.content[0].text).toContain('No staged files found'); expect(result.content[0].text).toContain('Release Readiness Analysis'); expect(result.content[0].text).toContain('Current Sprint'); expect(result.content[0].text).toContain('Next Sprint'); }); }); describe('Release Type Handling', () => { it('should handle different release types correctly', async () => { // Mock staged files mockExecSync.mockReturnValueOnce('A\tsrc/feature.ts'); // Mock partial completion mockExistsSync.mockReturnValue(true); mockReadFileSync.mockImplementation((path: any, _encoding?: any): any => { if (String(path).includes('TODO.md')) { return `# Project TODO ## Major Features - [x] Feature 1 - [x] Feature 2 - [ ] Feature 3 - [ ] Feature 4 ## Minor Features - [x] Enhancement 1 - [x] Enhancement 2 `; } return 'export function test() {}'; }); mockStatSync.mockReturnValue({ size: 1024 } as any); const { smartGitPush } = await import('../src/tools/smart-git-push-tool.js'); // Test patch release (should be more lenient) const patchResult = await smartGitPush({ projectPath: testDir, dryRun: true, checkReleaseReadiness: true, releaseType: 'patch' }); // Test major release (should be more strict) const majorResult = await smartGitPush({ projectPath: testDir, dryRun: true, checkReleaseReadiness: true, releaseType: 'major' }); expect(patchResult.content[0].text).toContain('Release Readiness Analysis'); expect(majorResult.content[0].text).toContain('Release Readiness Analysis'); }); }); describe('Interactive Mode with Release Readiness', () => { it('should include release readiness in interactive approval', async () => { // Mock staged files with issues mockExecSync.mockReturnValueOnce('A\tdebug_script.py'); // Mock problematic file and critical TODOs mockExistsSync.mockImplementation((path: any) => { if (String(path).includes('TODO.md')) return true; if (String(path).includes('debug_script.py')) return true; return false; }); mockReadFileSync.mockImplementation((path: any, _encoding?: any): any => { if (String(path).includes('TODO.md')) { return `# Project TODO ## Critical Issues - [ ] Fix security vulnerability (critical) - [ ] Memory leak in production (blocker) `; } if (String(path).includes('debug_script.py')) { return 'print("Debug information")'; } return ''; }); mockStatSync.mockReturnValue({ size: 1024 } as any); const { smartGitPush } = await import('../src/tools/smart-git-push-tool.js'); const result = await smartGitPush({ projectPath: testDir, dryRun: true, checkReleaseReadiness: true, releaseType: 'minor' }); expect(result.content[0].text).toContain('Release Readiness Analysis'); expect(result.content[0].text).toContain('NOT READY'); expect(result.content[0].text).toContain('security vulnerability'); }); }); describe('Error Handling', () => { it('should handle release readiness analysis errors gracefully', async () => { // Mock staged files mockExecSync.mockReturnValueOnce('A\tsrc/feature.ts'); // Mock TODO.md read error mockExistsSync.mockImplementation((path: any) => { if (String(path).includes('TODO.md')) return true; if (String(path).includes('feature.ts')) return true; return false; }); mockReadFileSync.mockImplementation((path: any, _encoding?: any): any => { if (String(path).includes('TODO.md')) { throw new Error('File read error'); } return 'export function test() {}'; }); mockStatSync.mockReturnValue({ size: 1024 } as any); const { smartGitPush } = await import('../src/tools/smart-git-push-tool.js'); const result = await smartGitPush({ projectPath: testDir, dryRun: true, checkReleaseReadiness: true, releaseType: 'minor' }); // Should still work but without release readiness analysis expect(result.content[0].text).toContain('Smart Git Push - Dry Run'); expect(result.content[0].text).toContain('**Files to Push**: 1'); }); it('should handle missing release readiness detector gracefully', async () => { // Mock staged files (git diff --cached --name-status) - reset default first mockExecSync.mockReset(); mockExecSync.mockReturnValue('A\tsrc/feature.ts'); // Use mockReturnValue instead of Once mockExistsSync.mockReturnValue(true); mockReadFileSync.mockReturnValue('export function test() {}'); mockStatSync.mockReturnValue({ size: 1024 } as any); const { smartGitPush } = await import('../src/tools/smart-git-push-tool.js'); const result = await smartGitPush({ projectPath: testDir, dryRun: true, checkReleaseReadiness: true, releaseType: 'minor' }); // Should still work but without release readiness analysis expect(result.content[0].text).toContain('Smart Git Push - Dry Run'); expect(result.content[0].text).toContain('**Files to Push**: 1'); }); }); describe('Performance with Release Readiness', () => { it('should not significantly slow down push analysis', async () => { // Mock staged files mockExecSync.mockReturnValueOnce('A\tsrc/feature.ts'); // Mock medium-sized TODO.md mockExistsSync.mockReturnValue(true); mockReadFileSync.mockImplementation((path: any, _encoding?: any): any => { if (String(path).includes('TODO.md')) { let content = '# Project TODO\n\n'; for (let i = 0; i < 10; i++) { content += `## Feature ${i}\n`; for (let j = 0; j < 10; j++) { content += `- [${j % 2 === 0 ? 'x' : ' '}] Task ${j}\n`; } } return content; } return 'export function test() {}'; }); mockStatSync.mockReturnValue({ size: 1024 } as any); const { smartGitPush } = await import('../src/tools/smart-git-push-tool.js'); const startTime = Date.now(); const result = await smartGitPush({ projectPath: testDir, dryRun: true, checkReleaseReadiness: true, releaseType: 'minor' }); const duration = Date.now() - startTime; expect(duration).toBeLessThan(2000); // Should complete within 2 seconds expect(result.content[0].text).toContain('Release Readiness Analysis'); }); }); describe('Output Format Validation', () => { it('should format release readiness output correctly', async () => { // Mock staged files mockExecSync.mockReturnValueOnce('A\tsrc/feature.ts'); // Mock TODO.md with specific completion rates mockExistsSync.mockReturnValue(true); mockReadFileSync.mockImplementation((path: any, _encoding?: any): any => { if (String(path).includes('TODO.md')) { return `# Project TODO ## Core Features - [x] Feature 1 - [x] Feature 2 - [x] Feature 3 - [ ] Feature 4 ## Testing - [x] Unit tests - [x] Integration tests `; } return 'export function test() {}'; }); mockStatSync.mockReturnValue({ size: 1024 } as any); const { smartGitPush } = await import('../src/tools/smart-git-push-tool.js'); const result = await smartGitPush({ projectPath: testDir, dryRun: true, checkReleaseReadiness: true, releaseType: 'minor' }); const text = result.content[0].text; // Check for proper markdown formatting expect(text).toContain('## Release Readiness Analysis'); expect(text).toContain('### Pre-Push Recommendations'); expect(text).toContain('**Score**:'); expect(text).toContain('Confidence:'); // Check for milestone status formatting expect(text).toContain('Core Features'); expect(text).toContain('Testing'); expect(text).toContain('75.0%'); // 3/4 for Core Features expect(text).toContain('100.0%'); // 2/2 for Testing }); it('should handle empty milestones gracefully', async () => { // Mock staged files mockExecSync.mockReturnValueOnce('A\tsrc/feature.ts'); // Mock empty TODO.md mockExistsSync.mockReturnValue(true); mockReadFileSync.mockImplementation((path: any, _encoding?: any): any => { if (String(path).includes('TODO.md')) { return '# Project TODO\n\nNo current tasks.'; } return 'export function test() {}'; }); mockStatSync.mockReturnValue({ size: 1024 } as any); const { smartGitPush } = await import('../src/tools/smart-git-push-tool.js'); const result = await smartGitPush({ projectPath: testDir, dryRun: true, checkReleaseReadiness: true, releaseType: 'minor' }); expect(result.content[0].text).toContain('Release Readiness Analysis'); expect(result.content[0].text).toContain('READY'); }); }); });

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