Skip to main content
Glama

AutoDev Codebase MCP Server

by anrgct
core-library.test.ts13 kB
/** * Integration tests for core library functionality * Tests that the abstracted core works with different platform adapters */ import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest' import { promises as fs } from 'fs' import path from 'path' import os from 'os' import { createSimpleNodeDependencies } from '../adapters/nodejs' import { CacheManager } from '../code-index/cache-manager' import { CodeIndexStateManager } from '../code-index/state-manager' import { CodeIndexConfigManager } from '../code-index/config-manager' import { DirectoryScanner } from '../code-index/processors/scanner' import { EmbedderProvider } from '../code-index/interfaces/manager' describe('Core Library Integration', () => { let tempDir: string let workspacePath: string let dependencies: ReturnType<typeof createSimpleNodeDependencies> beforeAll(async () => { // Create temporary directory for tests tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'core-lib-test-')) workspacePath = path.join(tempDir, 'test-workspace') await fs.mkdir(workspacePath, { recursive: true }) dependencies = createSimpleNodeDependencies(workspacePath) }) afterAll(async () => { // Clean up temporary directory await fs.rmdir(tempDir, { recursive: true }) }) describe('CacheManager Integration', () => { let cacheManager: CacheManager beforeEach(() => { cacheManager = new CacheManager( dependencies.fileSystem, dependencies.storage, workspacePath ) }) it('should initialize cache manager', async () => { await cacheManager.initialize() // After initialization, cache manager should be ready to use expect(cacheManager.getAllHashes()).toBeDefined() expect(typeof cacheManager.getAllHashes()).toBe('object') }) it('should manage file hashes', async () => { await cacheManager.initialize() const testFile = '/test/file.ts' const testHash = 'abc123' // Initially should not have hash expect(cacheManager.getHash(testFile)).toBeUndefined() // Update hash cacheManager.updateHash(testFile, testHash) expect(cacheManager.getHash(testFile)).toBe(testHash) // Should be in all hashes const allHashes = cacheManager.getAllHashes() expect(allHashes[testFile]).toBe(testHash) // Delete hash cacheManager.deleteHash(testFile) expect(cacheManager.getHash(testFile)).toBeUndefined() }) it('should handle cache clearing', async () => { await cacheManager.initialize() // Add some test hashes cacheManager.updateHash('/test1.ts', 'hash1') cacheManager.updateHash('/test2.ts', 'hash2') // Verify hashes are there expect(Object.keys(cacheManager.getAllHashes()).length).toBeGreaterThan(0) // Clear cache await cacheManager.clearCacheFile() // After clearing, we need to reinitialize to see the cleared state const newCacheManager = new CacheManager( dependencies.fileSystem, dependencies.storage, workspacePath ) await newCacheManager.initialize() expect(Object.keys(newCacheManager.getAllHashes()).length).toBe(0) }) }) describe('StateManager Integration', () => { let stateManager: CodeIndexStateManager beforeEach(() => { stateManager = new CodeIndexStateManager(dependencies.eventBus) }) it('should track indexing progress', async () => { return new Promise<void>((resolve) => { const unsubscribe = stateManager.onProgressUpdate((progress) => { expect(progress.processedItems).toBe(5) expect(progress.systemStatus).toBe('Indexing') unsubscribe() resolve() }) stateManager.reportBlockIndexingProgress(5, 10) }) }) it('should manage indexing state', () => { expect(stateManager.state).toBe('Standby') stateManager.setSystemState('Indexing', 'Starting indexing') expect(stateManager.state).toBe('Indexing') stateManager.setSystemState('Indexed', 'Indexing completed') expect(stateManager.state).toBe('Indexed') }) }) describe('ConfigManager Integration', () => { let configManager: CodeIndexConfigManager beforeEach(async () => { // Reset config to default state before each test await dependencies.configProvider.resetConfig() configManager = new CodeIndexConfigManager(dependencies.configProvider) }) it('should initialize with default configuration', async () => { await configManager.initialize() const config = configManager.getConfig() expect(config).toBeDefined() expect(config.embedderProvider).toBe("openai") }) it('should detect configuration changes', async () => { await configManager.initialize() const initialConfig = configManager.getConfig() // Simulate configuration change await dependencies.configProvider.saveConfig({ isEnabled: true, embedderProvider: "ollama" }) await configManager.initialize() // Reload config const newConfig = configManager.getConfig() expect(newConfig.isEnabled).toBe(true) expect(newConfig.embedderProvider).toBe("ollama") expect(newConfig.embedderProvider).not.toBe(initialConfig.embedderProvider) }) }) describe('DirectoryScanner Integration', () => { let scanner: DirectoryScanner beforeEach(async () => { // Create test files in workspace const srcDir = path.join(workspacePath, 'src') await dependencies.fileSystem.mkdir(srcDir) const testFiles = [ { path: 'src/index.ts', content: 'export * from "./utils"' }, { path: 'src/utils.ts', content: 'export function hello() { return "world" }' }, { path: 'src/component.tsx', content: 'export const Component = () => <div>Hello</div>' }, { path: 'README.md', content: '# Test Project' } ] for (const file of testFiles) { const filePath = path.join(workspacePath, file.path) const dir = path.dirname(filePath) if (!(await dependencies.fileSystem.exists(dir))) { await dependencies.fileSystem.mkdir(dir) } await dependencies.fileSystem.writeFile( filePath, new TextEncoder().encode(file.content) ) } // Initialize scanner with dependencies scanner = new DirectoryScanner({ fileSystem: dependencies.fileSystem, workspace: dependencies.workspace, pathUtils: dependencies.pathUtils, logger: dependencies.logger, embedder: null as any, // Mock embedder for testing qdrantClient: null as any, // Mock qdrant client for testing cacheManager: new CacheManager( dependencies.fileSystem, dependencies.storage, workspacePath ), eventBus: dependencies.eventBus }) }) it('should scan directory and find code blocks', async () => { const result = await scanner.scanDirectory(workspacePath) expect(result.codeBlocks).toBeDefined() expect(result.stats).toBeDefined() expect(result.stats.processed).toBeGreaterThanOrEqual(0) expect(result.stats.skipped).toBeGreaterThanOrEqual(0) expect(result.totalBlockCount).toBeGreaterThanOrEqual(0) }) it('should process files and create code blocks', async () => { const result = await scanner.scanDirectory(workspacePath) // Should have processing stats (processed + skipped should be at least as many as files we created) expect(result.stats.processed + result.stats.skipped).toBeGreaterThanOrEqual(0) // Should have returned a valid result structure expect(result.codeBlocks).toBeDefined() expect(Array.isArray(result.codeBlocks)).toBe(true) expect(result.stats).toBeDefined() expect(typeof result.stats.processed).toBe('number') expect(typeof result.stats.skipped).toBe('number') expect(typeof result.totalBlockCount).toBe('number') }) }) describe('Cross-Platform Compatibility', () => { it('should work with different path separators', async () => { const testPath = dependencies.pathUtils.join('src', 'components', 'Button.tsx') const normalized = dependencies.pathUtils.normalize(testPath) expect(normalized).toBe(path.normalize(testPath)) const isAbsolute = dependencies.pathUtils.isAbsolute(normalized) expect(isAbsolute).toBe(false) const absolutePath = dependencies.pathUtils.resolve(workspacePath, testPath) expect(dependencies.pathUtils.isAbsolute(absolutePath)).toBe(true) }) it('should handle workspace operations consistently', async () => { const relativePath = 'src/test.ts' const fullPath = path.join(workspacePath, relativePath) // Create file await dependencies.fileSystem.writeFile( fullPath, new TextEncoder().encode('// Test file') ) // Verify workspace operations expect(dependencies.workspace.getRelativePath(fullPath)).toBe(relativePath) expect(dependencies.workspace.getRootPath()).toBe(workspacePath) const found = await dependencies.workspace.findFiles('*.ts') expect(found).toContain(fullPath) }) it('should maintain event consistency across components', async () => { let eventCount = 0 const expectedEvents = 3 return new Promise<void>((resolve) => { const unsubscribe = dependencies.eventBus.on('cross-platform-test', (data) => { eventCount++ expect(data.source).toBeDefined() if (eventCount === expectedEvents) { unsubscribe() resolve() } }) // Emit events from different "components" dependencies.eventBus.emit('cross-platform-test', { source: 'cache-manager' }) dependencies.eventBus.emit('cross-platform-test', { source: 'scanner' }) dependencies.eventBus.emit('cross-platform-test', { source: 'config-manager' }) }) }) }) describe('Error Handling and Edge Cases', () => { it('should handle missing files gracefully', async () => { const configManager = new CodeIndexConfigManager(dependencies.configProvider) // This should not throw even if config file doesn't exist await expect(configManager.initialize()).resolves.not.toThrow() }) it('should handle invalid file operations', async () => { const invalidPath = path.join(workspacePath, 'non-existent', 'deeply', 'nested', 'file.txt') // Reading non-existent file should handle gracefully await expect(dependencies.fileSystem.readFile(invalidPath)).rejects.toThrow() // But exists should return false expect(await dependencies.fileSystem.exists(invalidPath)).toBe(false) }) it('should handle workspace operations on empty directories', async () => { const emptyDir = path.join(tempDir, 'empty-workspace') await dependencies.fileSystem.mkdir(emptyDir) const emptyDeps = createSimpleNodeDependencies(emptyDir) const files = await emptyDeps.workspace.findFiles('*') expect(files).toEqual([]) }) }) describe('Performance and Resource Management', () => { it('should handle large file operations efficiently', async () => { const largeContent = 'x'.repeat(1024 * 1024) // 1MB content const largePath = path.join(workspacePath, 'large-file.txt') const startTime = Date.now() await dependencies.fileSystem.writeFile( largePath, new TextEncoder().encode(largeContent) ) const readContent = await dependencies.fileSystem.readFile(largePath) const readText = new TextDecoder().decode(readContent) const endTime = Date.now() expect(readText).toBe(largeContent) expect(endTime - startTime).toBeLessThan(5000) // Should complete in under 5 seconds }) it('should clean up resources properly', async () => { const eventBus = dependencies.eventBus const initialListenerCount = (eventBus as any).listenerCount?.('test-cleanup') || 0 // Add some listeners const unsubscribe1 = eventBus.on('test-cleanup', () => {}) const unsubscribe2 = eventBus.on('test-cleanup', () => {}) const unsubscribe3 = eventBus.once('test-cleanup', () => {}) // Verify listeners were added const withListeners = (eventBus as any).listenerCount?.('test-cleanup') || 0 expect(withListeners).toBeGreaterThan(initialListenerCount) // Clean up unsubscribe1() unsubscribe2() unsubscribe3() // Verify cleanup (for EventBus implementations that support listenerCount) if ((eventBus as any).listenerCount) { const afterCleanup = (eventBus as any).listenerCount('test-cleanup') expect(afterCleanup).toBeLessThanOrEqual(withListeners) } }) }) })

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/anrgct/autodev-codebase'

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