import { describe, it, expect } from 'vitest';
import { searchContent, isSearchableContent } from '../search.js';
describe('isSearchableContent', () => {
    it('returns true for text/* types', () => {
        expect(isSearchableContent('text/plain')).toBe(true);
        expect(isSearchableContent('text/html')).toBe(true);
    });
    it('returns true for application/json', () => {
        expect(isSearchableContent('application/json')).toBe(true);
    });
    it('returns false for binary types', () => {
        expect(isSearchableContent('image/png')).toBe(false);
        expect(isSearchableContent('video/mp4')).toBe(false);
        expect(isSearchableContent('application/pdf')).toBe(false);
    });
});
describe('searchContent', () => {
    const maxChars = 10_000;
    describe('empty content', () => {
        it('returns empty message for empty string', () => {
            expect(searchContent('', { context_lines: 5, pattern: 'x' }, maxChars)).toBe('Content is empty (0 lines).');
        });
        it('returns empty message for single empty line', () => {
            expect(searchContent('\n', { context_lines: 5, line_range: { start: 1, end: 1 } }, maxChars)).not.toContain('Content is empty');
        });
    });
    describe('line_range validation', () => {
        const text = 'line 1\nline 2\nline 3\nline 4\nline 5';
        it('returns error when start > end', () => {
            const result = searchContent(text, { context_lines: 5, line_range: { start: 5, end: 2 } }, maxChars);
            expect(result).toContain('must be <= line_range.end');
        });
        it('returns error when start > total lines', () => {
            const result = searchContent(text, { context_lines: 5, line_range: { start: 100, end: 200 } }, maxChars);
            expect(result).toContain('beyond content length (5 lines)');
        });
        it('clamps end to total lines', () => {
            const result = searchContent(text, { context_lines: 5, line_range: { start: 3, end: 999 } }, maxChars);
            expect(result).toContain('[Lines 3-5 of 5]');
            expect(result).toContain('line 3');
            expect(result).toContain('line 5');
        });
    });
    describe('pattern search', () => {
        const text = Array.from({ length: 20 }, (_, i) => `line ${i + 1}`).join('\n');
        it('finds a single match with context', () => {
            const result = searchContent(text, { pattern: 'line 10', context_lines: 2 }, maxChars);
            expect(result).toContain('[1 match for /line 10/]');
            expect(result).toContain('> 10| line 10');
            expect(result).toContain('   8| line 8');
            expect(result).toContain('  12| line 12');
            expect(result).not.toContain('line 7');
        });
        it('finds multiple matches', () => {
            const result = searchContent(text, { pattern: 'line [12]0', context_lines: 0 }, maxChars);
            expect(result).toContain('2 matches');
            expect(result).toContain('> 10| line 10');
            expect(result).toContain('> 20| line 20');
        });
        it('returns no-match message when pattern not found', () => {
            const result = searchContent(text, { pattern: 'nonexistent', context_lines: 5 }, maxChars);
            expect(result).toContain("No matches found for pattern 'nonexistent'");
            expect(result).toContain('searched 20 lines');
        });
        it('uses context_lines: 0 for no context', () => {
            const result = searchContent(text, { pattern: 'line 5', context_lines: 0 }, maxChars);
            expect(result).toContain('> 5| line 5');
            expect(result).not.toContain('line 4');
            expect(result).not.toContain('line 6');
        });
        it('merges overlapping context into one group', () => {
            const result = searchContent(text, { pattern: 'line [67]', context_lines: 2 }, maxChars);
            expect(result).toContain('2 matches');
            expect(result).not.toContain('---');
        });
        it('separates non-overlapping groups with ---', () => {
            const result = searchContent(text, { pattern: 'line (1|20)', context_lines: 0 }, maxChars);
            expect(result).toContain('---');
        });
        it('falls back to literal match on invalid regex', () => {
            const text = 'foo (bar\nbaz\nfoo (bar again';
            const result = searchContent(text, { pattern: 'foo (bar', context_lines: 0 }, maxChars);
            expect(result).toContain('2 matches');
            expect(result).toContain('> 1| foo (bar');
            expect(result).toContain('> 3| foo (bar again');
        });
        it('sanitizes pattern in header', () => {
            const text = 'test line\nanother line';
            const result = searchContent(text, { pattern: 'test]\nline', context_lines: 0 }, maxChars);
            // The header should not contain raw ] or newlines
            const header = result.split('\n')[0];
            expect(header).not.toContain(']/');
            expect(header).not.toContain('\n');
        });
    });
    describe('pattern search with line_range', () => {
        const text = Array.from({ length: 30 }, (_, i) => `item ${i + 1}`).join('\n');
        it('searches only within the specified range', () => {
            const result = searchContent(text, { pattern: 'item 1', line_range: { start: 10, end: 20 }, context_lines: 0 }, maxChars);
            expect(result).toContain('in lines 10-20');
            expect(result).toContain('> 10| item 10');
            expect(result).toContain('> 11| item 11');
            expect(result).not.toContain('> 1|');
        });
        it('reports no matches within range', () => {
            const result = searchContent(text, { pattern: 'item 5', line_range: { start: 10, end: 20 }, context_lines: 0 }, maxChars);
            expect(result).toContain('No matches found');
            expect(result).toContain('in lines 10-20');
        });
    });
    describe('line range (no pattern)', () => {
        const text = Array.from({ length: 50 }, (_, i) => `line ${i + 1}`).join('\n');
        it('returns specified range with header', () => {
            const result = searchContent(text, { line_range: { start: 5, end: 10 }, context_lines: 5 }, maxChars);
            expect(result).toContain('[Lines 5-10 of 50]');
            expect(result).toContain('  5| line 5');
            expect(result).toContain(' 10| line 10');
        });
        it('does not show lines outside range', () => {
            const result = searchContent(text, { line_range: { start: 5, end: 10 }, context_lines: 5 }, maxChars);
            expect(result).not.toContain('line 4');
            expect(result).not.toContain('line 11');
        });
        it('does not include --- separators for contiguous lines', () => {
            const result = searchContent(text, { line_range: { start: 1, end: 10 }, context_lines: 5 }, maxChars);
            expect(result).not.toContain('---');
        });
    });
    describe('truncation', () => {
        it('truncates pattern results when output exceeds maxChars', () => {
            const text = Array.from({ length: 500 }, (_, i) => `match line ${i + 1}`).join('\n');
            const result = searchContent(text, { pattern: 'match', context_lines: 0 }, 200);
            expect(result).toContain('output truncated, narrow your search');
            expect(result.length).toBeLessThanOrEqual(250); // 200 + truncation message
        });
        it('truncates line range results when output exceeds maxChars', () => {
            const text = Array.from({ length: 500 }, (_, i) => `line ${i + 1}`).join('\n');
            const result = searchContent(text, { line_range: { start: 1, end: 500 }, context_lines: 5 }, 200);
            expect(result).toContain('output truncated, narrow your range');
            expect(result.length).toBeLessThanOrEqual(250);
        });
        it('does not truncate when output fits within maxChars', () => {
            const text = 'short\ncontent';
            const result = searchContent(text, { line_range: { start: 1, end: 2 }, context_lines: 5 }, maxChars);
            expect(result).not.toContain('truncated');
        });
    });
});
//# sourceMappingURL=search.test.js.map