Skip to main content
Glama
apolosan

Design Patterns MCP Server

by apolosan
relationships.test.ts.disabled14.4 kB
/** * Integration Tests for Relationship Functionality * Tests end-to-end relationship operations with real database */ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { DatabaseManager } from '../../src/services/database-manager.js'; import { PatternService } from '../../src/services/pattern-service.js'; import { SqlitePatternRepository } from '../../src/repositories/pattern-repository.js'; import { SqliteRelationshipRepository } from '../../src/repositories/relationship-repository.js'; import { CacheService } from '../../src/services/cache.js'; import { SemanticSearchService } from '../../src/services/semantic-search.js'; import { VectorOperationsService } from '../../src/services/vector-operations.js'; describe('Relationship Integration Tests', () => { let dbManager: DatabaseManager; let patternService: PatternService; beforeEach(async () => { // Create test database dbManager = new DatabaseManager({ filename: ':memory:', options: { readonly: false }, }); await dbManager.initialize(); // Initialize schema await createTestSchema(dbManager); // Initialize services const patternRepo = new SqlitePatternRepository(dbManager); const relationshipRepo = new SqliteRelationshipRepository(dbManager); const cacheService = new CacheService(); const semanticSearch = {} as SemanticSearchService; const vectorOps = {} as VectorOperationsService; patternService = new PatternService( patternRepo, relationshipRepo, cacheService, semanticSearch, vectorOps ); // Seed test patterns await seedTestPatterns(dbManager); }); afterEach(async () => { await dbManager.close(); }); describe('Pattern Relationship Management', () => { it('should create and retrieve relationships between patterns', async () => { // Create a relationship const relationship = await patternService.createRelationship({ sourcePatternId: 'adapter', targetPatternId: 'facade', type: 'related', description: 'Both are structural patterns that provide interfaces', }); expect(relationship).toBeDefined(); expect(relationship.sourcePatternId).toBe('adapter'); expect(relationship.targetPatternId).toBe('facade'); expect(relationship.type).toBe('related'); // Retrieve relationships for adapter const adapterRelationships = await patternService.getPatternRelationships('adapter'); expect(adapterRelationships).toHaveLength(1); expect(adapterRelationships[0].targetPatternId).toBe('facade'); // Retrieve relationships with pattern details const relationshipsWithDetails = await patternService.getRelationshipsWithPatterns(); expect(relationshipsWithDetails).toHaveLength(1); expect(relationshipsWithDetails[0].sourcePattern.name).toBe('Adapter'); expect(relationshipsWithDetails[0].targetPattern.name).toBe('Facade'); }); it('should handle multiple relationships for the same pattern', async () => { // Create multiple relationships await patternService.createRelationship({ sourcePatternId: 'adapter', targetPatternId: 'facade', type: 'related', description: 'Structural pattern relationship', }); await patternService.createRelationship({ sourcePatternId: 'adapter', targetPatternId: 'singleton', type: 'uses', description: 'Adapter may use Singleton for instance management', }); await patternService.createRelationship({ sourcePatternId: 'facade', targetPatternId: 'adapter', type: 'complements', description: 'Facade complements Adapter functionality', }); // Check relationships for adapter (should have 2 outgoing + 1 incoming = 3 total) const adapterRelationships = await patternService.getPatternRelationships('adapter'); expect(adapterRelationships).toHaveLength(3); // Check all relationships const allRelationships = await patternService.getRelationshipsWithPatterns(); expect(allRelationships).toHaveLength(3); }); it('should filter relationships by type', async () => { await patternService.createRelationship({ sourcePatternId: 'adapter', targetPatternId: 'facade', type: 'related', description: 'Structural pattern relationship', }); await patternService.createRelationship({ sourcePatternId: 'adapter', targetPatternId: 'singleton', type: 'uses', description: 'Adapter uses Singleton', }); const relatedRelationships = await patternService.getRelationshipsWithPatterns({ type: 'related', }); expect(relatedRelationships).toHaveLength(1); expect(relatedRelationships[0].type).toBe('related'); const usesRelationships = await patternService.getRelationshipsWithPatterns({ type: 'uses', }); expect(usesRelationships).toHaveLength(1); expect(usesRelationships[0].type).toBe('uses'); }); it('should filter relationships by strength', async () => { await patternService.createRelationship({ sourcePatternId: 'adapter', targetPatternId: 'facade', type: 'related', strength: 0.8, description: 'Strong relationship', }); await patternService.createRelationship({ sourcePatternId: 'adapter', targetPatternId: 'singleton', type: 'uses', strength: 0.3, description: 'Weak relationship', }); const strongRelationships = await patternService.getRelationshipsWithPatterns({ minStrength: 0.5, }); expect(strongRelationships).toHaveLength(1); expect(strongRelationships[0].strength).toBe(0.8); }); it('should update relationships', async () => { const created = await patternService.createRelationship({ sourcePatternId: 'adapter', targetPatternId: 'facade', type: 'related', strength: 0.5, description: 'Original description', }); const updated = await patternService.updateRelationship(created.id, { id: created.id, type: 'extends', strength: 0.9, description: 'Updated description', }); expect(updated).toBeDefined(); expect(updated!.type).toBe('extends'); expect(updated!.strength).toBe(0.9); expect(updated!.description).toBe('Updated description'); }); it('should delete relationships', async () => { await patternService.createRelationship({ sourcePatternId: 'adapter', targetPatternId: 'facade', type: 'related', description: 'Test relationship', }); // Verify it exists let relationships = await patternService.getPatternRelationships('adapter'); expect(relationships).toHaveLength(1); // Delete it const deleted = await patternService.deleteRelationship('adapter', 'facade'); expect(deleted).toBe(true); // Verify it's gone relationships = await patternService.getPatternRelationships('adapter'); expect(relationships).toHaveLength(0); }); it('should return pattern with relationships', async () => { await patternService.createRelationship({ sourcePatternId: 'adapter', targetPatternId: 'facade', type: 'related', description: 'Structural pattern relationship', }); const patternWithRelationships = await patternService.getPatternWithRelationships('adapter'); expect(patternWithRelationships).toBeDefined(); if (patternWithRelationships) { expect(patternWithRelationships.id).toBe('adapter'); expect(patternWithRelationships.relatedPatterns || []).toHaveLength(1); expect((patternWithRelationships.relatedPatterns || [])[0].patternId).toBe('facade'); } }); }); describe('Relationship Types', () => { it('should support all relationship types', async () => { const relationshipTypes = [ 'related', 'extends', 'implements', 'uses', 'similar', 'alternative', 'complements', 'conflicts', 'prerequisite', 'successor', ]; // Use unique source patterns for this test to avoid conflicts const uniqueSourceIds = relationshipTypes.map((_, i) => `test_adapter_${i}`); // Insert unique test patterns for (const sourceId of uniqueSourceIds) { dbManager.execute( ` INSERT OR IGNORE INTO patterns (id, name, category, description, complexity) VALUES (?, ?, 'Test', 'Test pattern', 'Low') `, [sourceId, `Test${sourceId}`] ); } for (let i = 0; i < relationshipTypes.length; i++) { const type = relationshipTypes[i]; const relationship = await patternService.createRelationship({ sourcePatternId: uniqueSourceIds[i], targetPatternId: 'facade', type: type as any, description: `Test ${type} relationship`, }); expect(relationship.type).toBe(type); } // Check that relationships were created (pick one of the test patterns) const relationships = await patternService.getPatternRelationships(uniqueSourceIds[0]); expect(relationships).toHaveLength(1); // Each test pattern has 1 relationship }); }); describe('Error Handling', () => { it('should throw error when creating relationship with non-existent source pattern', async () => { await expect( patternService.createRelationship({ sourcePatternId: 'nonexistent', targetPatternId: 'facade', type: 'related', description: 'Test relationship', }) ).rejects.toThrow('Source pattern nonexistent does not exist'); }); it('should throw error when creating relationship with non-existent target pattern', async () => { await expect( patternService.createRelationship({ sourcePatternId: 'adapter', targetPatternId: 'nonexistent', type: 'related', description: 'Test relationship', }) ).rejects.toThrow('Target pattern nonexistent does not exist'); }); it('should return null when updating non-existent relationship', async () => { const result = await patternService.updateRelationship('nonexistent-id', { id: 'nonexistent-id', type: 'related', }); expect(result).toBeNull(); }); it('should return false when deleting non-existent relationship', async () => { const result = await patternService.deleteRelationship('nonexistent1', 'nonexistent2'); expect(result).toBe(false); }); }); describe('Caching', () => { it('should cache relationship queries', async () => { await patternService.createRelationship({ sourcePatternId: 'adapter', targetPatternId: 'facade', type: 'related', description: 'Test relationship', }); // First query await patternService.getPatternRelationships('adapter'); // Second query should use cache const relationships = await patternService.getPatternRelationships('adapter'); expect(relationships).toHaveLength(1); }); it('should invalidate cache when creating relationships', async () => { await patternService.createRelationship({ sourcePatternId: 'adapter', targetPatternId: 'facade', type: 'related', description: 'Test relationship', }); // Cache should be invalidated after creation // This is tested implicitly by the caching test above }); it('should invalidate cache when updating relationships', async () => { const created = await patternService.createRelationship({ sourcePatternId: 'adapter', targetPatternId: 'facade', type: 'related', description: 'Original', }); await patternService.updateRelationship(created.id, { id: created.id, description: 'Updated', }); // Cache should be invalidated }); it('should invalidate cache when deleting relationships', async () => { await patternService.createRelationship({ sourcePatternId: 'adapter', targetPatternId: 'facade', type: 'related', description: 'Test', }); await patternService.deleteRelationship('adapter', 'facade'); // Cache should be invalidated }); }); }); async function createTestSchema(dbManager: DatabaseManager): Promise<void> { // Drop tables if they exist dbManager.execute('DROP TABLE IF EXISTS pattern_relationships'); dbManager.execute('DROP TABLE IF EXISTS pattern_implementations'); dbManager.execute('DROP TABLE IF EXISTS patterns'); // Create tables dbManager.execute(` CREATE TABLE patterns ( id TEXT PRIMARY KEY, name TEXT NOT NULL, category TEXT NOT NULL, description TEXT NOT NULL, when_to_use TEXT, benefits TEXT, drawbacks TEXT, use_cases TEXT, complexity TEXT NOT NULL, tags TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ) `); dbManager.execute(` CREATE TABLE pattern_relationships ( id TEXT PRIMARY KEY, source_pattern_id TEXT NOT NULL, target_pattern_id TEXT NOT NULL, type TEXT NOT NULL, strength REAL DEFAULT 1.0, description TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (source_pattern_id) REFERENCES patterns(id) ON DELETE CASCADE, FOREIGN KEY (target_pattern_id) REFERENCES patterns(id) ON DELETE CASCADE ) `); } async function seedTestPatterns(dbManager: DatabaseManager): Promise<void> { dbManager.execute(` INSERT INTO patterns (id, name, category, description, complexity) VALUES ('adapter', 'Adapter', 'Structural', 'Allows incompatible interfaces to work together', 'Low'), ('facade', 'Facade', 'Structural', 'Provides a simplified interface to a complex subsystem', 'Low'), ('singleton', 'Singleton', 'Creational', 'Ensures only one instance exists', 'Low') `); }

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/apolosan/design_patterns_mcp'

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