import { act, renderHook, waitFor } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' import { useSFTP } from '../../src/hooks/useSFTP' import { usePoolStore } from '../../src/stores/connection/poolStore' import { useSessionStore } from '../../src/stores/sessionStore' import { useTabStore } from '../../src/stores/tabStore' import { mockDesktopApi } from '../setup' const session = { id: 'transfer-host', name: 'Transfer host', host: 'host.example.com', port: 22, username: 'root', authType: 'agent' as const, } describe('useSFTP transfer lifecycle', () => { beforeEach(() => { vi.clearAllMocks() window.opsxApi = mockDesktopApi usePoolStore.setState({ connections: new Map() }) useSessionStore.setState({ sessions: [session] }) useTabStore.setState({ tabs: [{ id: 'sftp-tab', title: 'SFTP', type: 'sftp', sessionId: session.id, isActive: true }], activeTabId: 'sftp-tab', }) vi.mocked(mockDesktopApi.sshConnect).mockResolvedValue({ success: true, connectionId: 'connection-1' }) vi.mocked(mockDesktopApi.sessionGetById).mockResolvedValue({ success: true, session }) vi.mocked(mockDesktopApi.sftpConnect).mockResolvedValue({ success: true, sftpId: 'sftp-1' }) vi.mocked(mockDesktopApi.sftpListDirectory).mockResolvedValue({ success: true, files: [] }) vi.mocked(mockDesktopApi.getHomePath).mockResolvedValue({ success: true, path: '/Users/test' }) vi.mocked(mockDesktopApi.localReadDir).mockResolvedValue({ success: true, files: [] }) }) it('settles a canceled transfer even when its visible row is removed before native completion', async () => { let finishNativeUpload: ((value: { success: boolean }) => void) | undefined vi.mocked(mockDesktopApi.sftpUpload).mockImplementationOnce( () => new Promise((resolve) => { finishNativeUpload = resolve }) ) const { result } = renderHook(() => useSFTP({ tabId: 'sftp-tab' })) await act(async () => { await result.current.connect(session.id) }) let batchPromise: ReturnType act(() => { batchPromise = result.current.uploadFiles([ { path: '/Users/test/large.bin', name: 'large.bin', size: 1024 }, ]) }) await waitFor(() => expect(mockDesktopApi.sftpUpload).toHaveBeenCalledTimes(1)) const taskId = result.current.transferQueue[0]?.id expect(taskId).toBeTruthy() act(() => { result.current.cancelTransfer(taskId!) result.current.removeTransfer(taskId!) }) expect(result.current.transferQueue).toHaveLength(0) expect(mockDesktopApi.sftpCancelTransfer).toHaveBeenCalledWith('sftp-1') await act(async () => { finishNativeUpload?.({ success: true }) await expect(batchPromise!).resolves.toEqual({ completed: 0, failed: 0, errors: [] }) }) }) })