import { act, renderHook, waitFor } from '@testing-library/react'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { NextcloudFilesRequestError } from '@/lib/nextcloud-files-api'; import { useTransfers } from './use-transfers'; const mockCreate = vi.fn(); const mockUpload = vi.fn(); vi.mock('@/lib/nextcloud-files-api', async (importOriginal) => { const actual = await importOriginal(); return { ...actual, createFolder: (...a: unknown[]) => mockCreate(...a), listFolder: vi.fn(), }; }); vi.mock('@/lib/nextcloud-files-upload', () => ({ uploadFile: (...a: unknown[]) => mockUpload(...a), })); const file = (name: string) => new File(['x'], name); beforeEach(() => { mockCreate.mockReset().mockResolvedValue({ path: '/A' }); mockUpload.mockReset().mockResolvedValue({ path: '/A/a.txt', size: 1 }); }); describe('useTransfers — Ordner-Merkzettel (WR-07)', () => { it('eine neue Ablage nach dem Ende der vorigen legt den Ordner erneut an (er kann inzwischen geloescht sein)', async () => { const { result } = renderHook(() => useTransfers({ quotaAvailable: null })); act(() => result.current.enqueue([{ file: file('a.txt'), relativeDir: 'A' }], '/')); await waitFor(() => expect(result.current.transfers[0]?.status).toBe('done')); expect(mockCreate).toHaveBeenCalledTimes(1); expect(mockCreate).toHaveBeenLastCalledWith('/A'); act(() => result.current.enqueue([{ file: file('b.txt'), relativeDir: 'A' }], '/')); await waitFor(() => expect(result.current.transfers[1]?.status).toBe('done')); expect(mockCreate).toHaveBeenCalledTimes(2); }); it('innerhalb einer Ablage wird jeder Ordner nur einmal angelegt', async () => { const { result } = renderHook(() => useTransfers({ quotaAvailable: null })); act(() => result.current.enqueue( [ { file: file('a.txt'), relativeDir: 'A' }, { file: file('b.txt'), relativeDir: 'A' }, { file: file('c.txt'), relativeDir: 'A' }, ], '/', ), ); await waitFor(() => expect(result.current.transfers.every((t) => t.status === 'done')).toBe(true), ); expect(mockCreate).toHaveBeenCalledTimes(1); }); it('forgetFolders und ein Hochladen mit pathConflict leeren den Merkzettel', async () => { let release!: () => void; const hold = new Promise((r) => { release = r; }); mockUpload .mockImplementationOnce(async () => { await hold; return { path: '/A/a.txt', size: 1 }; }) .mockRejectedValueOnce(new NextcloudFilesRequestError(409, 'pathConflict', 'fehlt')) .mockResolvedValue({ path: '/A/c.txt', size: 1 }); const { result } = renderHook(() => useTransfers({ quotaAvailable: null })); // Die erste Datei laeuft noch: die zweite Ablage nutzt den Merkzettel. act(() => result.current.enqueue([{ file: file('a.txt'), relativeDir: 'A' }], '/')); await waitFor(() => expect(mockUpload).toHaveBeenCalledTimes(1)); act(() => result.current.enqueue([{ file: file('b.txt'), relativeDir: 'A' }], '/')); await waitFor(() => expect(result.current.transfers[1]?.status).toBe('error')); expect(mockCreate).toHaveBeenCalledTimes(1); // pathConflict: "Erneut versuchen" legt den Ordner wieder an. act(() => result.current.retry(result.current.transfers[1].id)); await waitFor(() => expect(result.current.transfers[1]?.status).toBe('done')); expect(mockCreate).toHaveBeenCalledTimes(2); act(() => result.current.forgetFolders()); act(() => result.current.enqueue([{ file: file('c.txt'), relativeDir: 'A' }], '/')); await waitFor(() => expect(result.current.transfers[2]?.status).toBe('done')); expect(mockCreate).toHaveBeenCalledTimes(3); release(); }); });