@@ -1,319 +1,63 @@
import { act , cleanup, fireEvent , render , screen , waitFor } from '@testing-library/react' ;
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 ( ) , list : vi.fn ( ) } ) ) ;
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 : ( ) = > < div > 本 人 微信 对 话 < / div > ,
CloudChannelConversations : ( { slug , channelAccountId } : { slug : string ; channelAccountId : string } ) = > < div > 微 信 会 话 { slug } { channelAccountId } < / div > ,
} ) ) ;
const slug = 'ml-' + 'a' . repeat ( 32 ) ;
const binding = {
id : 'wechat-1' , address : 'wechat://1' , display_name : '我的微信' , target_agent_address : 'agent://1' ,
route_revision : 3 , provider_generation : 'provider-1' , binding_id : 'binding-1' , enabled : false ,
status : 'bound' , worker_online : true , agent_slug : slug , published_version : 2 , desired_state : 'paused' ,
sync_state : 'ready' , health : 'connected' , revision : 3 , blockers : [ ] , access_mode : 'self_only' , policy_revision : 1 ,
} ;
const bindingB = {
. . . binding , id : 'wechat-2' , address : 'wechat://2' , display_name : '备用微信' , target_agent_address : 'agent://2' ,
binding_id : 'binding-2' , provider_generation : 'provider-2' , agent_slug : slug ,
} ;
const accountId = 'wechat-1' ;
beforeEach ( ( ) = > {
vi . resetAllMocks ( ) ;
api . recovery . mockResolvedValue ( { recent : null , pending : [ ] } ) ;
api . call . mockImplementation ( async ( operation : string ) = > {
if ( operation === 'channelBindings' ) return { items : [ binding ] , available_accounts : [ ] } ;
if ( operation === 'budget' ) return { request_limit_points : '2.00' , daily_limit_points : '10.00' } ;
if ( operation === 'channelCallers' ) return { items : [ ] } ;
if ( operation === 'channelActivity' ) return { items : [ ] , next_offset : null } ;
if ( operation === 'channelOperation' ) return { operation_id : 'receipt' , status : 'failed' } ;
if ( operation === 'enableChannelBinding' || operation === 'pauseChannelBinding' || operation === 'disconnectChannelBinding' ) return { channel : { . . . binding , enabled : true , desired_state : 'enabled' } } ;
if ( operation === 'channelPolicy' ) return { binding_id : 'binding-1' , channel_account_id : 'wechat-1' , access_mode : 'invited' , policy_revision : 2 , revoked_caller_ids : [ ] , revoked_invitation_ids : [ ] } ;
if ( operation === 'createChannelPairing' ) return { binding_id : 'binding-1' , provider_generation : 'provider-1' , kind : 'self' , consumed : false , code : 'ABC123' } ;
throw new Error ( ` Unexpected operation ${ operation } ` ) ;
} ) ;
} ) ;
afterEach ( cleanup ) ;
it ( 'keeps the connection action available when re covery is still loading at the first click ' , async ( ) = > {
let finishRecovery ! : ( va lue : { recent : null ; pending : [ ] } ) = > void ;
api . recovery . mockReturnValue ( new Promise ( resolve = > { finishRecovery = resolve ; } ) ) ;
const base = api . call . getMockImplementation ( ) ! ;
api . call . mockImplementation ( async ( operation : string , input ) = > operation === 'wechatBindStart' || operation === 'wechatBindStatus'
? { session_key : 'new-qr' , status : 'pending' , qrcode_url : 'https://wechat.example.test/login' } : base ( operation , input ) ) ;
render ( < CloudChannelP anel slug = { slug } publishedVersion = { 2 } / > ) ;
fireEvent . click ( await screen . findByRole ( 'button' , { name : '重新连接' , exact : true } ) ) ;
expect ( api . call . mock . calls . some ( call = > call [ 0 ] === 'wechatBindStart' ) ) . toBe ( false ) ;
expect ( screen . getByRole ( 'button' , { name : '重新连接' , exact : true } ) ) . toBeEnabled ( ) ;
await act ( async ( ) = > { finishRecovery ( { recent : null , pending : [ ] } ) ; } ) ;
fireEvent . click ( screen . getByRole ( 'button' , { name : '重新连接' , exact : true } ) ) ;
await screen . findByRole ( 'img' , { name : '个人微信登录二维码' } ) ;
expect ( api . call . mock . calls . filter ( call = > call [ 0 ] === 'wechatBindStart' ) ) . toHaveLength ( 1 ) ;
it ( 'shows only account activity and WeChat con versations without invitation or audience controls ' , async ( ) = > {
render ( < CloudChannelPanel s lug = { slug } accountId = { accountId } / > ) ;
await screen . findByText ( '还没有渠道活动。' ) ;
expect ( screen . getByRole ( 'button' , { name : '活动记录' } ) ) . toBeVisible ( ) ;
expect ( screen . getByRole ( 'button' , { name : '微信对话' } ) ) . toBeVisible ( ) ;
expect ( screen . queryByText ( /本人|受邀|邀请|使用范围|使用权限|授权调用者/ ) ) . toBeNull ( ) ;
expect ( api . call ) . toHaveBeenCalledWith ( 'ch ann elActivity' , { 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 ( 'requires an explicit enable confirmation and sends the typed binding op eration on ce' , async ( ) = > {
render ( < CloudChannelPanel slug = { slug } publishedVersion = { 2 } / > ) ;
const enable = await screen . findByRole ( 'button' , { name : '启用渠道 ' } ) ;
expect ( api . call . mock . calls . some ( call = > call [ 0 ] === 'enableChannelBinding' ) ) . toBe (false ) ;
fireEvent . click ( enable ) ;
expect ( screen . getByRole ( 'dialog' ) ) . toHaveTextContent ( '确认启用' ) ;
await screen . findByText ( '每次任务上限: 2.00 词元点数' ) ;
fireEvent . click ( screen . getByRole ( 'button' , { name : '确认启用' } ) ) ;
await waitFor ( ( ) = > expect ( api . call . mock . calls . some ( call = > call [ 0 ] === 'enableChannelBinding' ) ) . toBe ( true ) ) ;
const input = api . call . mock . calls . find ( call = > call [ 0 ] === 'enableChannelBinding' ) ? . [ 1 ] ;
expect ( input ) . toEqual ( expect . objectContaining ( { slug , channel_account_id : 'wechat-1' , expected_revision : 3 } ) ) ;
expect ( typeof input . operation_id ) . toBe ( 'string' ) ;
it ( 'opens the account-scoped WeChat conv ers ation surfa ce' , async ( ) = > {
render ( < CloudChannelPanel slug = { slug } accountId = { accountId } / > ) ;
fireEvent . click ( await screen . findByRole ( 'button' , { name : '微信对话 ' } ) ) ;
expect ( screen . getByText ( ` 微信会话 ${ slug } ${ accountId } ` ) ) . toBeVisible ( ) ;
} ) ;
it ( 'uses the bound WeChat identity without offering or requesting a self pairing code ' , async ( ) = > {
render ( < CloudChannelPanel slug = { slug } publishedVersion = { 2 } / > ) ;
fireEvent . click ( await screen . findByRole ( 'button' , { name : '使用权限' } ) ) ;
await screen . findByText ( '使用绑定时扫码的微信,直接发送消息即可。' ) ;
expect ( screen . queryByRole ( 'button' , { name : '生成本人配对码' } ) ) . toBeNull ( ) ;
expect ( screen . queryByRole ( 'button' , { name : '生成邀请码' } ) ) . toBeNull ( ) ;
expect ( api . call . mock . calls . some ( call = > [ 'createChannelPairing' , 'channelPolicy' ] . includes ( call [ 0 ] ) ) ) . toBe ( false ) ;
} ) ;
it ( 'still requires an explicit invitation for other contacts and copies the complete command' , async ( ) = > {
const base = api . call . getMockImplementation ( ) ! ;
api . call . mockImplementation ( async ( operation : string , input ) = > {
if ( operation === 'channelBindings' ) return { items : [ { . . . binding , access_mode : 'invited' } ] , available_accounts : [ ] } ;
if ( operation === 'createChannelPairing' ) return { invitation_id : 'invite-a' , binding_id : 'binding-1' , provider_generation : 'provider-1' , kind : 'invite' , consumed : false , code : 'INVITE123' } ;
return base ( operation , input ) ;
} ) ;
const copy = vi . fn ( ) . mockResolvedValue ( undefined ) ;
Object . defineProperty ( navigator , 'clipboard' , { configurable : true , value : { writeText : copy } } ) ;
render ( < CloudChannelPanel slug = { slug } publishedVersion = { 2 } / > ) ;
fireEvent . click ( await screen . findByRole ( 'button' , { name : '使用权限' } ) ) ;
fireEvent . click ( await screen . findByRole ( 'button' , { name : '生成邀请码' } ) ) ;
await screen . findByText ( 'INVITE123' ) ;
fireEvent . click ( screen . getByRole ( 'button' , { name : '复制邀请命令' } ) ) ;
expect ( copy ) . toHaveBeenCalledWith ( '邀请 INVITE123' ) ;
expect ( api . call . mock . calls . find ( call = > call [ 0 ] === 'createChannelPairing' ) ? . [ 1 ] ) . toEqual ( expect . objectContaining ( { kind : 'invite' , channel_account_id : 'wechat-1' } ) ) ;
} ) ;
it ( 'looks up delivery by the result message ID before offering file retry' , 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' } } ,
} ;
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 === 'channelBindings' ) return { items : [ binding ] , available_accounts : [ ] } ;
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 : [ ] } ;
if ( operation === 'channelCallers' ) return { items : [ ] } ;
throw new Error ( ` Unexpected operation ${ operation } ` ) ;
} ) ;
render ( < CloudChannelPanel slug = { slug } publishedVersion = { 2 } / > ) ;
fireEvent . click ( await screen . findByRole ( 'button' , { name : '活动记录' } ) ) ;
render ( < CloudChannelPanel slug = { slug } accountId = { accountId } / > ) ;
fireEvent . click ( await screen . findByRole ( 'button' , { name : '重新补发文件' } ) ) ;
await waitFor ( ( ) = > expect ( api . call ) . toHaveBeenCalledWith ( 'retryChannelDeliveryPart' , {
slug , channel_account_id : 'wechat-1' , logical_message_id : 'result-message-1' , part_id : 'part-1' ,
slug , channel_account_id : accountId , logical_message_id : 'result-message-1' , part_id : 'part-1' ,
} ) ) ;
} ) ;
it ( 'requires an explicit recovery click and reuses the durable original channel operation' , async ( ) = > {
it ( 'ignores retired recovery entries so they cannot block account file operations ' , async ( ) = > {
api . recovery . mockResolvedValue ( { recent : null , pending : [ {
id : 'channelPolicy:pending ' , operation : 'channelPolicy' , created_at : '2026-09-13T00:00:00Z' ,
input : { slug , channel_account_id : 'wechat-1' , operation_id : 'original-operation' , access_mode : 'invited' , expected_policy_revision : 1 } ,
id : 'channelPolicy:old ' , operation : 'channelPolicy' , created_at : '2026-09-13T00:00:00Z' , input : { slug , channel_account_id : accountId } ,
} ] } ) ;
api . resolvePending . mockResolvedValue ( { operation : 'channelPolicy' , result : { binding_id : 'binding-1' , channel_ account_i d: 'wechat-1' , access_mode : 'invited' , policy_revision : 2 , revoked_caller_ids : [ ] , revoked_invitation_ids : [ ] } } ) ;
render ( < CloudChannelPanel slug = { slug } publishedVersion = { 2 } / > ) ;
const recover = await screen . findByRole ( 'button' , { name : '恢复上次操作' } ) ;
expect ( api . resolvePending ) . not . toHaveBeenCalled ( ) ;
fireEvent . click ( recover ) ;
await waitFor ( ( ) = > expect ( api . resolvePending ) . toHaveBeenCalledWith ( 'channelPolicy:pending' , false ) ) ;
expect ( api . call . mock . calls . some ( call = > call [ 0 ] === 'channelPolicy' ) ) . toBe ( false ) ;
} ) ;
it ( 'can explicitly dismiss a timed-out channel operation without starting a mutation' , async ( ) = > {
api . recovery . mockResolvedValue ( { recent : null , pending : [ {
id : 'retryChannelDeliveryPart:pending' , operation : 'retryChannelDeliveryPart' , created_at : '2026-09-13T00:00:00Z' ,
input : { slug , channel_account_id : 'wechat-1' , logical_message_id : 'result-1' , part_id : 'part-1' } ,
} ] } ) ;
render ( < CloudChannelPanel slug = { slug } publishedVersion = { 2 } / > ) ;
const dismiss = await screen . findByRole ( 'button' , { name : '移除提醒' } ) ;
fireEvent . click ( dismiss ) ;
await waitFor ( ( ) = > expect ( api . resolvePending ) . toHaveBeenCalledWith ( 'retryChannelDeliveryPart:pending' , true ) ) ;
expect ( api . call . mock . calls . some ( call = > call [ 0 ] === 'retryChannelDeliveryPart' ) ) . toBe ( false ) ;
} ) ;
it ( 'opens the current verification input when recovery requires a new code' , async ( ) = > {
const pending = {
id : 'wechatBindVerification:pending' , operation : 'wechatBindVerification' , created_at : '2026-09-13T00:00:00Z' ,
input : { slug , channel_account_id : 'wechat-1' , session_key : 'session-1' , operation_id : 'operation-1' } ,
} ;
api . recovery . mockResolvedValue ( { recent : null , pending : [ pending ] } ) ;
api . resolvePending . mockResolvedValue ( { operation : 'wechatBindVerification' , requires_input : true , input : pending.input } ) ;
api . call . mockImplementation ( async ( operation : string ) = > {
if ( operation === 'channelBindings' ) return { items : [ binding ] , available_accounts : [ ] } ;
if ( operation === 'channelCallers' ) return { items : [ ] } ;
if ( operation === 'channelActivity' ) return { items : [ ] , next_offset : null } ;
if ( operation === 'wechatBindStatus' ) return { session_key : 'session-1' , status : 'verification_required' , message : '等待验证码' } ;
throw new Error ( ` Unexpected operation ${ operation } ` ) ;
} ) ;
render ( < CloudChannelPanel slug = { slug } publishedVersion = { 2 } / > ) ;
fireEvent . click ( await screen . findByRole ( 'button' , { name : '恢复上次操作' } ) ) ;
await waitFor ( ( ) = > expect ( screen . getByLabelText ( '微信验证码' ) ) . toBeInTheDocument ( ) ) ;
expect ( screen . getByText ( '上次扫码需要新的验证码,请输入后重新提交' ) ) . toBeInTheDocument ( ) ;
expect ( screen . queryByText ( '已恢复上次渠道操作,状态已经刷新' ) ) . toBeNull ( ) ;
expect ( api . call . mock . calls . some ( call = > call [ 0 ] === 'wechatBindVerification' ) ) . toBe ( false ) ;
} ) ;
it ( 'drops an old QR session when switching bindings and ignores its late status' , async ( ) = > {
let releaseOldStatus ! : ( value : unknown ) = > void ;
const oldStatus = new Promise ( resolve = > { releaseOldStatus = resolve ; } ) ;
let statusCalls = 0 ;
api . call . mockImplementation ( async ( operation : string , input : Record < string , unknown > ) = > {
if ( operation === 'channelBindings' ) return { items : [ { . . . binding , access_mode : 'invited' } , bindingB ] , available_accounts : [ ] } ;
if ( operation === 'channelCallers' ) return { items : [ ] } ;
if ( operation === 'channelActivity' ) return { items : [ ] , next_offset : null } ;
if ( operation === 'wechatBindStart' ) return { session_key : 'session-a' , status : 'waiting' , qrcode_url : 'https://provider.example/qr-a' } ;
if ( operation === 'wechatBindStatus' && input . session_key === 'session-a' ) {
statusCalls += 1 ;
return statusCalls === 1 ? oldStatus : { session_key : 'session-a' , status : 'waiting' , qrcode_url : 'https://provider.example/qr-a' } ;
}
throw new Error ( ` Unexpected operation ${ operation } ` ) ;
} ) ;
render ( < CloudChannelPanel slug = { slug } publishedVersion = { 2 } / > ) ;
fireEvent . click ( await screen . findByRole ( 'button' , { name : '重新连接' } ) ) ;
await screen . findByText ( '等待中' ) ;
fireEvent . change ( await screen . findByLabelText ( '当前渠道' ) , { target : { value : 'wechat-2' } } ) ;
await waitFor ( ( ) = > expect ( screen . queryByText ( '等待中' ) ) . toBeNull ( ) ) ;
await new Promise ( resolve = > setTimeout ( resolve , 0 ) ) ;
const callsAfterSwitch = statusCalls ;
releaseOldStatus ( { session_key : 'session-a' , status : 'waiting' , qrcode_url : 'https://provider.example/qr-a' } ) ;
await waitFor ( ( ) = > expect ( screen . queryByText ( '等待中' ) ) . toBeNull ( ) ) ;
expect ( statusCalls ) . toBe ( callsAfterSwitch ) ;
expect ( screen . queryByAltText ( '个人微信登录二维码' ) ) . toBeNull ( ) ;
} ) ;
it ( 'clears the previous binding pairing, callers, and activity when switching accounts' , async ( ) = > {
const callerA = { caller_id : 'caller-a' , access_mode : 'invited' , grant_state : 'authorized' , created_at : '2026-09-13T00:00:00Z' } ;
const activityA = {
run_id : 'run-a' , request_id : 'request-a' , status : 'completed' , token_usage : { total_tokens : 7 } ,
delivery : { state : 'known' } , timing : { created_at : '2026-09-13T00:00:00Z' } ,
} ;
api . call . mockImplementation ( async ( operation : string , input : Record < string , unknown > ) = > {
if ( operation === 'channelBindings' ) return { items : [ { . . . binding , access_mode : 'invited' } , bindingB ] , available_accounts : [ ] } ;
if ( operation === 'channelCallers' ) return { items : input.channel_account_id === 'wechat-1' ? [ callerA ] : [ ] } ;
if ( operation === 'channelActivity' ) return { items : input.channel_account_id === 'wechat-1' ? [ activityA ] : [ ] , next_offset : null } ;
if ( operation === 'createChannelPairing' ) return { binding_id : 'binding-1' , provider_generation : 'provider-1' , kind : 'invite' , consumed : false , code : 'PAIR-A' } ;
throw new Error ( ` Unexpected operation ${ operation } ` ) ;
} ) ;
render ( < CloudChannelPanel slug = { slug } publishedVersion = { 2 } / > ) ;
fireEvent . click ( await screen . findByRole ( 'button' , { name : '使用权限' } ) ) ;
await screen . findByText ( '受邀联系人' , { exact : true } ) ;
fireEvent . click ( screen . getByRole ( 'button' , { name : '生成邀请码' } ) ) ;
await screen . findByText ( 'PAIR-A' ) ;
fireEvent . click ( screen . getByRole ( 'button' , { name : '活动记录' } ) ) ;
await screen . findByText ( '词元 7' ) ;
fireEvent . click ( screen . getByRole ( 'button' , { name : '连接微信' } ) ) ;
fireEvent . change ( await screen . findByLabelText ( '当前渠道' ) , { target : { value : 'wechat-2' } } ) ;
await waitFor ( ( ) = > expect ( screen . queryByText ( 'PAIR-A' ) ) . toBeNull ( ) ) ;
fireEvent . click ( screen . getByRole ( 'button' , { name : '使用权限' } ) ) ;
await screen . findByText ( '首次收到你的微信消息后,会在这里显示本人会话。' ) ;
expect ( screen . queryByText ( '受邀联系人' , { exact : true } ) ) . toBeNull ( ) ;
fireEvent . click ( screen . getByRole ( 'button' , { name : '活动记录' } ) ) ;
render ( < CloudChannelPanel slug = { slug } accountI d= { accountId } / > ) ;
await screen . findByText ( '还没有渠道活动。' ) ;
expect ( screen . queryByText ( '词元 7' ) ) . toBeNull ( ) ;
} ) ;
it ( 'loads a later published target when the route agent list has a next cursor' , async ( ) = > {
api . list . mockResolvedValueOnce ( { agents : [ { slug : 'ml-' + 'b' . repeat ( 32 ) , name : '未发布' , published_version : null } ] , next_cursor : 'cursor-2' } )
. mockResolvedValueOnce ( { agents : [ { slug : 'ml-' + 'c' . repeat ( 32 ) , name : '第二页目标' , published_version : 1 } ] , next_cursor : null } ) ;
render ( < CloudChannelPanel slug = { slug } publishedVersion = { 2 } / > ) ;
fireEvent . click ( await screen . findByRole ( 'button' , { name : '选择目标智能体' } ) ) ;
await waitFor ( ( ) = > expect ( screen . getByRole ( 'option' , { name : /第二页目标/ } ) ) . toBeInTheDocument ( ) ) ;
expect ( api . list . mock . calls . some ( call = > call [ 0 ] === 'cursor-2' ) ) . toBe ( true ) ;
} ) ;
it ( 'removes file retry after queued delivery and keeps it hidden after a sent refresh' , async ( ) = > {
const activity = {
run_id : 'run-2' , request_id : 'request-2' , status : 'completed' , created_at : '2026-09-13T00:00:00Z' ,
delivery : { state : 'known' , result : { message_id : 'result-message-2' } } ,
} ;
let deliveryState : 'failed' | 'queued' | 'sent' = 'failed' ;
let deliveryCalls = 0 ;
api . call . mockImplementation ( async ( operation : string ) = > {
if ( operation === 'channelBindings' ) return { items : [ binding ] , available_accounts : [ ] } ;
if ( operation === 'channelCallers' ) return { items : [ ] } ;
if ( operation === 'channelActivity' ) return { items : [ activity ] , next_offset : null } ;
if ( operation === 'channelDelivery' ) {
deliveryCalls += 1 ;
const status = deliveryCalls === 1 ? 'failed' : deliveryState ;
return { message_id : 'result-message-2' , status , parts : [ { part_id : 'part-2' , type : 'file' , status } ] } ;
}
if ( operation === 'retryChannelDeliveryPart' ) {
deliveryState = 'queued' ;
return { message_id : 'result-message-2' , status : 'queued' , parts : [ ] } ;
}
throw new Error ( ` Unexpected operation ${ operation } ` ) ;
} ) ;
render ( < CloudChannelPanel slug = { slug } publishedVersion = { 2 } / > ) ;
fireEvent . click ( await screen . findByRole ( 'button' , { name : '活动记录' } ) ) ;
const retry = await screen . findByRole ( 'button' , { name : '重新补发文件' } ) ;
expect ( screen . getByText ( '微信投递:发送失败' ) ) . toBeVisible ( ) ;
fireEvent . click ( retry ) ;
await waitFor ( ( ) = > expect ( api . call ) . toHaveBeenCalledWith ( 'retryChannelDeliveryPart' , {
slug , channel_account_id : 'wechat-1' , logical_message_id : 'result-message-2' , part_id : 'part-2' ,
} ) ) ;
await waitFor ( ( ) = > expect ( screen . queryByRole ( 'button' , { name : '重新补发文件' } ) ) . toBeNull ( ) ) ;
deliveryState = 'sent' ;
fireEvent . click ( screen . getByRole ( 'button' , { name : '刷新' , exact : true } ) ) ;
await waitFor ( ( ) = > expect ( deliveryCalls ) . toBeGreaterThanOrEqual ( 3 ) ) ;
await screen . findByText ( '微信投递:已送达' ) ;
expect ( screen . queryByRole ( 'button' , { name : '重新补发文件' } ) ) . toBeNull ( ) ;
} ) ;
it ( 'does not label a known execution receipt as a successful WeChat delivery' , async ( ) = > {
const base = api . call . getMockImplementation ( ) ! ;
api . call . mockImplementation ( async ( operation : string , input ) = > operation === 'channelActivity'
? { items : [ { run_id : 'run-unknown' , status : 'completed' , delivery : { state : 'known' , core : { state : 'unknown' } } } ] , next_offset : null }
: base ( operation , input ) ) ;
render ( < CloudChannelPanel slug = { slug } publishedVersion = { 2 } / > ) ;
fireEvent . click ( await screen . findByRole ( 'button' , { name : '活动记录' } ) ) ;
await screen . findByText ( '微信投递:状态未知' ) ;
expect ( screen . queryByText ( '微信投递:已送达' ) ) . toBeNull ( ) ;
} ) ;
it ( 'retains verification recovery while the corresponding channel binding is still loading' , async ( ) = > {
const pending = { id : 'waiting-bindings' , operation : 'wechatBindVerification' , created_at : '2026-09-13T00:00:00Z' ,
input : { slug , channel_account_id : 'wechat-1' , session_key : 'original-session' , operation_id : 'original-verify' } } ;
let bindingsReady ! : ( value : { items : typeof binding [ ] ; available_accounts : [ ] } ) = > void ;
const delayedBindings = new Promise ( resolve = > { bindingsReady = resolve ; } ) ;
const base = api . call . getMockImplementation ( ) ! ;
api . call . mockImplementation ( async ( operation : string , input ) = > operation === 'channelBindings' ? delayedBindings
: operation === 'wechatBindStatus' ? { session_key : 'original-session' , status : 'verification_required' } : base ( operation , input ) ) ;
api . recovery . mockResolvedValue ( { recent : null , pending : [ pending ] } ) ;
api . resolvePending . mockImplementation ( async ( _id , discard ) = > {
if ( discard ) { api . recovery . mockResolvedValue ( { recent : null , pending : [ ] } ) ; return { discarded : true } ; }
return { requires_input : true , operation : pending.operation , input : pending.input } ;
} ) ;
render ( < CloudChannelPanel slug = { slug } publishedVersion = { 2 } / > ) ;
fireEvent . click ( await screen . findByRole ( 'button' , { name : '恢复上次操作' } ) ) ;
await screen . findByText ( /尚未读取到该操作的微信连接/ ) ;
expect ( screen . queryByText ( /结果尚未确认/ ) ) . toBeNull ( ) ;
expect ( api . resolvePending ) . not . toHaveBeenCalled ( ) ;
await act ( async ( ) = > { bindingsReady ( { items : [ binding ] , available_accounts : [ ] } ) ; } ) ;
fireEvent . click ( screen . getByRole ( 'button' , { name : '恢复上次操作' } ) ) ;
await screen . findByLabelText ( '微信验证码' ) ;
expect ( api . resolvePending ) . toHaveBeenCalledWith ( 'waiting-bindings' , false ) ;
expect ( api . resolvePending ) . toHaveBeenCalledWith ( 'waiting-bindings' , true ) ;
} ) ;
it ( 'refreshes the recovery gate after a temporary local journal read failure' , async ( ) = > {
api . recovery . mockRejectedValueOnce ( new Error ( '本机记录暂时无法读取' ) ) . mockResolvedValue ( { recent : null , pending : [ ] } ) ;
const base = api . call . getMockImplementation ( ) ! ;
api . call . mockImplementation ( async ( operation : string , input ) = > operation === 'wechatBindStart' || operation === 'wechatBindStatus'
? { session_key : 'retry-read' , status : 'pending' , qrcode_url : 'https://wechat.example.test/login' } : base ( operation , input ) ) ;
render ( < CloudChannelPanel slug = { slug } publishedVersion = { 2 } / > ) ;
await screen . findByText ( '本机记录暂时无法读取' ) ;
fireEvent . click ( screen . getByRole ( 'button' , { name : '重试' , exact : true } ) ) ;
await waitFor ( ( ) = > expect ( api . recovery ) . toHaveBeenCalledTimes ( 2 ) ) ;
await waitFor ( ( ) = > expect ( screen . queryByText ( '本机记录暂时无法读取' ) ) . toBeNull ( ) ) ;
fireEvent . click ( await screen . findByRole ( 'button' , { name : '重新连接' , exact : true } ) ) ;
await screen . findByRole ( 'img' , { name : '个人微信登录二维码' } ) ;
} ) ;