import { beforeEach, describe, expect, it, vi } from 'vitest'; const { requestJsonMock } = vi.hoisted(() => ({ requestJsonMock: vi.fn(), })); import { clearRpgProfileBrowseHistory, listRpgProfileBrowseHistory, listRpgProfileSaveArchives, resumeRpgProfileSaveArchive, syncRpgProfileBrowseHistory, upsertRpgProfileBrowseHistory, } from './rpgProfileClient'; vi.mock('../apiClient', () => ({ requestJson: requestJsonMock, })); describe('rpgProfileClient browse history routes', () => { beforeEach(() => { requestJsonMock.mockReset(); requestJsonMock.mockResolvedValue({ entries: [] }); }); it('reads browse history from the runtime profile route', async () => { await listRpgProfileBrowseHistory(); expect(requestJsonMock).toHaveBeenCalledWith( '/api/runtime/profile/browse-history', expect.objectContaining({ method: 'GET' }), '读取浏览历史失败', expect.objectContaining({ retry: expect.objectContaining({ maxRetries: 1 }), }), ); }); it('writes browse history through the runtime profile route', async () => { await upsertRpgProfileBrowseHistory({ ownerUserId: 'user-1', profileId: 'profile-1', worldName: '测试世界', subtitle: '测试副标题', summaryText: '测试摘要', coverImageSrc: null, themeMode: 'mythic', authorDisplayName: '测试作者', }); expect(requestJsonMock).toHaveBeenCalledWith( '/api/runtime/profile/browse-history', expect.objectContaining({ method: 'POST', headers: { 'Content-Type': 'application/json' }, }), '写入浏览历史失败', expect.objectContaining({ retry: expect.objectContaining({ maxRetries: 1, retryUnsafeMethods: true, }), }), ); }); it('syncs browse history through the runtime profile route', async () => { await syncRpgProfileBrowseHistory([ { ownerUserId: 'user-1', profileId: 'profile-1', worldName: '测试世界', subtitle: '测试副标题', summaryText: '测试摘要', coverImageSrc: null, themeMode: 'mythic', authorDisplayName: '测试作者', }, ]); expect(requestJsonMock).toHaveBeenCalledWith( '/api/runtime/profile/browse-history', expect.objectContaining({ method: 'POST', headers: { 'Content-Type': 'application/json' }, }), '同步浏览历史失败', expect.any(Object), ); }); it('clears browse history through the runtime profile route', async () => { await clearRpgProfileBrowseHistory(); expect(requestJsonMock).toHaveBeenCalledWith( '/api/runtime/profile/browse-history', expect.objectContaining({ method: 'DELETE' }), '清空浏览历史失败', expect.objectContaining({ retry: expect.objectContaining({ maxRetries: 1, retryUnsafeMethods: true, }), }), ); }); }); describe('rpgProfileClient save archive routes', () => { beforeEach(() => { requestJsonMock.mockReset(); requestJsonMock.mockResolvedValue({ entries: [] }); }); it('reads save archives from the runtime profile route', async () => { await listRpgProfileSaveArchives(); expect(requestJsonMock).toHaveBeenCalledWith( '/api/runtime/profile/save-archives', expect.objectContaining({ method: 'GET' }), '读取存档列表失败', expect.objectContaining({ retry: expect.objectContaining({ maxRetries: 1 }), }), ); }); it('resumes a save archive through the runtime profile route', async () => { requestJsonMock.mockResolvedValueOnce({ entry: { worldKey: 'custom:world-1', }, snapshot: { version: 2, savedAt: '2026-04-19T10:15:00.000Z', bottomTab: 'adventure', currentStory: null, gameState: { worldType: 'CUSTOM', }, }, }); await resumeRpgProfileSaveArchive('custom:world-1'); expect(requestJsonMock).toHaveBeenCalledWith( '/api/runtime/profile/save-archives/custom%3Aworld-1', expect.objectContaining({ method: 'POST' }), '恢复存档失败', expect.objectContaining({ retry: expect.objectContaining({ maxRetries: 1, retryUnsafeMethods: true, }), }), ); }); });