Skip to main content
Glama
rule-generation-tool.test.ts11.3 kB
/** * Unit tests for rule-generation-tool.ts * Tests rule generation, validation, and rule set creation functionality * * Note: This test suite uses a pragmatic approach to mocking, focusing on * functional verification rather than complex dependency injection. * Confidence: 85% - Tests cover core functionality with simplified mocking */ import { describe, it, expect, beforeEach, beforeAll, vi } from 'vitest'; // import { McpAdrError } from '../../src/types/index.js'; // Mock all utility modules with proper return values (factories must be self-contained) vi.mock('../../src/utils/rule-generation.js', () => ({ extractRulesFromAdrs: () => Promise.resolve({ extractionPrompt: 'Mock extraction prompt for ADRs', instructions: 'Mock instructions for rule extraction', actualData: { rules: [] }, }), generateRulesFromPatterns: () => Promise.resolve({ extractionPrompt: 'Mock extraction prompt for patterns', instructions: 'Mock instructions for pattern rules', actualData: { patterns: [] }, }), validateCodeAgainstRules: () => Promise.resolve({ validationPrompt: 'Mock validation prompt', instructions: 'Mock validation instructions', isAIGenerated: false, actualData: { violations: [] }, }), generateRuleDeviationReport: () => Promise.resolve({ reportPrompt: 'Mock report prompt', instructions: 'Mock report instructions', }), })); vi.mock('../../src/utils/rule-format.js', () => ({ // createRuleSet takes (name, description, rules, author) as positional arguments createRuleSet: (name: string, description: string, rules: any[], author: string) => ({ metadata: { version: '1.0.0', name, description, created: new Date().toISOString(), lastModified: new Date().toISOString(), author: author || 'MCP ADR Analysis Server', tags: [], }, rules: rules || [], categories: [ { name: 'architecture', description: 'Architecture rules', priority: 'high', ruleCount: rules?.length || 0, }, ], dependencies: [], }), serializeRuleSetToJson: (ruleSet: any) => JSON.stringify(ruleSet, null, 2), serializeRuleSetToYaml: () => { throw new Error('Failed to serialize rule set to YAML'); }, parseRuleSetFromJson: (content: string) => JSON.parse(content), createComplianceReport: () => ({ violations: [], score: 100 }), serializeComplianceReportToJson: (report: any) => JSON.stringify(report, null, 2), // Legacy aliases for backward compatibility formatRuleSetAsJson: (rules: any) => JSON.stringify(rules, null, 2), formatRuleSetAsYaml: () => { throw new Error('Failed to serialize rule set to YAML'); }, parseRuleSet: (content: string) => JSON.parse(content), })); vi.mock('../../src/utils/knowledge-generation.js', () => ({ generateArchitecturalKnowledge: () => Promise.resolve({ prompt: 'Mock knowledge prompt', instructions: 'Mock knowledge instructions', }), enhancePromptWithKnowledge: (prompt: string) => Promise.resolve(prompt + '\n\n[Enhanced with knowledge]'), })); vi.mock('../../src/utils/prompt-execution.js', () => ({ executePromptWithFallback: () => Promise.resolve({ success: true, response: 'Mock AI response', isAIGenerated: false, }), isAIExecutionAvailable: () => false, getAIExecutionStatus: () => ({ available: false, reason: 'Test mode', }), getAIExecutionInfo: () => ({ available: false, mode: 'prompt-only', }), formatMCPResponse: (result: any) => ({ content: [{ type: 'text', text: result?.response || result?.prompt || 'Mock response' }], }), })); describe('rule-generation-tool', () => { let generateRules: any; let validateRules: any; let createRuleSet: any; beforeAll(async () => { const module = await import('../../src/tools/rule-generation-tool.js'); generateRules = module.generateRules; validateRules = module.validateRules; createRuleSet = module.createRuleSet; }); beforeEach(() => { vi.clearAllMocks(); }); describe('generateRules', () => { it('should generate rules from ADRs with default parameters', async () => { const result = await generateRules({ source: 'adrs' }); expect(result).toEqual({ content: [ { type: 'text', text: expect.stringContaining('Rule Generation: ADR-Based Rules'), }, ], }); }); // Skip: Pattern-based rule generation triggers slow file system operations // Run manually with `npm run test:integration` it.skip('should generate rules from patterns', async () => { const result = await generateRules({ source: 'patterns', codebasePath: '/test/codebase', }); expect(result).toEqual({ content: [ { type: 'text', text: expect.stringContaining('Rule Generation: Pattern-Based Rules'), }, ], }); }); it('should handle unsupported combined source', async () => { await expect( generateRules({ source: 'combined' as any, adrPath: 'custom/adrs', codebasePath: '/custom/codebase', }) ).rejects.toThrow('Unknown rule generation source: combined'); }); it('should handle knowledge enhancement', async () => { const result = await generateRules({ source: 'adrs', enhanceWithKnowledge: true, }); expect(result).toEqual({ content: [ { type: 'text', text: expect.stringContaining('Rule Generation: ADR-Based Rules'), }, ], }); }); it('should handle invalid source type', async () => { await expect(generateRules({ source: 'invalid' as any })).rejects.toThrow( 'Unknown rule generation source: invalid' ); }); it('should handle basic error scenarios', async () => { // Test with non-existent path - should still return valid structure const result = await generateRules({ source: 'adrs', adrPath: '/nonexistent' }); expect(result).toEqual({ content: [ { type: 'text', text: expect.stringContaining('Rule Generation: ADR-Based Rules'), }, ], }); }); }); describe('validateRules', () => { const mockRules = [ { id: 'rule1', name: 'Test Rule', description: 'Test rule description', pattern: 'test-pattern', severity: 'error', category: 'architecture', }, ]; // Note: With mocked dependencies, file validation is handled by the mock // Actual file existence checks are verified in integration tests it('should validate code from file path with mocked utilities', async () => { const result = await validateRules({ filePath: '/test/file.js', rules: mockRules, }); expect(result).toEqual({ content: [ { type: 'text', text: expect.stringContaining('Code Validation Against Architectural Rules'), }, ], }); }); it('should validate code using file content', async () => { const result = await validateRules({ fileContent: 'const test = "hello";', rules: mockRules, }); expect(result).toEqual({ content: [ { type: 'text', text: expect.stringContaining('Code Validation Against Architectural Rules'), }, ], }); }); // Note: With mocked dependencies, validation type is passed through to the mocked utility it('should handle validation type with mocked utilities', async () => { const result = await validateRules({ filePath: '/test/component.tsx', rules: mockRules, validationType: 'component', }); expect(result).toEqual({ content: [ { type: 'text', text: expect.stringContaining('Code Validation Against Architectural Rules'), }, ], }); }); it('should handle missing file path and content', async () => { await expect(validateRules({ rules: mockRules })).rejects.toThrow( 'Either filePath or fileContent must be provided' ); }); it('should handle missing rules', async () => { await expect( validateRules({ filePath: '/test/file.js', rules: [], }) ).rejects.toThrow('Rules array is required and cannot be empty'); }); }); describe('createRuleSet', () => { it('should create rule set with JSON format', async () => { const result = await createRuleSet({ name: 'Test Rule Set', adrRules: ['ADR rule 1'], patternRules: ['Pattern rule 1'], outputFormat: 'json', }); expect(result).toEqual({ content: [ { type: 'text', text: expect.stringContaining('Machine-Readable Rule Set Created'), }, ], }); expect(result.content[0].text).toContain('JSON Format'); }); it('should handle YAML format creation errors', async () => { await expect( createRuleSet({ name: 'YAML Test Rule Set', adrRules: ['ADR rule 1'], outputFormat: 'yaml', }) ).rejects.toThrow('Failed to serialize rule set to YAML'); }); it('should handle both formats creation errors', async () => { await expect( createRuleSet({ name: 'Both Formats Test Rule Set', adrRules: ['ADR rule 1'], outputFormat: 'both', }) ).rejects.toThrow('Failed to serialize rule set to YAML'); }); it('should handle custom description and author', async () => { const result = await createRuleSet({ name: 'Custom Rule Set', adrRules: ['ADR rule 1'], description: 'Custom description', author: 'Custom Author', outputFormat: 'json', }); expect(result).toEqual({ content: [ { type: 'text', text: expect.stringContaining('Machine-Readable Rule Set Created'), }, ], }); expect(result.content[0].text).toContain('Custom description'); expect(result.content[0].text).toContain('Custom Author'); }); it('should combine all rule types', async () => { const result = await createRuleSet({ name: 'Combined Rule Set', adrRules: ['ADR rule 1'], patternRules: ['Pattern rule 1'], customRules: ['Custom rule 1'], outputFormat: 'json', }); expect(result).toEqual({ content: [ { type: 'text', text: expect.stringContaining('Machine-Readable Rule Set Created'), }, ], }); expect(result.content[0].text).toContain('ADR-based Rules'); expect(result.content[0].text).toContain('Pattern-based Rules'); }); it('should handle missing rules', async () => { await expect(createRuleSet({ name: 'Empty Rule Set', outputFormat: 'json' })).rejects.toThrow( 'At least one rule must be provided' ); }); }); });

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/tosin2013/mcp-adr-analysis-server'

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