Skip to main content
Glama
apolosan

Design Patterns MCP Server

by apolosan
pattern-matching.test.ts8.77 kB
import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import { DatabaseManager } from '../../src/services/database-manager'; import { createVectorOperationsService } from '../../src/services/vector-operations'; import { createPatternMatcher } from '../../src/services/pattern-matcher'; import { createPatternSeeder } from '../../src/services/pattern-seeder'; import path from 'path'; async function createFullSchema(dbManager: DatabaseManager): Promise<void> { // Drop existing tables to ensure clean schema dbManager.execute('DROP TABLE IF EXISTS pattern_embeddings'); dbManager.execute('DROP TABLE IF EXISTS pattern_relationships'); dbManager.execute('DROP TABLE IF EXISTS pattern_implementations'); dbManager.execute('DROP TABLE IF EXISTS patterns'); // Create patterns table with all columns including examples 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, examples TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ) `); // Create pattern_implementations table dbManager.execute(` CREATE TABLE pattern_implementations ( id TEXT PRIMARY KEY, pattern_id TEXT NOT NULL, language TEXT NOT NULL, approach TEXT NOT NULL, code TEXT NOT NULL, explanation TEXT NOT NULL, dependencies TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (pattern_id) REFERENCES patterns(id) ON DELETE CASCADE ) `); // Create pattern_relationships table 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 ) `); // Create pattern_embeddings table dbManager.execute(` CREATE TABLE pattern_embeddings ( pattern_id TEXT PRIMARY KEY, embedding TEXT NOT NULL, model TEXT NOT NULL, strategy TEXT NOT NULL, dimensions INTEGER NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) `); // Create indexes dbManager.execute('CREATE INDEX IF NOT EXISTS idx_patterns_category ON patterns(category)'); dbManager.execute('CREATE INDEX IF NOT EXISTS idx_patterns_complexity ON patterns(complexity)'); dbManager.execute( 'CREATE INDEX IF NOT EXISTS idx_pattern_implementations_pattern_id ON pattern_implementations(pattern_id)' ); dbManager.execute( 'CREATE INDEX IF NOT EXISTS idx_pattern_implementations_language ON pattern_implementations(language)' ); dbManager.execute( 'CREATE INDEX IF NOT EXISTS idx_pattern_relationships_source ON pattern_relationships(source_pattern_id)' ); dbManager.execute( 'CREATE INDEX IF NOT EXISTS idx_pattern_relationships_target ON pattern_relationships(target_pattern_id)' ); dbManager.execute( 'CREATE INDEX IF NOT EXISTS idx_pattern_relationships_type ON pattern_relationships(type)' ); } describe('Pattern Matching with Semantic Search', () => { let dbManager: DatabaseManager; let vectorOps: any; let patternMatcher: any; beforeAll(async () => { // Initialize test database dbManager = new DatabaseManager({ filename: ':memory:', options: { readonly: false }, }); await dbManager.initialize(); // Create full schema manually (since migrations fail on in-memory DBs with existing tables) await createFullSchema(dbManager); // Seed patterns for testing const seeder = createPatternSeeder(dbManager, { patternsPath: path.resolve(__dirname, '../../data/patterns'), batchSize: 10, skipExisting: false, }); const result = await seeder.seedAll(); if (!result.success) { throw new Error(`Failed to seed patterns: ${result.message}`); } // Debug logging removed for cleaner test output // Initialize vector operations vectorOps = createVectorOperationsService(dbManager); // Create pattern matcher patternMatcher = createPatternMatcher(dbManager, vectorOps); }); afterAll(async () => { if (dbManager) { await dbManager.close(); } }); it('should find patterns using semantic similarity', async () => { const request = { id: 'test-1', query: 'factory method pattern', maxResults: 3, }; const matches = await patternMatcher.findMatchingPatterns(request); expect(matches.length).toBeGreaterThan(0); expect(matches[0]).toHaveProperty('pattern'); expect(matches[0]).toHaveProperty('confidence'); expect(matches[0].confidence).toBeGreaterThan(0); expect(matches[0].confidence).toBeLessThanOrEqual(1); }); it('should rank patterns by relevance', async () => { const request = { id: 'test-2', query: 'factory method pattern', maxResults: 5, }; const matches = await patternMatcher.findMatchingPatterns(request); expect(matches.length).toBeGreaterThan(0); // Results should be sorted by confidence (highest first) for (let i = 1; i < matches.length; i++) { expect(matches[i - 1].confidence).toBeGreaterThanOrEqual(matches[i].confidence); } // First result should have reasonable confidence expect(matches[0].confidence).toBeGreaterThan(0.1); }); it('should handle multiple programming languages', async () => { const request = { id: 'test-3', query: 'factory method', programmingLanguage: 'typescript', maxResults: 3, }; const matches = await patternMatcher.findMatchingPatterns(request); expect(matches.length).toBeGreaterThan(0); // Check that implementation guidance is provided const firstMatch = matches[0]; expect(firstMatch).toHaveProperty('implementation'); expect(firstMatch.implementation).toHaveProperty('steps'); expect(Array.isArray(firstMatch.implementation.steps)).toBe(true); expect(firstMatch.implementation.steps.length).toBeGreaterThan(0); }); it('should support hybrid search (keyword + semantic)', async () => { const request = { id: 'test-4', query: 'factory method pattern', maxResults: 3, }; const matches = await patternMatcher.findMatchingPatterns(request); expect(matches.length).toBeGreaterThan(0); // Should find Factory Method pattern const factoryMethodMatch = matches.find((m: any) => m.pattern.name.toLowerCase().includes('factory method') ); expect(factoryMethodMatch).toBeTruthy(); expect(factoryMethodMatch!.confidence).toBeGreaterThan(0.3); }); it('should provide pattern explanations', async () => { const request = { id: 'test-5', query: 'observer pattern', maxResults: 1, }; const matches = await patternMatcher.findMatchingPatterns(request); expect(matches.length).toBeGreaterThan(0); const match = matches[0]; expect(match).toHaveProperty('justification'); expect(match.justification).toHaveProperty('primaryReason'); expect(match.justification).toHaveProperty('supportingReasons'); expect(match.justification).toHaveProperty('benefits'); expect(match.justification).toHaveProperty('drawbacks'); // Benefits and drawbacks should be arrays expect(Array.isArray(match.justification.benefits)).toBe(true); expect(Array.isArray(match.justification.drawbacks)).toBe(true); }); it('should filter by categories', async () => { const request = { id: 'test-6', query: 'factory method pattern', categories: ['Creational'], maxResults: 5, }; const matches = await patternMatcher.findMatchingPatterns(request); expect(matches.length).toBeGreaterThan(0); // All results should be from Creational category for (const match of matches) { expect(match.pattern.category).toBe('Creational'); } }); it('should handle edge case queries gracefully', async () => { const request = { id: 'test-7', query: 'single instance global access', maxResults: 3, }; const matches = await patternMatcher.findMatchingPatterns(request); // Should return array (may or may not be empty depending on matching) expect(Array.isArray(matches)).toBe(true); expect(matches.length).toBeGreaterThanOrEqual(0); }); });

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