import { renderHook, act, waitFor } from '@/__tests__/utils/test-helpers' import { usePortfolios } from './use-portfolios' import { apiClient } from '@/lib/api-client' import { createMockPortfolio } from '@/__tests__/utils/test-helpers' jest.mock('@/lib/api-client') describe('usePortfolios Hook', () => { beforeEach(() => { jest.clearAllMocks() }) describe('fetchPortfolios', () => { it('should fetch portfolios on mount', async () => { const mockPortfolios = [createMockPortfolio({ id: 1 }), createMockPortfolio({ id: 2 })] ;(apiClient.get as jest.Mock).mockResolvedValueOnce({ success: true, data: mockPortfolios, }) const { result } = renderHook(() => usePortfolios()) expect(result.current.isLoading).toBe(true) await waitFor(() => { expect(result.current.isLoading).toBe(false) }) expect(result.current.portfolios).toEqual(mockPortfolios) expect(result.current.error).toBeNull() }) it('should set error on fetch failure', async () => { const errorMessage = 'Failed to fetch' ;(apiClient.get as jest.Mock).mockRejectedValueOnce(new Error(errorMessage)) const { result } = renderHook(() => usePortfolios()) await waitFor(() => { expect(result.current.isLoading).toBe(false) }) expect(result.current.error).toBe(errorMessage) expect(result.current.portfolios).toEqual([]) }) it('should handle non-Error exceptions', async () => { ;(apiClient.get as jest.Mock).mockRejectedValueOnce('Unknown error') const { result } = renderHook(() => usePortfolios()) await waitFor(() => { expect(result.current.isLoading).toBe(false) }) expect(result.current.error).toBe('Failed to fetch portfolios') }) it('should clear error on successful refetch', async () => { ;(apiClient.get as jest.Mock).mockRejectedValueOnce(new Error('Network error')) const { result } = renderHook(() => usePortfolios()) await waitFor(() => { expect(result.current.error).toBe('Network error') }) ;(apiClient.get as jest.Mock).mockResolvedValueOnce({ success: true, data: [createMockPortfolio()], }) await act(async () => { await result.current.fetchPortfolios() }) expect(result.current.error).toBeNull() expect(result.current.portfolios.length).toBeGreaterThan(0) }) }) describe('createPortfolio', () => { it('should create new portfolio', async () => { const existingPortfolio = createMockPortfolio({ id: 1 }) const newPortfolio = createMockPortfolio({ id: 2, name: 'New Portfolio' }) ;(apiClient.get as jest.Mock).mockResolvedValueOnce({ success: true, data: [existingPortfolio], }) const { result } = renderHook(() => usePortfolios()) await waitFor(() => { expect(result.current.isLoading).toBe(false) }) ;(apiClient.post as jest.Mock).mockResolvedValueOnce({ success: true, data: newPortfolio, }) let createdPortfolio await act(async () => { createdPortfolio = await result.current.createPortfolio( newPortfolio.name, newPortfolio.domain ) }) expect(createdPortfolio).toEqual(newPortfolio) expect(result.current.portfolios).toContainEqual(newPortfolio) expect(result.current.portfolios.length).toBe(2) }) it('should call API with correct parameters', async () => { ;(apiClient.get as jest.Mock).mockResolvedValueOnce({ success: true, data: [], }) const { result } = renderHook(() => usePortfolios()) await waitFor(() => { expect(result.current.isLoading).toBe(false) }) const newPortfolio = createMockPortfolio() ;(apiClient.post as jest.Mock).mockResolvedValueOnce({ success: true, data: newPortfolio, }) await act(async () => { await result.current.createPortfolio(newPortfolio.name, newPortfolio.domain) }) expect(apiClient.post).toHaveBeenCalledWith('/portfolios', { name: newPortfolio.name, domain: newPortfolio.domain, }) }) it('should throw error on creation failure', async () => { ;(apiClient.get as jest.Mock).mockResolvedValueOnce({ success: true, data: [], }) const { result } = renderHook(() => usePortfolios()) await waitFor(() => { expect(result.current.isLoading).toBe(false) }) const errorMessage = 'Failed to create portfolio' ;(apiClient.post as jest.Mock).mockRejectedValueOnce(new Error(errorMessage)) await expect( act(async () => { await result.current.createPortfolio('Test', 'test.com') }) ).rejects.toThrow(errorMessage) }) }) describe('uploadPortfolio', () => { it('should upload portfolio file', async () => { const portfolio = createMockPortfolio({ id: 1 }) ;(apiClient.get as jest.Mock).mockResolvedValueOnce({ success: true, data: [portfolio], }) const { result } = renderHook(() => usePortfolios()) await waitFor(() => { expect(result.current.isLoading).toBe(false) }) const file = new File(['test content'], 'portfolio.zip') const uploadedPortfolio = createMockPortfolio({ id: 1, path: '/uploads/portfolio.zip', }) ;(apiClient.upload as jest.Mock).mockResolvedValueOnce({ success: true, data: uploadedPortfolio, }) let result_uploaded await act(async () => { result_uploaded = await result.current.uploadPortfolio(1, file) }) expect(result_uploaded).toEqual(uploadedPortfolio) expect(result.current.portfolios[0]).toEqual(uploadedPortfolio) }) it('should call upload API with FormData', async () => { const portfolio = createMockPortfolio({ id: 1 }) ;(apiClient.get as jest.Mock).mockResolvedValueOnce({ success: true, data: [portfolio], }) const { result } = renderHook(() => usePortfolios()) await waitFor(() => { expect(result.current.isLoading).toBe(false) }) const file = new File(['test'], 'test.zip') ;(apiClient.upload as jest.Mock).mockResolvedValueOnce({ success: true, data: portfolio, }) await act(async () => { await result.current.uploadPortfolio(1, file) }) expect(apiClient.upload).toHaveBeenCalledWith( '/portfolios/1/upload', expect.any(FormData) ) }) it('should throw error on upload failure', async () => { ;(apiClient.get as jest.Mock).mockResolvedValueOnce({ success: true, data: [createMockPortfolio()], }) const { result } = renderHook(() => usePortfolios()) await waitFor(() => { expect(result.current.isLoading).toBe(false) }) const errorMessage = 'Upload failed' ;(apiClient.upload as jest.Mock).mockRejectedValueOnce( new Error(errorMessage) ) const file = new File(['test'], 'test.zip') await expect( act(async () => { await result.current.uploadPortfolio(1, file) }) ).rejects.toThrow(errorMessage) }) }) describe('deployPortfolio', () => { it('should deploy portfolio', async () => { const portfolio = createMockPortfolio({ id: 1, active: false }) ;(apiClient.get as jest.Mock).mockResolvedValueOnce({ success: true, data: [portfolio], }) const { result } = renderHook(() => usePortfolios()) await waitFor(() => { expect(result.current.isLoading).toBe(false) }) const deployedPortfolio = createMockPortfolio({ id: 1, active: true, }) ;(apiClient.post as jest.Mock).mockResolvedValueOnce({ success: true, data: deployedPortfolio, }) let deployed await act(async () => { deployed = await result.current.deployPortfolio(1) }) expect(deployed).toEqual(deployedPortfolio) expect(result.current.portfolios[0].active).toBe(true) }) it('should call deploy API correctly', async () => { ;(apiClient.get as jest.Mock).mockResolvedValueOnce({ success: true, data: [createMockPortfolio()], }) const { result } = renderHook(() => usePortfolios()) await waitFor(() => { expect(result.current.isLoading).toBe(false) }) ;(apiClient.post as jest.Mock).mockResolvedValueOnce({ success: true, data: createMockPortfolio(), }) await act(async () => { await result.current.deployPortfolio(1) }) expect(apiClient.post).toHaveBeenCalledWith('/portfolios/1/deploy') }) it('should throw error on deployment failure', async () => { ;(apiClient.get as jest.Mock).mockResolvedValueOnce({ success: true, data: [createMockPortfolio()], }) const { result } = renderHook(() => usePortfolios()) await waitFor(() => { expect(result.current.isLoading).toBe(false) }) const errorMessage = 'Deployment failed' ;(apiClient.post as jest.Mock).mockRejectedValueOnce( new Error(errorMessage) ) await expect( act(async () => { await result.current.deployPortfolio(1) }) ).rejects.toThrow(errorMessage) }) }) describe('state management', () => { it('should maintain portfolio list correctly', async () => { const portfolios = [ createMockPortfolio({ id: 1 }), createMockPortfolio({ id: 2 }), createMockPortfolio({ id: 3 }), ] ;(apiClient.get as jest.Mock).mockResolvedValueOnce({ success: true, data: portfolios, }) const { result } = renderHook(() => usePortfolios()) await waitFor(() => { expect(result.current.portfolios.length).toBe(3) }) expect(result.current.portfolios).toEqual(portfolios) }) it('should track loading state correctly', async () => { ;(apiClient.get as jest.Mock).mockImplementationOnce( () => new Promise((resolve) => setTimeout( () => resolve({ success: true, data: [createMockPortfolio()], }), 100 ) ) ) const { result } = renderHook(() => usePortfolios()) expect(result.current.isLoading).toBe(true) await waitFor(() => { expect(result.current.isLoading).toBe(false) }) }) }) })