import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { fetchUrlContent } from '../src/lib/url-fetch.ts'; function makeFetch(responses: Array<{ ok: boolean; json?: () => Promise; text?: () => Promise }>) { let call = 0; return vi.fn(async () => { const r = responses[Math.min(call++, responses.length - 1)]; return r as unknown as Response; }); } describe('fetchUrlContent', () => { let originalFetch: typeof globalThis.fetch; beforeEach(() => { originalFetch = globalThis.fetch; }); afterEach(() => { globalThis.fetch = originalFetch; vi.restoreAllMocks(); }); it('returns mana-search markdown when available', async () => { globalThis.fetch = makeFetch([ { ok: true, json: async () => ({ success: true, content: { title: 'Test Page', markdown: '# Test\nContent here' }, }), }, ]); const result = await fetchUrlContent('https://example.com'); expect(result).toContain('# Test Page'); expect(result).toContain('Content here'); }); it('falls back to direct fetch when mana-search fails', async () => { globalThis.fetch = makeFetch([ { ok: false, json: async () => ({ success: false }) }, { ok: true, text: async () => '

Direct content

', }, ]); const result = await fetchUrlContent('https://example.com'); expect(result).toContain('Direct content'); }); it('strips HTML tags in direct fetch fallback', async () => { globalThis.fetch = makeFetch([ { ok: false, json: async () => ({ success: false }) }, { ok: true, text: async () => '

Clean text

', }, ]); const result = await fetchUrlContent('https://example.com'); expect(result).toContain('Clean text'); expect(result).not.toContain('

'); expect(result).not.toContain('alert(1)'); expect(result).not.toContain('body{}'); }); it('returns null when mana-search returns no content and direct fetch fails', async () => { globalThis.fetch = vi.fn().mockRejectedValue(new Error('Network error')); const result = await fetchUrlContent('https://example.com'); expect(result).toBeNull(); }); it('returns null when mana-search returns empty content and direct fetch returns empty', async () => { globalThis.fetch = makeFetch([ { ok: true, json: async () => ({ success: true, content: { markdown: ' ' } }), }, { ok: true, text: async () => ' ', }, ]); const result = await fetchUrlContent('https://example.com'); expect(result).toBeNull(); }); it('truncates content to 8000 characters max', async () => { const longContent = 'A'.repeat(9000); globalThis.fetch = makeFetch([ { ok: true, json: async () => ({ success: true, content: { markdown: longContent }, }), }, ]); const result = await fetchUrlContent('https://example.com'); expect(result).not.toBeNull(); expect(result!.length).toBeLessThanOrEqual(8000); }); });