/** * syncByVersion 游标同步单测(批次二③) * * 锁定基于版本号的精确增量恢复不变量: * 1. 成功同步并推进持久化游标 * 2. full_sync=true 时退化(返回 false) * 3. 异常时退化(返回 false) * 4. 无 version(<=0)时直接退化 * * 对标 Matrix resume 游标:断线恢复从全量轮询升级为精确增量。 */ import { useMessageStore } from '../store'; import { MessageSyncService } from '../services/MessageSyncService'; import { messageService } from '@/services/message'; import { messageRepository } from '@/database'; function createSyncService(): MessageSyncService { return new MessageSyncService( () => 'me', { markAsRead: async () => {}, markAllAsRead: async () => {} } as any, { getSenderInfo: async () => null, enrichMessagesWithSenderInfo: () => {} } as any, undefined ); } describe('syncByVersion 游标同步', () => { beforeEach(() => { useMessageStore.getState().reset(); (messageRepository as any).__reset(); (messageService as any).__reset(); }); it('无变更时返回成功且推进游标', async () => { const serviceMock = messageService as any; serviceMock.__setSyncByVersionHandler((version: number) => ({ current_version: version + 10, changes: [], full_sync: false, has_more: false, })); const sync = createSyncService(); const ok = await sync.syncByVersion(100); expect(ok).toBe(true); // 游标推进到 current_version expect(useMessageStore.getState().syncVersion).toBe(110); }); it('full_sync=true 时退化为 false', async () => { const serviceMock = messageService as any; serviceMock.__setSyncByVersionHandler(() => ({ current_version: 0, changes: [], full_sync: true, has_more: false, })); const sync = createSyncService(); const ok = await sync.syncByVersion(100); expect(ok).toBe(false); }); it('API 抛异常时退化为 false', async () => { const serviceMock = messageService as any; serviceMock.__setSyncByVersionHandler(() => { throw new Error('网络故障'); }); const sync = createSyncService(); const ok = await sync.syncByVersion(100); expect(ok).toBe(false); }); it('version <= 0 时直接退化', async () => { const sync = createSyncService(); const ok1 = await sync.syncByVersion(0); const ok2 = await sync.syncByVersion(-1); expect(ok1).toBe(false); expect(ok2).toBe(false); }); it('有会话变更时按需同步会话详情', async () => { const serviceMock = messageService as any; // 先在 store 放一个会话,让 fetchConversationDetail 有上下文 useMessageStore.getState().setConversationsWithUnread( new Map([['c1', { id: 'c1', last_seq: 5, unread_count: 0 } as any]]), 0, 0 ); let detailCalled = false; serviceMock.__setSyncByVersionHandler((version: number) => ({ current_version: version + 5, changes: [ { conversation_id: 'c1', change_type: 'new_message', max_seq: 10, version: version + 1 }, ], full_sync: false, has_more: false, })); // fetchConversationDetail 内部调 getConversationById serviceMock.getConversationById = async () => { detailCalled = true; return { id: 'c1', last_seq: 10 } as any; }; serviceMock.getMessages = async () => ({ messages: [], total: 0, page: 1, page_size: 20 }); const sync = createSyncService(); const ok = await sync.syncByVersion(100); expect(ok).toBe(true); expect(detailCalled).toBe(true); expect(useMessageStore.getState().syncVersion).toBe(105); }); });