import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'; import { afterEach, beforeEach, expect, it, vi } from 'vitest'; import { CloudChannelPanel } from '@/pages/CloudAgents/CloudChannelPanel'; const api = vi.hoisted(() => ({ call: vi.fn(), recovery: vi.fn(), resolvePending: vi.fn() })); vi.mock('@/lib/cloud-agents-api', () => ({ cloudAgentsApi: api })); vi.mock('@/pages/CloudAgents/CloudChannelConversations', () => ({ CloudChannelConversations: ({ slug, channelAccountId }: { slug: string; channelAccountId: string }) =>
微信会话 {slug} {channelAccountId}
, })); const slug = 'ml-' + 'a'.repeat(32); const accountId = 'wechat-1'; beforeEach(() => { vi.resetAllMocks(); api.recovery.mockResolvedValue({ recent: null, pending: [] }); api.call.mockImplementation(async (operation: string) => { if (operation === 'channelActivity') return { items: [], next_offset: null }; throw new Error(`Unexpected operation ${operation}`); }); }); afterEach(cleanup); it('shows only account activity and WeChat conversations without invitation or audience controls', async () => { render(); await screen.findByText('还没有渠道活动。'); expect(screen.getByRole('button', { name: '活动记录' })).toBeVisible(); expect(screen.getByRole('button', { name: '微信对话' })).toBeVisible(); expect(screen.queryByText(/本人|受邀|邀请|使用范围|使用权限|授权调用者/)).toBeNull(); expect(api.call).toHaveBeenCalledWith('channelActivity', { slug, channel_account_id: accountId, offset: 0, limit: 50 }); expect(api.call.mock.calls.some(call => ['channelPolicy', 'createChannelPairing', 'channelCallers', 'revokeChannelCaller'].includes(call[0]))).toBe(false); }); it('opens the account-scoped WeChat conversation surface', async () => { render(); fireEvent.click(await screen.findByRole('button', { name: '微信对话' })); expect(screen.getByText(`微信会话 ${slug} ${accountId}`)).toBeVisible(); }); it('looks up delivery by result message ID and keeps file retry account-scoped', async () => { const activity = { run_id: 'run-1', request_id: 'request-1', status: 'completed', created_at: '2026-09-13T00:00:00Z', delivery: { state: 'known', result: { message_id: 'result-message-1' } } }; api.call.mockImplementation(async (operation: string) => { if (operation === 'channelActivity') return { items: [activity], next_offset: null }; if (operation === 'channelDelivery') return { message_id: 'result-message-1', status: 'partial', parts: [{ part_id: 'part-1', type: 'file', status: 'failed' }] }; if (operation === 'retryChannelDeliveryPart') return { message_id: 'result-message-1', status: 'queued', parts: [] }; throw new Error(`Unexpected operation ${operation}`); }); render(); fireEvent.click(await screen.findByRole('button', { name: '重新补发文件' })); await waitFor(() => expect(api.call).toHaveBeenCalledWith('retryChannelDeliveryPart', { slug, channel_account_id: accountId, logical_message_id: 'result-message-1', part_id: 'part-1', })); }); it('ignores retired recovery entries so they cannot block account file operations', async () => { api.recovery.mockResolvedValue({ recent: null, pending: [{ id: 'channelPolicy:old', operation: 'channelPolicy', created_at: '2026-09-13T00:00:00Z', input: { slug, channel_account_id: accountId }, }] }); render(); await screen.findByText('还没有渠道活动。'); expect(screen.queryByText(/结果尚未确认/)).toBeNull(); expect(api.resolvePending).not.toHaveBeenCalled(); });