Skip to main content
Glama
codebase-scanner-enhanced.test.ts11.5 kB
import { CodebaseScanner } from '../../src/scanner/codebase-scanner.js'; import { Neo4jClient } from '../../src/graph/neo4j-client.js'; import { ProjectLanguageDetector } from '../../src/scanner/detection/language-detector.js'; import { ProjectBuildFileDetector } from '../../src/scanner/detection/build-file-detector.js'; import * as fs from 'fs'; // Mock dependencies jest.mock('../../src/graph/neo4j-client.js'); jest.mock('../../src/scanner/detection/language-detector.js'); jest.mock('../../src/scanner/detection/build-file-detector.js'); jest.mock('fs'); jest.mock('simple-git', () => ({ simpleGit: jest.fn(() => ({ clone: jest.fn(), listRemote: jest.fn() })) })); const MockNeo4jClient = Neo4jClient as jest.MockedClass<typeof Neo4jClient>; const MockLanguageDetector = ProjectLanguageDetector as jest.MockedClass<typeof ProjectLanguageDetector>; const MockBuildFileDetector = ProjectBuildFileDetector as jest.MockedClass<typeof ProjectBuildFileDetector>; const mockFs = fs as jest.Mocked<typeof fs>; describe('CodebaseScanner Enhanced Validation', () => { let scanner: CodebaseScanner; let mockClient: jest.Mocked<Neo4jClient>; let mockLanguageDetector: jest.Mocked<ProjectLanguageDetector>; let mockBuildFileDetector: jest.Mocked<ProjectBuildFileDetector>; beforeEach(() => { mockClient = { connect: jest.fn(), disconnect: jest.fn(), runQuery: jest.fn(), initializeDatabase: jest.fn(), healthCheck: jest.fn() } as any; mockLanguageDetector = { detectFromBuildFiles: jest.fn(), detectFromFileExtensions: jest.fn(), detectPrimaryLanguage: jest.fn(), detectLanguages: jest.fn(), validateLanguages: jest.fn(), getRecommendedScanConfig: jest.fn() } as any; mockBuildFileDetector = { detect: jest.fn(), canDetect: jest.fn(), extractMetadata: jest.fn() } as any; MockNeo4jClient.mockImplementation(() => mockClient); MockLanguageDetector.mockImplementation(() => mockLanguageDetector); MockBuildFileDetector.mockImplementation(() => mockBuildFileDetector); scanner = new CodebaseScanner(mockClient); jest.clearAllMocks(); }); describe('validateProjectStructure', () => { it('should return error for non-existent path', async () => { mockFs.existsSync.mockReturnValue(false); const result = await scanner.validateProjectStructure('/nonexistent/path'); expect(result.isValid).toBe(false); expect(result.suggestions).toContain('Project path does not exist: /nonexistent/path'); expect(result.detectedLanguages).toEqual([]); }); it('should use build file detection when successful', async () => { mockFs.existsSync.mockReturnValue(true); mockBuildFileDetector.detect.mockResolvedValue({ isValid: true, suggestions: ['✅ TypeScript project detected'], detectedLanguages: ['typescript'], primaryLanguage: 'typescript', projectMetadata: [{ name: 'test-project', language: 'typescript', buildFilePath: '/test/package.json' }], subProjects: [], isMonoRepo: false }); mockLanguageDetector.validateLanguages.mockReturnValue({ supported: ['typescript'], unsupported: [], warnings: [] }); const result = await scanner.validateProjectStructure('/test/project'); expect(result.isValid).toBe(true); expect(result.detectedLanguages).toEqual(['typescript']); expect(result.suggestions).toContain('✅ TypeScript project detected'); expect(mockBuildFileDetector.detect).toHaveBeenCalledWith('/test/project'); }); it('should fallback to file extension detection when no build files', async () => { mockFs.existsSync.mockReturnValue(true); mockBuildFileDetector.detect.mockResolvedValue({ isValid: false, suggestions: [], detectedLanguages: [], projectMetadata: [], subProjects: [], isMonoRepo: false }); mockLanguageDetector.detectFromFileExtensions.mockResolvedValue(['java']); mockLanguageDetector.validateLanguages.mockReturnValue({ supported: ['java'], unsupported: [], warnings: [] }); const result = await scanner.validateProjectStructure('/test/project'); expect(result.isValid).toBe(true); expect(result.detectedLanguages).toEqual(['java']); expect(result.suggestions).toContain('💡 No build files detected - using file extension detection'); }); it('should handle completely empty projects', async () => { mockFs.existsSync.mockReturnValue(true); mockBuildFileDetector.detect.mockResolvedValue({ isValid: false, suggestions: [], detectedLanguages: [], projectMetadata: [], subProjects: [], isMonoRepo: false }); mockLanguageDetector.detectFromFileExtensions.mockResolvedValue([]); mockLanguageDetector.validateLanguages.mockReturnValue({ supported: [], unsupported: [], warnings: [] }); const result = await scanner.validateProjectStructure('/test/project'); expect(result.isValid).toBe(false); expect(result.detectedLanguages).toEqual([]); expect(result.suggestions).toContain('⚠️ No source files found - check project path and file extensions'); }); it('should add src directory suggestion when missing', async () => { mockFs.existsSync.mockImplementation((path: any) => { if (path.includes('src')) return false; return true; }); mockBuildFileDetector.detect.mockResolvedValue({ isValid: true, suggestions: [], detectedLanguages: ['typescript'], projectMetadata: [], subProjects: [], isMonoRepo: false }); mockLanguageDetector.validateLanguages.mockReturnValue({ supported: ['typescript'], unsupported: [], warnings: [] }); const result = await scanner.validateProjectStructure('/test/project'); expect(result.suggestions).toContain('💡 Consider organizing code in a src/ directory for better analysis'); }); it('should include language validation warnings', async () => { mockFs.existsSync.mockReturnValue(true); mockBuildFileDetector.detect.mockResolvedValue({ isValid: true, suggestions: [], detectedLanguages: ['typescript', 'csharp'], projectMetadata: [], subProjects: [], isMonoRepo: false }); mockLanguageDetector.validateLanguages.mockReturnValue({ supported: ['typescript'], unsupported: ['csharp'], warnings: ['⚠️ csharp is detected but not yet fully supported for parsing'] }); const result = await scanner.validateProjectStructure('/test/project'); expect(result.suggestions).toContain('⚠️ csharp is detected but not yet fully supported for parsing'); }); it('should handle detection errors gracefully', async () => { mockFs.existsSync.mockReturnValue(true); mockBuildFileDetector.detect.mockRejectedValue(new Error('Detection failed')); const result = await scanner.validateProjectStructure('/test/project'); expect(result.isValid).toBe(false); expect(result.suggestions).toContain('❌ Failed to analyze project structure: Detection failed'); }); }); describe('getRecommendedScanConfig', () => { it('should provide comprehensive scan configuration', async () => { const mockDetection = { isValid: true, suggestions: ['✅ TypeScript project detected'], detectedLanguages: ['typescript'], primaryLanguage: 'typescript', projectMetadata: [{ name: 'my-app', version: '1.0.0', language: 'typescript', buildFilePath: '/test/package.json' }], subProjects: [], isMonoRepo: false }; const mockRecommendation = { languages: ['typescript'], primaryLanguage: 'typescript', buildSystems: ['npm'], frameworks: ['React'], suggestions: ['🚀 Framework: React'], includeTests: true, excludePaths: ['node_modules', 'dist', 'build'] }; // Mock the methods directly on the scanner instance jest.spyOn(scanner, 'validateProjectStructure').mockResolvedValue(mockDetection); mockLanguageDetector.getRecommendedScanConfig.mockResolvedValue(mockRecommendation); const result = await scanner.getRecommendedScanConfig('/test/project', 'test-project'); expect(result.scanConfig.projectId).toBe('test-project'); expect(result.scanConfig.projectName).toBe('my-app'); // From metadata expect(result.scanConfig.languages).toEqual(['typescript']); expect(result.scanConfig.excludePaths).toEqual(['node_modules', 'dist', 'build']); expect(result.scanConfig.includeTests).toBe(true); expect(result.projectMetadata).toEqual(mockDetection.projectMetadata); expect(result.suggestions).toEqual(expect.arrayContaining([ '✅ TypeScript project detected', '🚀 Framework: React' ])); }); it('should use project ID as name when no metadata available', async () => { const mockDetection = { isValid: true, suggestions: [], detectedLanguages: ['java'], projectMetadata: [], subProjects: [], isMonoRepo: false }; const mockRecommendation = { languages: ['java'], primaryLanguage: 'java', buildSystems: ['maven'], frameworks: [], suggestions: [], includeTests: true, excludePaths: ['target', 'build'] }; mockBuildFileDetector.detect.mockResolvedValue(mockDetection); mockLanguageDetector.getRecommendedScanConfig.mockResolvedValue(mockRecommendation); const result = await scanner.getRecommendedScanConfig('/test/project'); expect(result.scanConfig.projectId).toBe('project'); // From path expect(result.scanConfig.projectName).toBeUndefined(); // No metadata }); it('should prefer primary language metadata', async () => { const mockDetection = { isValid: true, suggestions: [], detectedLanguages: ['typescript', 'java'], primaryLanguage: 'typescript', projectMetadata: [ { name: 'backend', language: 'java', buildFilePath: '/test/pom.xml' }, { name: 'frontend', language: 'typescript', buildFilePath: '/test/package.json' } ], subProjects: [], isMonoRepo: true }; const mockRecommendation = { languages: ['typescript', 'java'], primaryLanguage: 'typescript', buildSystems: ['npm', 'maven'], frameworks: [], suggestions: [], includeTests: true, excludePaths: ['node_modules', 'target'] }; jest.spyOn(scanner, 'validateProjectStructure').mockResolvedValue(mockDetection); mockLanguageDetector.getRecommendedScanConfig.mockResolvedValue(mockRecommendation); const result = await scanner.getRecommendedScanConfig('/test/project'); expect(result.scanConfig.projectName).toBe('frontend'); // Primary language metadata }); }); });

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/JonnoC/CodeRAG'

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