import { beforeEach, describe, expect, it, vi } from 'vitest'; const proxyFetchMock = vi.hoisted(() => vi.fn()); vi.mock('@/lib/server/proxy-fetch', () => ({ proxyFetch: proxyFetchMock, })); import { parseBraveSearchHtml, searchWithBrave } from '@/lib/web-search/brave'; describe('parseBraveSearchHtml', () => { // Result 1 uses Brave's current `
` title // markup; result 2 uses the legacy `` form — both must parse. it('extracts web results (div + legacy span titles), decodes entities, strips date prefixes, and skips Brave links', () => { const html = `
example.com
Example & Title
Jan 1, 2026 - Result content & more
Brave Help
Internal Brave result
Second result

2 days ago - Secondary description

footer
`; expect(parseBraveSearchHtml(html, 5)).toEqual([ { title: 'Example & Title', url: 'https://example.com/a?x=1&y=2', content: 'Result content & more', score: 1, }, { title: 'Second result', url: 'https://second.example.com', content: 'Secondary description', score: 0.9, }, ]); }); it('does not pair a mismatched title open/close tag (span … /div)', () => { // The closing tag is tied to the captured opening tag, so a malformed // `…
` is not treated as a title and the result is skipped. const html = `
Broken Title
Some content
`; expect(parseBraveSearchHtml(html, 5)).toEqual([]); }); }); describe('searchWithBrave', () => { beforeEach(() => { proxyFetchMock.mockReset(); }); it('uses Brave public search without an API key and clamps long queries', async () => { proxyFetchMock.mockResolvedValueOnce( new Response( `
Example
Content
`, { status: 200, headers: { 'content-type': 'text/html' } }, ), ); const result = await searchWithBrave({ query: 'x'.repeat(500), maxResults: 3, }); const requestedUrl = new URL(proxyFetchMock.mock.calls[0][0]); expect(requestedUrl.origin).toBe('https://search.brave.com'); expect(requestedUrl.pathname).toBe('/search'); expect(requestedUrl.searchParams.get('q')).toHaveLength(400); expect(proxyFetchMock.mock.calls[0][1]).toMatchObject({ method: 'GET' }); expect(result.sources).toHaveLength(1); expect(result.query).toHaveLength(400); }); });