import { describe, it, expect, mock } from 'bun:test';
import { ContentAnalyzer } from '@/llm/analyzer.js';
import { LLMClient } from '@/llm/client.js';
import { AnalysisType, ContentType } from '@/llm/types.js';
describe('content analyzer', () => {
describe('content analysis', () => {
it('should handle unavailable llm client', async () => {
const mockClient = {
isAvailable: () => false,
analyze: mock(),
getProviderName: () => null,
} as unknown as LLMClient;
const analyzer = new ContentAnalyzer(mockClient);
const result = await analyzer.analyzeContent({
content: 'test content',
url: 'https://example.com',
title: 'Test Page',
links: [],
analysisType: AnalysisType.FULL,
});
expect(result).toBeNull();
expect(mockClient.analyze).not.toHaveBeenCalled();
});
it('should perform full analysis when llm is available', async () => {
const mockAnalysisResponse = JSON.stringify({
summary: 'Test summary',
keyPoints: ['Point 1', 'Point 2'],
contentType: 'tutorial',
relevantLinks: [
{
url: 'https://example.com/link',
title: 'Related Link',
relevance: 0.8,
reason: 'Related content',
category: 'related_topic',
},
],
confidence: 0.9,
});
const mockClient = {
isAvailable: () => true,
analyze: mock().mockResolvedValue(mockAnalysisResponse),
getProviderName: () => 'anthropic',
} as unknown as LLMClient;
const analyzer = new ContentAnalyzer(mockClient);
const result = await analyzer.analyzeContent({
content: 'This is a tutorial about testing',
url: 'https://example.com/tutorial',
title: 'Testing Tutorial',
links: ['https://example.com/link'],
analysisType: AnalysisType.FULL,
});
expect(result).not.toBeNull();
expect(result!.summary).toBe('Test summary');
expect(result!.keyPoints).toEqual(['Point 1', 'Point 2']);
expect(result!.contentType).toBe(ContentType.TUTORIAL);
expect(result!.relevantLinks).toHaveLength(1);
expect(result!.confidence).toBe(0.9);
});
it('should handle malformed json response', async () => {
const mockClient = {
isAvailable: () => true,
analyze: mock().mockResolvedValue('invalid json'),
getProviderName: () => 'anthropic',
} as unknown as LLMClient;
const analyzer = new ContentAnalyzer(mockClient);
const result = await analyzer.analyzeContent({
content: 'test content',
url: 'https://example.com',
title: 'Test Page',
links: [],
analysisType: AnalysisType.FULL,
});
// should attempt fallback analysis
expect(mockClient.analyze).toHaveBeenCalled();
// result might be null or contain fallback data
});
it('should perform summary-only analysis', async () => {
const mockClient = {
isAvailable: () => true,
analyze: mock().mockResolvedValue('This is a concise summary of the content.'),
getProviderName: () => 'anthropic',
} as unknown as LLMClient;
const analyzer = new ContentAnalyzer(mockClient);
const result = await analyzer.analyzeContent({
content: 'Long content that needs summarization',
url: 'https://example.com',
title: 'Test Page',
links: [],
analysisType: AnalysisType.SUMMARY_ONLY,
});
expect(result).not.toBeNull();
expect(result!.summary).toBe('This is a concise summary of the content.');
expect(result!.keyPoints).toEqual([]);
expect(result!.relevantLinks).toEqual([]);
expect(result!.confidence).toBe(0.7);
});
it('should handle links analysis', async () => {
const mockLinksResponse = JSON.stringify([
{
url: 'https://example.com/related',
title: 'Related Page',
relevance: 0.9,
reason: 'Directly related to main topic',
category: 'related_topic',
},
]);
const mockClient = {
isAvailable: () => true,
analyze: mock().mockResolvedValue(mockLinksResponse),
getProviderName: () => 'anthropic',
} as unknown as LLMClient;
const analyzer = new ContentAnalyzer(mockClient);
const result = await analyzer.analyzeContent({
content: 'Content with links',
url: 'https://example.com',
title: 'Test Page',
links: ['https://example.com/related', 'https://example.com/other'],
analysisType: AnalysisType.LINKS_ONLY,
});
expect(result).not.toBeNull();
expect(result!.relevantLinks).toHaveLength(1);
expect(result!.relevantLinks[0].url).toBe('https://example.com/related');
expect(result!.relevantLinks[0].relevance).toBe(0.9);
});
it('should handle empty links', async () => {
const mockClient = {
isAvailable: () => true,
analyze: mock(),
getProviderName: () => 'anthropic',
} as unknown as LLMClient;
const analyzer = new ContentAnalyzer(mockClient);
const result = await analyzer.analyzeContent({
content: 'Content without links',
url: 'https://example.com',
title: 'Test Page',
links: [],
analysisType: AnalysisType.LINKS_ONLY,
});
expect(result).not.toBeNull();
expect(result!.relevantLinks).toEqual([]);
expect(result!.confidence).toBe(1.0);
expect(mockClient.analyze).not.toHaveBeenCalled();
});
});
describe('availability check', () => {
it('should report availability correctly', () => {
const availableClient = {
isAvailable: () => true,
getProviderName: () => 'anthropic',
} as unknown as LLMClient;
const unavailableClient = {
isAvailable: () => false,
getProviderName: () => null,
} as unknown as LLMClient;
const availableAnalyzer = new ContentAnalyzer(availableClient);
const unavailableAnalyzer = new ContentAnalyzer(unavailableClient);
expect(availableAnalyzer.isAvailable()).toBe(true);
expect(unavailableAnalyzer.isAvailable()).toBe(false);
});
});
});