Skip to main content
Glama
index.test.ts7.59 kB
/** * Brain Module Tests */ import { describe, test, expect, vi, beforeEach, afterEach } from 'vitest'; import { Brain, createBrain } from './index.js'; import type { WikiDocument } from './types.js'; // Mock dependencies vi.mock('./embeddings.js', () => ({ EmbeddingService: vi.fn().mockImplementation(() => ({ isEnabled: vi.fn().mockReturnValue(true), getDimensions: vi.fn().mockReturnValue(1536), getEmbedding: vi.fn().mockResolvedValue(Array(1536).fill(0.1)), getEmbeddings: vi.fn().mockResolvedValue([Array(1536).fill(0.1)]), })), })); vi.mock('./vector-store.js', () => ({ VectorStore: vi.fn().mockImplementation(() => ({ init: vi.fn().mockResolvedValue(undefined), save: vi.fn().mockResolvedValue(5), search: vi.fn().mockResolvedValue([ { id: 'doc1-chunk-0', text: 'Content about topic', title: 'Doc 1', url: 'http://test.com/doc1', score: 0.1 }, ]), count: vi.fn().mockResolvedValue(10), clear: vi.fn().mockResolvedValue(undefined), })), })); vi.mock('./processor.js', () => ({ LlmProcessor: vi.fn().mockImplementation(() => ({ isEnabled: vi.fn().mockReturnValue(true), summarize: vi.fn().mockResolvedValue('This is a summary.'), suggestTags: vi.fn().mockResolvedValue(['tag1', 'tag2', 'tag3']), answerFromContext: vi.fn().mockResolvedValue('This is the answer based on context.'), generateMermaid: vi.fn().mockResolvedValue('graph TD\n A --> B'), })), })); vi.mock('@langchain/textsplitters', () => ({ RecursiveCharacterTextSplitter: vi.fn().mockImplementation(() => ({ createDocuments: vi.fn().mockResolvedValue([ { pageContent: 'Chunk 1 content' }, { pageContent: 'Chunk 2 content' }, ]), })), })); describe('Brain', () => { let brain: Brain; beforeEach(() => { vi.clearAllMocks(); // Set env for enabled state process.env.ENABLE_SMART_FEATURES = 'true'; process.env.OPENAI_API_KEY = 'test-key'; brain = new Brain({ openaiApiKey: 'test-key', enabled: true }); }); afterEach(() => { delete process.env.ENABLE_SMART_FEATURES; delete process.env.OPENAI_API_KEY; }); describe('isEnabled', () => { test('should return true when enabled', () => { expect(brain.isEnabled()).toBe(true); }); test('should return false when disabled', () => { const disabledBrain = new Brain({ enabled: false }); expect(disabledBrain.isEnabled()).toBe(false); }); }); describe('syncDocuments', () => { test('should sync documents and return stats', async () => { const docs: WikiDocument[] = [ { id: 'doc1', title: 'Test Doc', text: 'Document content here' }, { id: 'doc2', title: 'Test Doc 2', text: 'More content' }, ]; const result = await brain.syncDocuments(docs); expect(result.documents).toBe(2); expect(result.chunks).toBeGreaterThan(0); }); test('should skip documents without text', async () => { const docs: WikiDocument[] = [ { id: 'doc1', title: 'Empty Doc', text: '' }, { id: 'doc2', title: 'Valid Doc', text: 'Has content' }, ]; const result = await brain.syncDocuments(docs); expect(result.documents).toBe(1); }); test('should throw when disabled', async () => { const disabledBrain = new Brain({ enabled: false }); await expect(disabledBrain.syncDocuments([])).rejects.toThrow('Smart features are disabled'); }); }); describe('search', () => { test('should return search results', async () => { const results = await brain.search('test query'); expect(results).toHaveLength(1); expect(results[0].title).toBe('Doc 1'); }); test('should throw when disabled', async () => { const disabledBrain = new Brain({ enabled: false }); await expect(disabledBrain.search('test')).rejects.toThrow('Smart features are disabled'); }); }); describe('ask', () => { test('should return answer with sources', async () => { const result = await brain.ask('What is the topic?'); expect(result.answer).toBe('This is the answer based on context.'); expect(result.sources).toHaveLength(1); }); test('should return message when no documents found', async () => { // Override search mock for this test const { VectorStore } = await import('./vector-store.js'); vi.mocked(VectorStore).mockImplementationOnce(() => ({ init: vi.fn().mockResolvedValue(undefined), save: vi.fn(), search: vi.fn().mockResolvedValue([]), count: vi.fn().mockResolvedValue(0), clear: vi.fn(), }) as never); const newBrain = new Brain({ openaiApiKey: 'test-key', enabled: true }); const result = await newBrain.ask('Unknown question'); expect(result.answer).toBe('No relevant documents found in the wiki.'); expect(result.sources).toHaveLength(0); }); }); describe('summarize', () => { test('should return summary', async () => { const summary = await brain.summarize('Long document text'); expect(summary).toBe('This is a summary.'); }); }); describe('suggestTags', () => { test('should return tag suggestions', async () => { const tags = await brain.suggestTags('Document about technology'); expect(tags).toEqual(['tag1', 'tag2', 'tag3']); }); }); describe('generateDiagram', () => { test('should return Mermaid diagram', async () => { const diagram = await brain.generateDiagram('Login flow'); expect(diagram).toContain('graph'); }); }); describe('getStats', () => { test('should return stats when enabled', async () => { const stats = await brain.getStats(); expect(stats.enabled).toBe(true); expect(stats.chunks).toBe(10); }); test('should return disabled stats when not enabled', async () => { const disabledBrain = new Brain({ enabled: false }); const stats = await disabledBrain.getStats(); expect(stats.enabled).toBe(false); expect(stats.chunks).toBe(0); }); }); describe('clear', () => { test('should clear the store', async () => { await expect(brain.clear()).resolves.toBeUndefined(); }); }); }); describe('createBrain factory', () => { test('should create a new Brain instance', () => { const brain1 = createBrain({ enabled: true, openaiApiKey: 'test' }); const brain2 = createBrain({ enabled: true, openaiApiKey: 'test' }); // Factory creates new instances each time expect(brain1).not.toBe(brain2); }); test('should accept custom dependencies', () => { const mockDeps = { embeddings: { isEnabled: () => true, getDimensions: () => 1536, getEmbedding: vi.fn().mockResolvedValue(Array(1536).fill(0.1)), getEmbeddings: vi.fn().mockResolvedValue([Array(1536).fill(0.1)]), }, store: { init: vi.fn().mockResolvedValue(undefined), save: vi.fn().mockResolvedValue(5), search: vi.fn().mockResolvedValue([]), count: vi.fn().mockResolvedValue(0), clear: vi.fn().mockResolvedValue(undefined), }, processor: { isEnabled: () => true, summarize: vi.fn().mockResolvedValue('Mock summary'), suggestTags: vi.fn().mockResolvedValue(['mock-tag']), answerFromContext: vi.fn().mockResolvedValue('Mock answer'), generateMermaid: vi.fn().mockResolvedValue('graph TD\n Mock'), }, }; const brain = createBrain({ enabled: true }, mockDeps); expect(brain.isEnabled()).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/huiseo/outline-wiki-mcp'

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