Skip to main content
Glama
TrackerManager.test.ts9.03 kB
/** * Tests for TrackerManager - Core tracker management functionality */ import { TrackerManager } from '../../src/core/TrackerManager.js'; import { TEST_CONFIG, MOCK_CROSSREF_ENTRIES, MOCK_TRACKER_CONTENT, createMockFS } from '../utils/test-helpers.js'; import fs from 'fs/promises'; import matter from 'gray-matter'; // Mock the file system jest.mock('fs/promises'); jest.mock('gray-matter'); const mockFS = fs as jest.Mocked<typeof fs>; const mockMatter = matter as jest.MockedFunction<typeof matter>; describe('TrackerManager', () => { let trackerManager: TrackerManager; beforeEach(() => { trackerManager = new TrackerManager(TEST_CONFIG); jest.clearAllMocks(); // Mock console methods to reduce test noise jest.spyOn(console, 'log').mockImplementation(); jest.spyOn(console, 'error').mockImplementation(); jest.spyOn(console, 'warn').mockImplementation(); }); afterEach(() => { jest.restoreAllMocks(); }); describe('initialization', () => { it('should load crossref and trackers successfully', async () => { // Mock crossref file loading mockFS.readFile.mockResolvedValueOnce(JSON.stringify(MOCK_CROSSREF_ENTRIES)); // Mock tracker file loading mockFS.readFile.mockResolvedValueOnce(MOCK_TRACKER_CONTENT); // Mock gray-matter parsing mockMatter.mockReturnValue({ orig: '', language: '', matter: '', stringify: jest.fn(), data: { tag: 'gsc-ai', friendlyName: 'GSC AI Consulting', contextType: 'business' }, content: '## Action Items\\n- [ ] Test item' } as any); await trackerManager.initialize(); expect(mockFS.readFile).toHaveBeenCalledWith(TEST_CONFIG.crossrefPath, 'utf-8'); expect(console.log).toHaveBeenCalledWith('Loaded 3 crossref entries'); }); it('should throw error when crossref loading fails', async () => { mockFS.readFile.mockRejectedValueOnce(new Error('File not found')); await expect(trackerManager.initialize()).rejects.toThrow('Cannot initialize without crossref data'); }); it('should skip inactive trackers during loading', async () => { mockFS.readFile.mockResolvedValueOnce(JSON.stringify(MOCK_CROSSREF_ENTRIES)); // Mock successful loading for active tracker mockFS.readFile.mockResolvedValueOnce(MOCK_TRACKER_CONTENT); mockMatter.mockReturnValue({ orig: "", language: "", matter: "", stringify: jest.fn(), data: { tag: 'gsc-ai', contextType: 'business' }, content: 'content' } as any); await trackerManager.initialize(); // Should only load active trackers (inactive-test should be skipped) expect(mockFS.readFile).toHaveBeenCalledTimes(3); // crossref + 2 active trackers }); }); describe('tracker retrieval', () => { beforeEach(async () => { // Setup successful initialization mockFS.readFile.mockResolvedValueOnce(JSON.stringify(MOCK_CROSSREF_ENTRIES)); mockFS.readFile.mockResolvedValueOnce(MOCK_TRACKER_CONTENT); mockMatter.mockReturnValue({ orig: "", language: "", matter: "", stringify: jest.fn(), data: { tag: 'gsc-ai', friendlyName: 'GSC AI Consulting', contextType: 'business' }, content: MOCK_TRACKER_CONTENT } as any); await trackerManager.initialize(); }); it('should get tracker by tag', () => { const tracker = trackerManager.getTracker('gsc-ai'); expect(tracker).toBeDefined(); expect(tracker?.frontmatter.tag).toBe('gsc-ai'); expect(tracker?.frontmatter.contextType).toBe('business'); }); it('should return undefined for non-existent tracker', () => { const tracker = trackerManager.getTracker('non-existent'); expect(tracker).toBeUndefined(); }); it('should get all trackers when no context filter applied', () => { const trackers = trackerManager.getTrackersByContext(); expect(trackers).toHaveLength(2); // Both gsc-ai and project-55 are loaded expect(trackers[0].frontmatter.tag).toBe('gsc-ai'); }); it('should filter trackers by context type', () => { const businessTrackers = trackerManager.getTrackersByContext('business'); const personalTrackers = trackerManager.getTrackersByContext('personal'); expect(businessTrackers).toHaveLength(2); // Both trackers are business context in our mock expect(businessTrackers[0].frontmatter.contextType).toBe('business'); expect(personalTrackers).toHaveLength(0); }); }); describe('context extraction', () => { beforeEach(async () => { mockFS.readFile.mockResolvedValueOnce(JSON.stringify(MOCK_CROSSREF_ENTRIES)); mockFS.readFile.mockResolvedValueOnce(MOCK_TRACKER_CONTENT); mockMatter.mockReturnValue({ orig: "", language: "", matter: "", stringify: jest.fn(), data: { tag: 'gsc-ai', friendlyName: 'GSC AI Consulting', contextType: 'business' }, content: MOCK_TRACKER_CONTENT } as any); await trackerManager.initialize(); }); it('should create context map for AI inference', () => { const contextMap = trackerManager.getContextMap(); expect(contextMap).toHaveProperty('gsc-ai'); expect(contextMap['gsc-ai']).toEqual({ friendlyName: 'GSC AI Consulting', contextType: 'business', keywords: expect.any(Array), recentActivity: expect.any(Array) } as any); }); it('should extract hashtags and keywords from content', () => { const contextMap = trackerManager.getContextMap(); const keywords = contextMap['gsc-ai'].keywords; expect(keywords).toContain('#task'); expect(keywords).toContain('#urgent'); expect(keywords).toContain('#completed'); }); }); describe('file operations', () => { beforeEach(async () => { mockFS.readFile.mockResolvedValueOnce(JSON.stringify(MOCK_CROSSREF_ENTRIES)); mockFS.readFile.mockResolvedValueOnce(MOCK_TRACKER_CONTENT); mockMatter.mockReturnValue({ orig: "", language: "", matter: "", stringify: jest.fn(), data: { tag: 'gsc-ai', friendlyName: 'GSC AI Consulting', contextType: 'business' }, content: MOCK_TRACKER_CONTENT } as any); await trackerManager.initialize(); }); it('should append entry to tracker successfully', async () => { const existingContent = `---\\ntag: gsc-ai\\n---\\n\\n## Action Items\\n- [ ] Existing item`; mockFS.readFile.mockResolvedValueOnce(existingContent); mockMatter .mockReturnValueOnce({ orig: '', language: '', matter: '', stringify: jest.fn(), data: { tag: 'gsc-ai' }, content: '\\n## Action Items\\n- [ ] Existing item' } as any) .mockImplementation(matter.stringify as any); mockFS.writeFile.mockResolvedValueOnce(undefined); const result = await trackerManager.appendToTracker('gsc-ai', '- [ ] #task New test item'); expect(result).toBe(true); expect(mockFS.writeFile).toHaveBeenCalled(); }); it('should return false when tracker not found', async () => { const result = await trackerManager.appendToTracker('non-existent', '- [ ] Test item'); expect(result).toBe(false); expect(console.error).toHaveBeenCalledWith('Tracker not found: non-existent'); }); it('should handle file write errors gracefully', async () => { mockFS.readFile.mockResolvedValueOnce('mock content'); mockMatter.mockReturnValue({ orig: "", language: "", matter: "", stringify: jest.fn(), data: { tag: 'gsc-ai' }, content: 'content' } as any); mockFS.writeFile.mockRejectedValueOnce(new Error('Write failed')); const result = await trackerManager.appendToTracker('gsc-ai', '- [ ] Test item'); expect(result).toBe(false); expect(console.error).toHaveBeenCalledWith('Failed to append to tracker gsc-ai:', expect.any(Error)); }); }); describe('refresh functionality', () => { it('should reinitialize when refreshed', async () => { const initializeSpy = jest.spyOn(trackerManager, 'initialize' as any); // Mock successful initialization mockFS.readFile.mockResolvedValue(JSON.stringify(MOCK_CROSSREF_ENTRIES)); mockMatter.mockReturnValue({ orig: "", language: "", matter: "", stringify: jest.fn(), data: { tag: 'test' }, content: 'content' } as any); await trackerManager.refresh(); expect(initializeSpy).toHaveBeenCalled(); }); }); });

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/jgsteeler/churnflow-mcp'

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