Skip to main content
Glama
ReviewManager.test.ts10.6 kB
import { ReviewManager } from '../../src/core/ReviewManager.js'; import { TrackerManager } from '../../src/core/TrackerManager.js'; import { ReviewableItem, ReviewAction, ReviewStatus, ChurnConfig } from '../../src/types/churn.js'; // Mock TrackerManager const mockTrackerManager = { appendToTracker: jest.fn().mockResolvedValue(true), getContextMap: jest.fn(), refresh: jest.fn() } as jest.Mocked<Partial<TrackerManager>>; // Mock config const mockConfig: ChurnConfig = { collectionsPath: './test-collections', trackingPath: './test-tracking', crossrefPath: './test-crossref.json', aiProvider: 'openai', aiApiKey: 'test-key', confidenceThreshold: 0.7 }; describe('ReviewManager', () => { let reviewManager: ReviewManager; beforeEach(() => { reviewManager = new ReviewManager(mockConfig, mockTrackerManager as TrackerManager); jest.clearAllMocks(); }); describe('flagItemForReview', () => { it('should create a reviewable item with correct defaults', () => { const content = 'Test review item'; const confidence = 0.6; const tracker = 'test-tracker'; const section = 'actions'; const reviewItem = reviewManager.flagItemForReview( content, confidence, tracker, section, 'capture' ); expect(reviewItem).toMatchObject({ content, confidence, currentTracker: tracker, currentSection: section, source: 'capture', reviewStatus: 'flagged', // confidence >= 0.5 metadata: { keywords: [], urgency: 'medium', type: 'review', editableFields: ['tracker', 'priority', 'tags', 'type'] } }); expect(reviewItem.id).toMatch(/^review_\d+_[a-z0-9]+$/); expect(reviewItem.timestamp).toBeInstanceOf(Date); }); it('should set status to pending for low confidence items', () => { const reviewItem = reviewManager.flagItemForReview( 'Low confidence item', 0.3, 'test-tracker', 'actions', 'inference' ); expect(reviewItem.reviewStatus).toBe('pending'); }); it('should use provided metadata', () => { const metadata = { keywords: ['urgent', 'task'], urgency: 'high' as const, type: 'action' as const, editableFields: ['priority'] }; const reviewItem = reviewManager.flagItemForReview( 'Test item', 0.8, 'test-tracker', 'actions', 'capture', metadata ); expect(reviewItem.metadata).toEqual(metadata); }); }); describe('getItemsNeedingReview', () => { beforeEach(() => { // Add some test items reviewManager.flagItemForReview('Item 1', 0.3, 'tracker1', 'actions', 'capture'); reviewManager.flagItemForReview('Item 2', 0.8, 'tracker1', 'actions', 'capture'); reviewManager.flagItemForReview('Item 3', 0.6, 'tracker2', 'references', 'inference'); }); it('should return all items needing review when no tracker specified', () => { const items = reviewManager.getItemsNeedingReview(); expect(items).toHaveLength(3); expect(items.every(item => item.reviewStatus === 'pending' || item.reviewStatus === 'flagged' )).toBe(true); }); it('should filter by tracker when specified', () => { const items = reviewManager.getItemsNeedingReview('tracker1'); expect(items).toHaveLength(2); expect(items.every(item => item.currentTracker === 'tracker1')).toBe(true); }); it('should exclude confirmed items', () => { const items = reviewManager.getItemsNeedingReview(); const firstItem = items[0]; // Confirm one item reviewManager.updateReviewStatus(firstItem.id, 'confirmed'); const updatedItems = reviewManager.getItemsNeedingReview(); expect(updatedItems).toHaveLength(2); expect(updatedItems.find(item => item.id === firstItem.id)).toBeUndefined(); }); }); describe('getReviewStatus', () => { beforeEach(() => { reviewManager.flagItemForReview('Pending item', 0.3, 'tracker1', 'actions', 'capture'); reviewManager.flagItemForReview('Flagged item', 0.8, 'tracker1', 'actions', 'capture'); const items = reviewManager.getItemsNeedingReview(); reviewManager.updateReviewStatus(items[1].id, 'confirmed'); }); it('should return correct status counts', () => { const status = reviewManager.getReviewStatus(); expect(status).toEqual({ pending: 1, flagged: 0, confirmed: 1, total: 2 }); }); }); describe('processReviewAction', () => { let reviewItem: ReviewableItem; beforeEach(() => { reviewItem = reviewManager.flagItemForReview( 'Test action item', 0.8, 'test-tracker', 'actions', 'capture' ); }); it('should accept an item and move to tracker', async () => { const result = await reviewManager.processReviewAction(reviewItem.id, 'accept'); expect(result).toBe(true); expect(mockTrackerManager.appendToTracker).toHaveBeenCalledWith( 'test-tracker', expect.any(String) ); // Item should be removed from review queue expect(reviewManager.getReviewItem(reviewItem.id)).toBeUndefined(); }); it('should edit priority', async () => { const result = await reviewManager.processReviewAction( reviewItem.id, 'edit-priority', { priority: 'high' } ); expect(result).toBe(true); const updatedItem = reviewManager.getReviewItem(reviewItem.id); expect(updatedItem?.metadata.urgency).toBe('high'); }); it('should edit tags', async () => { const newTags = ['urgent', 'task']; const result = await reviewManager.processReviewAction( reviewItem.id, 'edit-tags', { tags: newTags } ); expect(result).toBe(true); const updatedItem = reviewManager.getReviewItem(reviewItem.id); expect(updatedItem?.metadata.keywords).toEqual(newTags); }); it('should edit type', async () => { const result = await reviewManager.processReviewAction( reviewItem.id, 'edit-type', { type: 'action' } ); expect(result).toBe(true); const updatedItem = reviewManager.getReviewItem(reviewItem.id); expect(updatedItem?.metadata.type).toBe('action'); }); it('should move item to different tracker', async () => { const result = await reviewManager.processReviewAction( reviewItem.id, 'move', { tracker: 'new-tracker' } ); expect(result).toBe(true); const updatedItem = reviewManager.getReviewItem(reviewItem.id); expect(updatedItem?.currentTracker).toBe('new-tracker'); }); it('should reject and remove item', async () => { const result = await reviewManager.processReviewAction(reviewItem.id, 'reject'); expect(result).toBe(true); expect(reviewManager.getReviewItem(reviewItem.id)).toBeUndefined(); }); it('should throw error for non-existent item', async () => { await expect( reviewManager.processReviewAction('non-existent', 'accept') ).rejects.toThrow('Review item with ID non-existent not found'); }); it('should throw error for unknown action', async () => { await expect( reviewManager.processReviewAction(reviewItem.id, 'unknown' as ReviewAction) ).rejects.toThrow('Unknown review action: unknown'); }); }); describe('batchProcessReview', () => { let reviewItems: ReviewableItem[]; beforeEach(() => { reviewItems = [ reviewManager.flagItemForReview('Item 1', 0.8, 'tracker1', 'actions', 'capture'), reviewManager.flagItemForReview('Item 2', 0.6, 'tracker1', 'actions', 'capture'), reviewManager.flagItemForReview('Item 3', 0.9, 'tracker2', 'references', 'inference') ]; }); it('should process multiple actions successfully', async () => { const actions = [ { itemId: reviewItems[0].id, action: 'accept' as ReviewAction }, { itemId: reviewItems[1].id, action: 'edit-priority' as ReviewAction, newValues: { priority: 'high' } }, { itemId: reviewItems[2].id, action: 'reject' as ReviewAction } ]; const result = await reviewManager.batchProcessReview(actions); expect(result.success).toBe(3); expect(result.failed).toBe(0); expect(result.results).toHaveLength(3); expect(result.results.every(r => r.success)).toBe(true); }); it('should handle individual failures in batch', async () => { const actions = [ { itemId: 'non-existent', action: 'accept' as ReviewAction }, { itemId: reviewItems[1].id, action: 'edit-priority' as ReviewAction, newValues: { priority: 'high' } } ]; const result = await reviewManager.batchProcessReview(actions); expect(result.success).toBe(1); expect(result.failed).toBe(1); expect(result.results[0].success).toBe(false); expect(result.results[0].error).toContain('not found'); expect(result.results[1].success).toBe(true); }); }); describe('clearConfirmedItems', () => { it('should remove only confirmed items', () => { const item1 = reviewManager.flagItemForReview('Item 1', 0.8, 'tracker1', 'actions', 'capture'); const item2 = reviewManager.flagItemForReview('Item 2', 0.6, 'tracker1', 'actions', 'capture'); reviewManager.updateReviewStatus(item1.id, 'confirmed'); const clearedCount = reviewManager.clearConfirmedItems(); expect(clearedCount).toBe(1); expect(reviewManager.getReviewItem(item1.id)).toBeUndefined(); expect(reviewManager.getReviewItem(item2.id)).toBeDefined(); }); }); describe('updateReviewStatus', () => { it('should update status for existing item', () => { const item = reviewManager.flagItemForReview('Test item', 0.8, 'tracker1', 'actions', 'capture'); const result = reviewManager.updateReviewStatus(item.id, 'confirmed'); expect(result).toBe(true); expect(reviewManager.getReviewItem(item.id)?.reviewStatus).toBe('confirmed'); }); it('should return false for non-existent item', () => { const result = reviewManager.updateReviewStatus('non-existent', 'confirmed'); expect(result).toBe(false); }); }); });

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