Skip to main content
Glama
enhancedLocalContext.test.ts17.4 kB
/** * @fileOverview: Comprehensive test suite for enhanced local context * @module: EnhancedLocalContextTests * @keyTests: * - Unit tests for core functions * - Golden tests for deterministic output * - Performance benchmarks * - Integration tests with project_hints * @context: Ensures reliability and deterministic behavior of enhanced local context */ import { localContext, LocalContextRequest, LocalContextResponse } from '../enhancedLocalContext'; import { runAstQueriesOnFiles } from '../astQueryEngine'; import { rankCandidatesWithScoring } from '../candidateRanking'; import { assembleMiniBundle } from '../miniBundleAssembler'; import { generateDeterministicAnswer } from '../answerDraftGenerator'; import { FileInfo } from '../../../core/compactor/fileDiscovery'; import * as path from 'path'; import * as fs from 'fs'; // ===== TEST FIXTURES ===== const FIXTURE_PROJECT_PATH = path.join(__dirname, 'fixtures', 'sample-project'); const SAMPLE_FILES: FileInfo[] = [ { absPath: path.join(FIXTURE_PROJECT_PATH, 'src', 'database', 'connection.ts'), relPath: 'src/database/connection.ts', size: 2048, ext: '.ts', language: 'typescript', }, { absPath: path.join(FIXTURE_PROJECT_PATH, 'src', 'api', 'routes.ts'), relPath: 'src/api/routes.ts', size: 1536, ext: '.ts', language: 'typescript', }, { absPath: path.join(FIXTURE_PROJECT_PATH, 'src', 'auth', 'middleware.ts'), relPath: 'src/auth/middleware.ts', size: 1024, ext: '.ts', language: 'typescript', }, ]; const SAMPLE_DATABASE_CONTENT = ` import Database from 'better-sqlite3'; import { logger } from '../utils/logger'; export function initializeDatabase(dbPath: string) { const db = new Database(dbPath); logger.info('Database initialized'); return db; } export function queryRecords(db: any, table: string) { return db.prepare(\`SELECT * FROM \${table}\`).all(); } export function insertRecord(db: any, table: string, data: any) { const stmt = db.prepare(\`INSERT INTO \${table} VALUES (?)\`); return stmt.run(data); } `; const SAMPLE_API_CONTENT = ` import express from 'express'; import { authenticateUser } from '../auth/middleware'; const router = express.Router(); router.get('/api/users', authenticateUser, (req, res) => { // Get users logic }); router.post('/api/users', authenticateUser, (req, res) => { // Create user logic }); export default router; `; // ===== MOCK SETUP ===== // Mock file system for test fixtures jest.mock('fs', () => ({ ...jest.requireActual('fs'), readFileSync: jest.fn(), })); // Mock FileDiscovery to avoid touching real filesystem jest.mock('../../../core/compactor/fileDiscovery', () => { const actual = jest.requireActual('../../../core/compactor/fileDiscovery'); return { ...actual, FileDiscovery: class MockFileDiscovery { basePath: string; options: any; constructor(basePath: string, options: any) { this.basePath = basePath; this.options = options; } async discoverFiles() { return SAMPLE_FILES; } }, }; }); const mockReadFileSync = fs.readFileSync as jest.MockedFunction<typeof fs.readFileSync>; beforeEach(() => { mockReadFileSync.mockImplementation((filePath: any) => { const pathStr = filePath.toString(); if (pathStr.includes('connection.ts')) { return SAMPLE_DATABASE_CONTENT; } else if (pathStr.includes('routes.ts')) { return SAMPLE_API_CONTENT; } else if (pathStr.includes('middleware.ts')) { return ` export function authenticateUser(req: any, res: any, next: any) { const token = req.headers.authorization; if (!token) { return res.status(401).json({ error: 'No token provided' }); } next(); } `; } return 'export default {};'; }); }); // ===== UNIT TESTS ===== describe('Enhanced Local Context - Unit Tests', () => { describe('AST Query Engine', () => { it('should find import queries correctly', async () => { const queries = [{ kind: 'import' as const, source: /better-sqlite3/ }]; const results = await runAstQueriesOnFiles(SAMPLE_FILES.slice(0, 1), queries, 10); expect(results).toHaveLength(1); expect(results[0].symbol).toContain('better-sqlite3'); expect(results[0].kind).toBe('import'); }); it('should find export queries correctly', async () => { const queries = [{ kind: 'export' as const, name: /initialize/ }]; const results = await runAstQueriesOnFiles(SAMPLE_FILES.slice(0, 1), queries, 10); expect(results).toHaveLength(1); expect(results[0].symbol).toContain('initializeDatabase'); expect(results[0].kind).toBe('export'); }); it('should find function calls correctly', async () => { const queries = [{ kind: 'call' as const, callee: /prepare/ }]; const results = await runAstQueriesOnFiles(SAMPLE_FILES.slice(0, 1), queries, 10); expect(results.length).toBeGreaterThan(0); expect(results.some(r => r.symbol.includes('prepare'))).toBe(true); }); }); describe('Candidate Ranking', () => { it('should rank candidates by relevance', async () => { const mockCandidates = [ { file: 'src/database/connection.ts', symbol: 'initializeDatabase', start: 100, end: 200, kind: 'function', score: 0, reasons: ['database initialization'], role: 'initialization', }, { file: 'src/utils/helper.ts', symbol: 'helperFunction', start: 50, end: 100, kind: 'function', score: 0, reasons: ['utility function'], role: 'helper', }, ]; const mockProjectContext = { files: SAMPLE_FILES, exports: [], imports: [], routes: [], env: [], systems: {}, }; const ranked = await rankCandidatesWithScoring( mockCandidates, mockProjectContext, 'database initialization', 'init-read-write' ); expect(ranked).toHaveLength(2); expect(ranked[0].symbol).toBe('initializeDatabase'); // Should be ranked higher expect(ranked[0].finalScore).toBeGreaterThan(ranked[1].finalScore); }); }); describe('Answer Draft Generation', () => { it('should generate deterministic answers for database queries', () => { const mockTargets = [ { file: 'src/database/connection.ts', symbol: 'initializeDatabase', start: 100, end: 200, role: 'DB init', confidence: 0.9, why: ['database initialization function'], }, ]; const mockIndices = { systems: { db: { engine: 'vector-chroma' }, }, env: ['DB_PATH'], }; const answer = generateDeterministicAnswer( 'init-read-write', 'understand', mockTargets, mockIndices ); expect(answer).toContain('vector-chroma'); expect(answer).toContain('initializeDatabase'); expect(answer).toContain('DB_PATH'); expect(answer.length).toBeGreaterThan(50); expect(answer.length).toBeLessThan(500); // Reasonable length }); it('should handle missing template gracefully', () => { const answer = generateDeterministicAnswer('unknown-plan', 'unknown-task', [], {}); expect(answer).toBeTruthy(); expect(answer).toContain('unknown-plan'); }); }); }); // ===== INTEGRATION TESTS ===== describe('Enhanced Local Context - Integration Tests', () => { // Mock the project detection and file discovery jest.mock('../../../tools/utils/pathUtils', () => ({ detectWorkspaceDirectory: () => FIXTURE_PROJECT_PATH, })); it('should handle database queries end-to-end', async () => { const request: LocalContextRequest = { projectPath: FIXTURE_PROJECT_PATH, query: 'How does database connection and storage work?', taskType: 'understand', maxTokens: 2000, maxSimilarChunks: 10, useProjectHintsCache: false, // Disable for testing }; const result = await localContext(request); expect(result.success).toBe(true); expect(result.answerDraft).toBeTruthy(); expect(result.jumpTargets.length).toBeGreaterThan(0); expect(result.metadata.filesScanned).toBeGreaterThan(0); // Check that database-related symbols were found const hasDbSymbols = result.jumpTargets.some( target => target.symbol.toLowerCase().includes('database') || target.symbol.toLowerCase().includes('init') ); expect(hasDbSymbols).toBe(true); }); it('should handle API route queries', async () => { const request: LocalContextRequest = { projectPath: FIXTURE_PROJECT_PATH, query: 'Show me the API endpoints and routing', taskType: 'understand', attackPlan: 'api-route', maxTokens: 2000, maxSimilarChunks: 10, useProjectHintsCache: false, }; const result = await localContext(request); expect(result.success).toBe(true); expect(result.answerDraft).toContain('API'); // Should find route-related symbols const hasRouteSymbols = result.jumpTargets.some( target => target.file.includes('routes') || target.symbol.toLowerCase().includes('router') ); expect(hasRouteSymbols).toBe(true); }); it('should respect token budgets', async () => { const request: LocalContextRequest = { projectPath: FIXTURE_PROJECT_PATH, query: 'Analyze all code patterns', taskType: 'understand', maxTokens: 500, // Very small budget maxSimilarChunks: 20, useProjectHintsCache: false, }; const result = await localContext(request); expect(result.success).toBe(true); expect(result.metadata.bundleTokens).toBeLessThanOrEqual(500); }); it('demotes scripts and projection_matrix noise in path scoring', async () => { const { rankCandidatesWithScoring } = await import('../candidateRanking'); const noisy = { file: 'scripts/generate_projection_matrix.js', symbol: 'generate_projection_matrix', start: 0, end: 10, kind: 'function', score: 0, reasons: [], role: 'helper', } as any; const legit = { file: 'src/middleware/auth.ts', symbol: 'AuthMiddleware', start: 0, end: 10, kind: 'export', score: 0, reasons: ['middleware'], role: 'middleware', } as any; const ranked = await rankCandidatesWithScoring( [noisy, legit], { files: SAMPLE_FILES, exports: [], imports: [], routes: [], env: [], systems: {} } as any, 'auth middleware', 'auth' ); expect(ranked[0].file).toContain('auth'); expect(ranked[ranked.length - 1].file).toContain('scripts'); }); it('includes env hints for SUPABASE when topic is auth', async () => { const request: LocalContextRequest = { projectPath: FIXTURE_PROJECT_PATH, query: 'auth supabase login', taskType: 'understand', maxTokens: 1000, useProjectHintsCache: false, }; const result = await localContext(request); // llmBundle should exist with envHints // Note: llmBundle is optional in type; ensure we generated it expect(result.llmBundle).toBeDefined(); const envHints = result.llmBundle?.envHints || []; expect(envHints.some(k => /SUPABASE_/i.test(k))).toBe(true); }); it('should respect exclude patterns when provided', async () => { const request: LocalContextRequest = { projectPath: FIXTURE_PROJECT_PATH, query: 'auth supabase login', taskType: 'understand', maxTokens: 1000, useProjectHintsCache: false, excludePatterns: ['**/auth/**'], }; const result = await localContext(request); expect(result.success).toBe(true); const hasAuthFile = result.jumpTargets.some(target => /auth/i.test(target.file)); expect(hasAuthFile).toBe(false); }); }); // ===== GOLDEN TESTS ===== describe('Enhanced Local Context - Golden Tests', () => { // These tests ensure deterministic output for regression testing it('should produce consistent output for database query', async () => { const request: LocalContextRequest = { projectPath: FIXTURE_PROJECT_PATH, query: 'database initialization', taskType: 'understand', attackPlan: 'init-read-write', maxTokens: 1500, maxSimilarChunks: 5, useProjectHintsCache: false, }; const result1 = await localContext(request); const result2 = await localContext(request); // Results should be deterministic (same inputs = same outputs) expect(result1.answerDraft).toBe(result2.answerDraft); expect(result1.jumpTargets.length).toBe(result2.jumpTargets.length); expect(result1.miniBundle.length).toBe(result2.miniBundle.length); }); it('should have consistent metadata format', async () => { const request: LocalContextRequest = { projectPath: FIXTURE_PROJECT_PATH, query: 'test query', taskType: 'debug', maxTokens: 1000, useProjectHintsCache: false, }; const result = await localContext(request); expect(result.metadata).toMatchObject({ filesScanned: expect.any(Number), symbolsConsidered: expect.any(Number), originalTokens: expect.any(Number), compactedTokens: expect.any(Number), bundleTokens: expect.any(Number), processingTimeMs: expect.any(Number), }); expect(result.next).toMatchObject({ mode: expect.stringMatching(/^(code_lookup|project_research|implementation_ready)$/), openFiles: expect.any(Array), checks: expect.any(Array), }); }); }); // ===== PERFORMANCE TESTS ===== describe('Enhanced Local Context - Performance Tests', () => { it('should complete analysis within reasonable time', async () => { const startTime = Date.now(); const request: LocalContextRequest = { projectPath: FIXTURE_PROJECT_PATH, query: 'comprehensive code analysis', taskType: 'understand', maxTokens: 3000, maxSimilarChunks: 20, useProjectHintsCache: false, }; const result = await localContext(request); const endTime = Date.now(); const duration = endTime - startTime; expect(result.success).toBe(true); expect(duration).toBeLessThan(5000); // Should complete within 5 seconds expect(result.metadata.processingTimeMs).toBeLessThan(5000); }); it('should handle large file sets efficiently', async () => { // Create a larger mock file set const largeFileSet = Array.from({ length: 50 }, (_, i) => ({ absPath: path.join(FIXTURE_PROJECT_PATH, `src/file${i}.ts`), relPath: `src/file${i}.ts`, size: 1024, ext: '.ts', language: 'typescript' as const, })); mockReadFileSync.mockReturnValue('export const test = "value";'); const request: LocalContextRequest = { projectPath: FIXTURE_PROJECT_PATH, query: 'find test patterns', taskType: 'test', maxTokens: 2000, maxSimilarChunks: 30, useProjectHintsCache: false, }; const result = await localContext(request); expect(result.success).toBe(true); expect(result.metadata.processingTimeMs).toBeLessThan(10000); // 10 second limit }); }); // ===== ERROR HANDLING TESTS ===== describe('Enhanced Local Context - Error Handling', () => { it('should handle invalid queries gracefully', async () => { const request: LocalContextRequest = { projectPath: FIXTURE_PROJECT_PATH, query: '', // Empty query taskType: 'understand', maxTokens: 1000, useProjectHintsCache: false, }; const result = await localContext(request); // Should not crash, but may return empty results expect(result).toBeDefined(); expect(result.success).toBeDefined(); }); it('should handle missing files gracefully', async () => { mockReadFileSync.mockImplementation(() => { throw new Error('File not found'); }); const request: LocalContextRequest = { projectPath: FIXTURE_PROJECT_PATH, query: 'analyze missing files', taskType: 'debug', maxTokens: 1000, useProjectHintsCache: false, }; const result = await localContext(request); // Should handle errors gracefully expect(result).toBeDefined(); expect(typeof result.answerDraft).toBe('string'); }); it('should respect maximum token limits', async () => { const request: LocalContextRequest = { projectPath: FIXTURE_PROJECT_PATH, query: 'comprehensive analysis', taskType: 'understand', maxTokens: 100, // Very small limit maxSimilarChunks: 50, // Large chunk request useProjectHintsCache: false, }; const result = await localContext(request); expect(result.success).toBe(true); expect(result.metadata.bundleTokens).toBeLessThanOrEqual(100); }); }); // ===== HELPER FUNCTIONS FOR TESTS ===== function createMockFileInfo(relPath: string, content: string): FileInfo { return { absPath: path.join(FIXTURE_PROJECT_PATH, relPath), relPath, size: content.length, ext: path.extname(relPath), language: relPath.endsWith('.ts') ? 'typescript' : relPath.endsWith('.js') ? 'javascript' : 'typescript', }; }

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/sbarron/AmbianceMCP'

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