import { act, renderHook, waitFor } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' import useAdminChatSocket from '@/hooks/useAdminChatSocket' const socketListeners = new Map void>() const socket = { connected: true, on: vi.fn((event: string, handler: (...args: never[]) => void) => { socketListeners.set(event, handler) return socket }), timeout: vi.fn(() => ({ emit: vi.fn((event: string, _payload: unknown, callback: (error: null, ack?: { ok: boolean }) => void) => { callback(null, { ok: true }) return event }), })), removeAllListeners: vi.fn(), disconnect: vi.fn(), } vi.mock('socket.io-client', () => ({ io: vi.fn(() => socket) })) vi.mock('@/hooks/useAuth', () => ({ default: () => ({ user: { accessToken: 'admin-token' } }), })) vi.mock('@/lib/observability/client', () => ({ reportBrowserEvent: vi.fn() })) describe('useAdminChatSocket', () => { beforeEach(() => { vi.clearAllMocks() socketListeners.clear() socket.connected = true }) it('joins on connect, forwards live messages, then leaves and disconnects on unmount', async () => { const onMessage = vi.fn() const { result, unmount } = renderHook(() => useAdminChatSocket('conversation-1', onMessage)) act(() => socketListeners.get('connect')?.()) await waitFor(() => { expect(result.current.status).toBe('connected') }) const joinEmitter = socket.timeout.mock.results[0]?.value.emit expect(joinEmitter).toHaveBeenCalledWith('admin:conversation:join', { conversationId: 'conversation-1' }, expect.any(Function)) const incoming = { id: 'message-1', conversationId: 'conversation-1', senderId: 'user-1', senderFirstName: 'علی', senderLastName: 'رضایی', senderAvatarUrl: null, senderGender: 'male' as const, body: 'پیام زنده', imageUrl: null, createdAt: '2026-08-01T10:00:00.000Z', } act(() => socketListeners.get('message:new')?.({ message: incoming } as never)) expect(onMessage).toHaveBeenCalledWith(incoming) unmount() const leaveEmitter = socket.timeout.mock.results[1]?.value.emit expect(leaveEmitter).toHaveBeenCalledWith('admin:conversation:leave', { conversationId: 'conversation-1' }, expect.any(Function)) expect(socket.removeAllListeners).toHaveBeenCalled() expect(socket.disconnect).toHaveBeenCalled() }) })