Skip to main content
Glama
toolCombinations.test.ts10.7 kB
/** * Integration tests for tool combinations to improve coverage * Tests workflows that use multiple tools together */ import { describe, test, beforeEach, afterEach, expect } from '@jest/globals'; import { setupStandardMocks, setupTestEnvironment, cleanupMocks, } from '../../__tests__/utils/mockSetup'; import { createTestProject, TestPatterns, VALID_TEST_CONTENT, } from '../../__tests__/utils/testHelpers'; setupStandardMocks(); describe('Tool Combination Integration Tests', () => { let envRestore: ReturnType<typeof setupTestEnvironment>['restore']; let testProject: Awaited<ReturnType<typeof createTestProject>>; beforeEach(async () => { cleanupMocks(); const env = setupTestEnvironment({ USE_LOCAL_EMBEDDINGS: 'true', AMBIANCE_API_KEY: 'test-key', }); envRestore = env.restore; // Create a test project with realistic files testProject = await createTestProject([ { name: 'src/index.ts', content: VALID_TEST_CONTENT.codeContent, }, { name: 'src/utils.ts', content: ` export function parseConfig(input: string): Config { try { return JSON.parse(input); } catch (error) { throw new Error('Invalid configuration format'); } } export interface Config { apiKey: string; timeout: number; } `, }, { name: 'src/database.ts', content: ` import { Config } from './utils'; export class DatabaseConnection { private config: Config; constructor(config: Config) { this.config = config; } async connect(): Promise<void> { // Connection logic } async disconnect(): Promise<void> { // Disconnection logic } } `, }, ]); }); afterEach(async () => { await testProject.cleanup(); envRestore(); cleanupMocks(); }); describe('Project Analysis Workflow', () => { test('should combine local_project_hints and local_context for comprehensive analysis', async () => { const { handleProjectHints } = require('../localTools/projectHints'); const { handleSemanticCompact } = require('../localTools/semanticCompact'); // Step 1: Get project overview const hintsResult = await handleProjectHints({ projectPath: testProject.path, format: 'structured', includeContent: true, useAI: false, }); expect(hintsResult.success).toBe(true); expect(hintsResult.hints).toBeDefined(); // Step 2: Get focused context based on hints const contextResult = await handleSemanticCompact({ projectPath: testProject.path, query: 'database connection configuration', maxTokens: 4000, format: 'enhanced', }); expect(contextResult.success).toBe(true); // Results should complement each other expect(hintsResult.hints.projectOverview).toBeDefined(); expect(contextResult.compactedContent).toBeDefined(); }); test('should handle file_summary then local_context workflow', async () => { const { handleFileSummary } = require('../localTools/fileSummary'); const { handleSemanticCompact } = require('../localTools/semanticCompact'); // Step 1: Analyze specific file const summaryResult = await handleFileSummary({ filePath: `${testProject.path}/src/database.ts`, projectPath: testProject.path, includeSymbols: true, maxSymbols: 10, }); expect(summaryResult.success).toBe(true); // Step 2: Get broader context about database-related code const contextResult = await handleSemanticCompact({ projectPath: testProject.path, query: 'database connections and configuration', maxTokens: 4000, format: 'enhanced', }); expect(contextResult.success).toBe(true); // Should provide complementary information expect(summaryResult.summary).toBeDefined(); expect(contextResult.compactedContent).toBeDefined(); }); }); describe('Error Recovery Workflows', () => { test('should handle failed tool gracefully and continue with alternatives', async () => { const { handleProjectHints } = require('../localTools/projectHints'); const { handleFileSummary } = require('../localTools/fileSummary'); // First tool fails let hintsResult; try { hintsResult = await handleProjectHints({ projectPath: '/nonexistent/path', format: 'structured', }); } catch (error) { expect(error).toBeDefined(); } // Should still be able to use alternative approach const summaryResult = await handleFileSummary({ filePath: `${testProject.path}/src/index.ts`, projectPath: testProject.path, includeSymbols: true, }); expect(summaryResult.success).toBe(true); }); test('should handle partial failures in multi-file analysis', async () => { const { handleFileSummary } = require('../localTools/fileSummary'); // Mix of valid and invalid files const files = [ `${testProject.path}/src/index.ts`, // valid '/nonexistent/file.ts', // invalid `${testProject.path}/src/utils.ts`, // valid ]; const results = await Promise.allSettled( files.map(filePath => handleFileSummary({ filePath, projectPath: testProject.path, includeSymbols: true, }) ) ); // Should have both successful and failed results const successful = results.filter(r => r.status === 'fulfilled').length; const failed = results.filter(r => r.status === 'rejected').length; expect(successful).toBeGreaterThan(0); expect(failed).toBeGreaterThan(0); expect(successful + failed).toBe(files.length); }); }); describe('Performance Under Load', () => { test('should handle concurrent tool executions', async () => { const { handleFileSummary } = require('../localTools/fileSummary'); const { handleSemanticCompact } = require('../localTools/semanticCompact'); // Execute multiple tools concurrently const promises = [ handleFileSummary({ filePath: `${testProject.path}/src/index.ts`, projectPath: testProject.path, }), handleFileSummary({ filePath: `${testProject.path}/src/utils.ts`, projectPath: testProject.path, }), handleSemanticCompact({ projectPath: testProject.path, query: 'configuration management', maxTokens: 2000, }), handleSemanticCompact({ projectPath: testProject.path, query: 'database operations', maxTokens: 2000, }), ]; const results = await Promise.all(promises); // All should succeed results.forEach(result => { expect(result.success).toBe(true); }); }); test('should handle resource cleanup after multiple operations', async () => { const { handleSemanticCompact } = require('../localTools/semanticCompact'); // Perform multiple operations that might create resources for (let i = 0; i < 5; i++) { const result = await handleSemanticCompact({ projectPath: testProject.path, query: `test query ${i}`, maxTokens: 1000, }); expect(result.success).toBe(true); } // Resources should be properly cleaned up (no memory leaks) // This is more of a smoke test - in real scenarios you'd monitor memory usage expect(true).toBe(true); }); }); describe('Configuration Variations', () => { test('should work with different format combinations', async () => { const { handleSemanticCompact } = require('../localTools/semanticCompact'); const formats = ['enhanced', 'structured', 'compact']; for (const format of formats) { const result = await handleSemanticCompact({ projectPath: testProject.path, query: 'project structure', maxTokens: 2000, format, }); expect(result.success).toBe(true); expect(result.compactedContent).toBeDefined(); } }); test('should handle different token limits gracefully', async () => { const { handleSemanticCompact } = require('../localTools/semanticCompact'); const tokenLimits = [500, 2000, 8000]; for (const maxTokens of tokenLimits) { const result = await handleSemanticCompact({ projectPath: testProject.path, query: 'comprehensive analysis', maxTokens, }); expect(result.success).toBe(true); // Should respect token limits (approximate) expect(result.compactedContent.length).toBeLessThan(maxTokens * 6); // ~6 chars per token } }); }); describe('Data Flow Validation', () => { test('should maintain data consistency across tool chain', async () => { const { handleProjectHints } = require('../localTools/projectHints'); const { handleSemanticCompact } = require('../localTools/semanticCompact'); // Get hints about the project const hintsResult = await handleProjectHints({ projectPath: testProject.path, format: 'structured', includeContent: true, }); expect(hintsResult.success).toBe(true); const detectedFiles = hintsResult.hints.fileBreakdown?.totalFiles || 0; // Use semantic compaction on the same project const contextResult = await handleSemanticCompact({ projectPath: testProject.path, query: 'all files and functions', maxTokens: 4000, }); expect(contextResult.success).toBe(true); // Data should be consistent expect(detectedFiles).toBeGreaterThan(0); expect(contextResult.compactedContent).toContain('function'); }); test('should handle edge cases in data transformation', async () => { const { handleSemanticCompact } = require('../localTools/semanticCompact'); // Test with empty query const emptyQueryResult = await handleSemanticCompact({ projectPath: testProject.path, query: '', maxTokens: 2000, }); // Should handle gracefully expect(emptyQueryResult.success).toBe(true); // Test with very specific query const specificResult = await handleSemanticCompact({ projectPath: testProject.path, query: 'DatabaseConnection constructor parameters and error handling patterns', maxTokens: 2000, }); expect(specificResult.success).toBe(true); }); }); });

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