import { beforeEach, describe, expect, it, vi } from 'vitest'; const { requestJsonMock } = vi.hoisted(() => ({ requestJsonMock: vi.fn(), })); import { clearRpgProfileBrowseHistory, listRpgProfileBrowseHistory, listRpgProfileSaveArchives, resumeRpgProfileSaveArchive, submitRpgProfileFeedback, syncRpgProfileBrowseHistory, upsertRpgProfileBrowseHistory, } from './rpgProfileClient'; vi.mock('../apiClient', () => ({ BACKGROUND_AUTH_REQUEST_OPTIONS: { authImpact: 'local', skipRefresh: true, notifyAuthStateChange: false, clearAuthOnUnauthorized: false, }, requestJson: requestJsonMock, })); describe('rpgProfileClient browse history routes', () => { beforeEach(() => { requestJsonMock.mockReset(); requestJsonMock.mockResolvedValue({ entries: [] }); }); it('reads browse history from the profile route', async () => { await listRpgProfileBrowseHistory(); expect(requestJsonMock).toHaveBeenCalledWith( '/api/profile/browse-history', expect.objectContaining({ method: 'GET' }), '读取浏览历史失败', expect.objectContaining({ authImpact: 'local', retry: expect.objectContaining({ maxRetries: 1 }), skipRefresh: true, notifyAuthStateChange: false, clearAuthOnUnauthorized: false, }), ); }); it('writes browse history through the profile route', async () => { await upsertRpgProfileBrowseHistory({ ownerUserId: 'user-1', profileId: 'profile-1', worldName: '测试世界', subtitle: '测试副标题', summaryText: '测试摘要', coverImageSrc: null, themeMode: 'mythic', authorDisplayName: '测试作者', }); expect(requestJsonMock).toHaveBeenCalledWith( '/api/profile/browse-history', expect.objectContaining({ method: 'POST', headers: { 'Content-Type': 'application/json' }, }), '写入浏览历史失败', expect.objectContaining({ authImpact: 'local', retry: expect.objectContaining({ maxRetries: 1, retryUnsafeMethods: true, }), skipRefresh: true, notifyAuthStateChange: false, clearAuthOnUnauthorized: false, }), ); }); it('syncs browse history through the profile route', async () => { await syncRpgProfileBrowseHistory([ { ownerUserId: 'user-1', profileId: 'profile-1', worldName: '测试世界', subtitle: '测试副标题', summaryText: '测试摘要', coverImageSrc: null, themeMode: 'mythic', authorDisplayName: '测试作者', }, ]); expect(requestJsonMock).toHaveBeenCalledWith( '/api/profile/browse-history', expect.objectContaining({ method: 'POST', headers: { 'Content-Type': 'application/json' }, }), '同步浏览历史失败', expect.any(Object), ); }); it('clears browse history through the profile route', async () => { await clearRpgProfileBrowseHistory(); expect(requestJsonMock).toHaveBeenCalledWith( '/api/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 profile route', async () => { await listRpgProfileSaveArchives(); expect(requestJsonMock).toHaveBeenCalledWith( '/api/profile/save-archives', expect.objectContaining({ method: 'GET' }), '读取存档列表失败', expect.objectContaining({ authImpact: 'local', retry: expect.objectContaining({ maxRetries: 1 }), skipRefresh: true, notifyAuthStateChange: false, clearAuthOnUnauthorized: false, }), ); }); it('resumes a save archive through the 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/profile/save-archives/custom%3Aworld-1', expect.objectContaining({ method: 'POST' }), '恢复存档失败', expect.objectContaining({ retry: expect.objectContaining({ maxRetries: 1, retryUnsafeMethods: true, }), }), ); }); }); describe('rpgProfileClient feedback routes', () => { beforeEach(() => { requestJsonMock.mockReset(); requestJsonMock.mockResolvedValue({ feedback: { feedbackId: 'feedback:user-1:1', status: 'open', createdAt: '2026-05-08T10:00:00Z', evidenceItems: [], }, }); }); it('submits profile feedback through the profile route', async () => { await submitRpgProfileFeedback({ description: '图片上传后没有展示预览', contactPhone: null, evidenceItems: [ { fileName: 'preview.png', contentType: 'image/png', sizeBytes: 128, dataUrl: 'data:image/png;base64,ZmVlZGJhY2s=', }, ], }); expect(requestJsonMock).toHaveBeenCalledWith( '/api/profile/feedback', expect.objectContaining({ method: 'POST', headers: { 'Content-Type': 'application/json' }, }), '提交反馈失败', expect.objectContaining({ retry: expect.objectContaining({ maxRetries: 1, retryUnsafeMethods: true, }), }), ); expect(JSON.parse(requestJsonMock.mock.calls[0][1].body)).toEqual({ description: '图片上传后没有展示预览', contactPhone: null, evidenceItems: [ { fileName: 'preview.png', contentType: 'image/png', sizeBytes: 128, dataUrl: 'data:image/png;base64,ZmVlZGJhY2s=', }, ], }); }); });