import { beforeEach, expect, it, vi } from 'vitest' const { get } = vi.hoisted(() => ({ get: vi.fn() })) vi.mock('../../composables/useApi', () => ({ useApi: () => ({ get }) })) import { decodePlatformPickerPath, encodePlatformPickerPath, usePlatformFilePickerAdapter, } from '../../composables/usePlatformFilePickerAdapter' beforeEach(() => get.mockReset()) it('preserves mount and relative path identities with punctuation and Unicode', () => { const path = '测试/one, [2] # ?.raw' expect(decodePlatformPickerPath(encodePlatformPickerPath('instrument', path))).toEqual({ mountId: 'instrument', path, }) expect(() => decodePlatformPickerPath('["mount"]')).toThrow() expect(() => decodePlatformPickerPath('["", "file"]')).toThrow() }) it('passes cancellation, marks offline mounts, and maps compound extensions', async () => { get .mockResolvedValueOnce({ mounts: [ { id: 'online', label: 'Instrument', available: true }, { id: 'offline', label: 'NAS', available: false }, ], }) .mockResolvedValueOnce({ listings: [{ mount_id: 'online', path: '', entries: [{ kind: 'file', path: 'run.mzML.gz', name: 'run.mzML.gz', size_bytes: 7 }], }], }) const adapter = usePlatformFilePickerAdapter({ apiBaseUrl: '/api' }) const signal = new AbortController().signal const roots = await adapter.listRoot({ signal }) expect(roots[1]?.unavailableReason).toContain('offline') const files = await adapter.listChildren(roots[0]!.path, { signal }) expect(files[0]).toMatchObject({ kind: 'file', format: 'mzML.gz', bytes: 7, parentPath: roots[0]!.path, }) expect(get.mock.calls.every((call) => call[1].signal === signal)).toBe(true) }) it('refuses truncated listings rather than returning a partial folder selection', async () => { get.mockResolvedValue({ listings: [{ mount_id: 'a', path: '', truncated: true, entries: [] }] }) await expect( usePlatformFilePickerAdapter({ apiBaseUrl: '/api' }).listChildren( encodePlatformPickerPath('a'), ), ).rejects.toThrow('listing limit') }) it('scopes shallow search to one level and recursive search skips offline mounts', async () => { get.mockImplementation(async (url: string) => url === '/filesystem/mounts' ? { mounts: [ { id: 'a', label: 'Instrument', available: true }, { id: 'b', label: 'NAS', available: false }, ], } : { listings: [{ mount_id: 'a', path: '', entries: [{ kind: 'file', path: 'run.raw', name: 'run.raw', size_bytes: 1 }] }] }, ) const adapter = usePlatformFilePickerAdapter({ apiBaseUrl: '/api' }) expect(await adapter.search('run', { path: '', recursive: false })).toEqual([]) expect(await adapter.search('run', { path: '', recursive: true })).toHaveLength(1) expect(get.mock.calls.filter((call) => call[0].includes('/tree'))).toHaveLength(1) }) it('forwards explicit refresh to mount browsing and supports a scoped root', async () => { get.mockResolvedValue({ listings: [{ mount_id: 'raw', path: 'study', entries: [] }] }) const adapter = usePlatformFilePickerAdapter({ apiBaseUrl: '/api', rootLocation: { mount_id: 'raw', path: 'study' }, }) const signal = new AbortController().signal await adapter.listRoot({ refresh: true, signal }) expect(get).toHaveBeenCalledExactlyOnceWith( '/filesystem/tree?mount_id=raw&path=study&refresh=true', { signal }, ) }) it('forwards a mount-list refresh so server directory snapshots are invalidated too', async () => { get.mockResolvedValue({ mounts: [] }) const adapter = usePlatformFilePickerAdapter() const signal = new AbortController().signal await adapter.listRoot({ refresh: true, signal }) expect(get).toHaveBeenCalledExactlyOnceWith('/filesystem/mounts?refresh=true', { signal }) }) it('should search a folder on the server with the query and scope', async () => { get.mockResolvedValue({ entries: [], truncated: false }) await usePlatformFilePickerAdapter({ apiBaseUrl: '/api' }) .search('run', { path: encodePlatformPickerPath('raw', 'study'), recursive: false }) expect(get).toHaveBeenCalledExactlyOnceWith( '/filesystem/search?mount_id=raw&path=study&q=run&recursive=false', { signal: undefined }, ) })