import { beforeEach, describe, expect, it, vi } from 'vitest'; vi.mock('$lib/services/tools.service', () => ({ ToolsService: { executeToolRaw: vi.fn() } })); import { GlobSearchType } from '$lib/enums'; import { ToolsService } from '$lib/services/tools.service'; import { runGlobSearchWithChildren } from '$lib/utils'; const mockExecute = vi.mocked(ToolsService.executeToolRaw); // Distinct roots per test so the module-level search cache never serves a // prior test's result under the same (type, path, glob, depth) key. beforeEach(() => { mockExecute.mockReset(); }); describe('runGlobSearchWithChildren', () => { it('returns ranked outer entries as absolute paths without descending', async () => { mockExecute.mockResolvedValueOnce({ base: '/Users/rootA', entries: [ { path: 'note.md', type: 'file' }, { path: 'src', type: 'dir' } ] }); const res = await runGlobSearchWithChildren( 'note', '/Users/rootA', 3, 50, new AbortController().signal ); expect(res.error).toBeUndefined(); expect(res.entries.map((e) => e.path)).toEqual(['/Users/rootA/note.md', '/Users/rootA/src']); expect(res.exactDir).toBeUndefined(); expect(mockExecute).toHaveBeenCalledTimes(1); }); it('appends a matched directorys children when the query ends with a separator', async () => { mockExecute .mockResolvedValueOnce({ base: '/Users/rootB', entries: [{ path: 'src', type: 'dir' }] }) .mockResolvedValueOnce({ base: '/Users/rootB/src', entries: [ { path: 'a.txt', type: 'file' }, { path: 'sub', type: 'dir' } ] }); const res = await runGlobSearchWithChildren( '/Users/rootB/src/', '/Users/rootB', 3, 50, new AbortController().signal, { descendOnTrailingSeparator: true, type: GlobSearchType.ALL } ); expect(res.error).toBeUndefined(); expect(res.exactDir).toBe('/Users/rootB/src'); expect(res.entries.map((e) => e.path)).toEqual([ '/Users/rootB/src', '/Users/rootB/src/a.txt', '/Users/rootB/src/sub' ]); expect(mockExecute).toHaveBeenCalledTimes(2); }); it('does not descend without a trailing separator in mention mode', async () => { mockExecute.mockResolvedValueOnce({ base: '/Users/rootC', entries: [{ path: 'src', type: 'dir' }] }); const res = await runGlobSearchWithChildren( '/Users/rootC/src', '/Users/rootC', 3, 50, new AbortController().signal, { descendOnTrailingSeparator: true, type: GlobSearchType.ALL } ); expect(res.exactDir).toBeUndefined(); expect(mockExecute).toHaveBeenCalledTimes(1); }); it('descends on an exact directory match in WD mode', async () => { mockExecute .mockResolvedValueOnce({ base: '/Users/rootD', entries: [{ path: 'src', type: 'dir' }] }) .mockResolvedValueOnce({ base: '/Users/rootD/src', entries: [{ path: 'a.txt', type: 'file' }] }); const res = await runGlobSearchWithChildren( '/Users/rootD/src', '/Users/rootD', 3, 50, new AbortController().signal, { type: GlobSearchType.DIR } ); expect(res.exactDir).toBe('/Users/rootD/src'); expect(res.entries.map((e) => e.path)).toEqual(['/Users/rootD/src', '/Users/rootD/src/a.txt']); expect(mockExecute).toHaveBeenCalledTimes(2); }); it('surfaces a server error without attempting a child walk', async () => { mockExecute.mockResolvedValueOnce({ error: 'boom' }); const res = await runGlobSearchWithChildren( 'src', '/Users/rootE', 3, 50, new AbortController().signal ); expect(res.error).toBe('boom'); expect(res.entries).toEqual([]); expect(mockExecute).toHaveBeenCalledTimes(1); }); });