Skip to main content
Glama
database.service.test.ts9.82 kB
/** * Database Service Tests * * Tests for the database service including CRUD operations and search functionality. */ import mongoose from 'mongoose'; import databaseService from '../src/services/database.service'; import KnowledgeEntry, { IKnowledgeEntry } from '../src/models/knowledge-entry.model'; import embeddingService from '../src/services/embedding.service'; // Mock dependencies jest.mock('../src/services/embedding.service', () => ({ generateEmbedding: jest.fn().mockResolvedValue(Array(1536).fill(0.1)), generateEntryEmbedding: jest.fn().mockResolvedValue(Array(1536).fill(0.1)), getDimensions: jest.fn().mockReturnValue(1536), clearCache: jest.fn(), })); // Mock MongoDB connection jest.mock('mongoose', () => { const actualMongoose = jest.requireActual('mongoose'); return { ...actualMongoose, connect: jest.fn().mockResolvedValue(undefined), disconnect: jest.fn().mockResolvedValue(undefined), }; }); // Mock KnowledgeEntry model jest.mock('../src/models/knowledge-entry.model', () => { const mockModel = { findById: jest.fn(), findByIdAndUpdate: jest.fn(), findByIdAndDelete: jest.fn(), find: jest.fn(), aggregate: jest.fn(), }; mockModel.prototype.save = jest.fn().mockResolvedValue({ _id: 'mock-id', title: 'Test Entry', content: 'Test content', contentType: 'text', category: 'test', tags: ['test'], vector: Array(1536).fill(0.1), createdAt: new Date(), updatedAt: new Date(), accessCount: 0, recordAccess: jest.fn().mockResolvedValue(undefined), }); return { __esModule: true, default: mockModel, }; }); describe('Database Service', () => { beforeEach(() => { jest.clearAllMocks(); }); afterAll(async () => { await databaseService.disconnect(); }); describe('Connection Management', () => { it('should connect to MongoDB', async () => { await databaseService.connect(); expect(mongoose.connect).toHaveBeenCalled(); }); it('should disconnect from MongoDB', async () => { await databaseService.disconnect(); expect(mongoose.disconnect).toHaveBeenCalled(); }); }); describe('CRUD Operations', () => { it('should create a knowledge entry', async () => { const entryData = { title: 'Test Entry', content: 'Test content', contentType: 'text', category: 'test', tags: ['test'], }; const newEntry = await databaseService.createEntry(entryData); expect(embeddingService.generateEntryEmbedding).toHaveBeenCalledWith( entryData.title, entryData.content, entryData.tags ); expect(newEntry).toBeDefined(); expect(newEntry.title).toBe(entryData.title); }); it('should get a knowledge entry by ID', async () => { const mockEntry = { _id: 'mock-id', title: 'Test Entry', content: 'Test content', contentType: 'text', category: 'test', tags: ['test'], vector: Array(1536).fill(0.1), createdAt: new Date(), updatedAt: new Date(), accessCount: 1, recordAccess: jest.fn().mockResolvedValue(undefined), }; (KnowledgeEntry.findById as jest.Mock).mockResolvedValue(mockEntry); const entry = await databaseService.getEntryById('mock-id'); expect(KnowledgeEntry.findById).toHaveBeenCalledWith('mock-id'); expect(entry).toEqual(mockEntry); expect(mockEntry.recordAccess).toHaveBeenCalled(); }); it('should update a knowledge entry', async () => { const mockEntry = { _id: 'mock-id', title: 'Original Title', content: 'Original content', contentType: 'text', category: 'test', tags: ['test'], vector: Array(1536).fill(0.1), }; const updatedMockEntry = { ...mockEntry, title: 'Updated Title', }; (KnowledgeEntry.findById as jest.Mock).mockResolvedValue(mockEntry); (KnowledgeEntry.findByIdAndUpdate as jest.Mock).mockResolvedValue(updatedMockEntry); const updateData = { title: 'Updated Title', }; const updatedEntry = await databaseService.updateEntry('mock-id', updateData); expect(KnowledgeEntry.findById).toHaveBeenCalledWith('mock-id'); expect(embeddingService.generateEntryEmbedding).toHaveBeenCalled(); expect(KnowledgeEntry.findByIdAndUpdate).toHaveBeenCalled(); expect(updatedEntry).toEqual(updatedMockEntry); }); it('should delete a knowledge entry', async () => { (KnowledgeEntry.findByIdAndDelete as jest.Mock).mockResolvedValue({ _id: 'mock-id' }); const result = await databaseService.deleteEntry('mock-id'); expect(KnowledgeEntry.findByIdAndDelete).toHaveBeenCalledWith('mock-id'); expect(result).toBe(true); }); }); describe('Search Operations', () => { it('should perform vector search', async () => { const mockResults = [ { _id: 'result-1', title: 'Result 1', content: 'Result content 1', score: 0.95, }, { _id: 'result-2', title: 'Result 2', content: 'Result content 2', score: 0.85, }, ]; (KnowledgeEntry.aggregate as jest.Mock).mockResolvedValue(mockResults); const results = await databaseService.vectorSearch('test query', 2); expect(embeddingService.generateEmbedding).toHaveBeenCalledWith('test query'); expect(KnowledgeEntry.aggregate).toHaveBeenCalled(); expect(results).toEqual(mockResults); }); it('should perform text search', async () => { const mockResults = [ { _id: 'result-1', title: 'Result 1', content: 'Result content 1', score: 0.95, }, ]; (KnowledgeEntry.find as jest.Mock).mockImplementation(() => ({ sort: jest.fn().mockImplementation(() => ({ limit: jest.fn().mockResolvedValue(mockResults), })), })); const results = await databaseService.textSearch('test query', 1); expect(KnowledgeEntry.find).toHaveBeenCalled(); expect(results).toEqual(mockResults); }); it('should perform hybrid search', async () => { const vectorResults = [ { _id: 'result-1', title: 'Vector Result 1', content: 'Vector content 1', score: 0.95, }, ]; const textResults = [ { _id: 'result-2', title: 'Text Result 2', content: 'Text content 2', score: 0.85, }, ]; // Mock the vector and text search methods jest.spyOn(databaseService, 'vectorSearch').mockResolvedValue(vectorResults); jest.spyOn(databaseService, 'textSearch').mockResolvedValue(textResults); const results = await databaseService.hybridSearch('test query', 2); expect(databaseService.vectorSearch).toHaveBeenCalledWith('test query', 2); expect(databaseService.textSearch).toHaveBeenCalledWith('test query', 2); expect(results.length).toBe(2); expect(results).toContainEqual(vectorResults[0]); expect(results).toContainEqual(textResults[0]); }); }); describe('Filtering Operations', () => { it('should get entries by category', async () => { const mockResults = [ { _id: 'result-1', title: 'Category Result 1', content: 'Category content 1', category: 'test-category', }, ]; (KnowledgeEntry.find as jest.Mock).mockImplementation(() => ({ limit: jest.fn().mockResolvedValue(mockResults), })); const results = await databaseService.getEntriesByCategory('test-category', 1); expect(KnowledgeEntry.find).toHaveBeenCalledWith({ category: 'test-category' }); expect(results).toEqual(mockResults); }); it('should get entries by tag', async () => { const mockResults = [ { _id: 'result-1', title: 'Tag Result 1', content: 'Tag content 1', tags: ['test-tag'], }, ]; (KnowledgeEntry.find as jest.Mock).mockImplementation(() => ({ limit: jest.fn().mockResolvedValue(mockResults), })); const results = await databaseService.getEntriesByTag('test-tag', 1); expect(KnowledgeEntry.find).toHaveBeenCalledWith({ tags: 'test-tag' }); expect(results).toEqual(mockResults); }); it('should get most accessed entries', async () => { const mockResults = [ { _id: 'result-1', title: 'Popular Result 1', content: 'Popular content 1', accessCount: 10, }, ]; (KnowledgeEntry.find as jest.Mock).mockImplementation(() => ({ sort: jest.fn().mockImplementation(() => ({ limit: jest.fn().mockResolvedValue(mockResults), })), })); const results = await databaseService.getMostAccessedEntries(1); expect(KnowledgeEntry.find).toHaveBeenCalled(); expect(results).toEqual(mockResults); }); }); describe('Cache Management', () => { it('should clear search cache', () => { // Mock the internal NodeCache instance const originalClearSearchCache = databaseService.clearSearchCache; databaseService.clearSearchCache = jest.fn(); databaseService.clearSearchCache(); expect(databaseService.clearSearchCache).toHaveBeenCalled(); // Restore original method databaseService.clearSearchCache = originalClearSearchCache; }); }); });

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/tgf-between-your-legs/sdof-mcp'

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