/** @vitest-environment jsdom */ import { afterEach, describe, expect, it, vi } from 'vitest'; vi.mock('@tauri-apps/api/core', () => ({ invoke: vi.fn().mockResolvedValue(undefined), })); vi.mock('../src/services/clientAuth', () => ({ getStoredAuthAccessToken: vi.fn(() => 'test-token'), })); vi.mock('../src/services/clientHttp', () => ({ fetchClientHttp: vi.fn(), getClientServerBaseUrl: vi.fn(() => 'https://example.test'), })); import { invoke } from '@tauri-apps/api/core'; import { fetchClientHttp } from '../src/services/clientHttp'; import { captureClientError, getPendingClientErrorEvents, installWebviewLogBridge, markClientErrorEventsSubmitted, resetClientErrorEventsForTests, submitErrorReportBatch, } from '../src/services/errorReporting'; describe('客户端错误报告池', () => { afterEach(() => { resetClientErrorEventsForTests(); vi.clearAllMocks(); }); it('按 fingerprint 合并重复错误并累计次数', async () => { const first = await captureClientError(new Error('重复错误'), { source: 'test', }); const second = await captureClientError(new Error('重复错误'), { source: 'test', }); expect(second.eventId).toBe(first.eventId); expect(getPendingClientErrorEvents()).toHaveLength(1); expect(getPendingClientErrorEvents()[0]?.count).toBe(2); }); it('限制当前进程错误池最多保留 100 条', async () => { for (let index = 0; index < 101; index += 1) { await captureClientError(new Error(`错误 ${index}`), { source: 'test' }); } expect(getPendingClientErrorEvents()).toHaveLength(100); }); it('提交成功后只清除本次提交的事件', async () => { const first = await captureClientError(new Error('第一个错误'), { source: 'test', }); const second = await captureClientError(new Error('第二个错误'), { source: 'test', }); vi.mocked(fetchClientHttp).mockResolvedValue( new Response(JSON.stringify({ data: { batchId: 'batch-1' } }), { status: 200, }), ); await submitErrorReportBatch({ events: [first], logs: [] }); markClientErrorEventsSubmitted([first]); expect(getPendingClientErrorEvents()).toEqual([second]); }); it('未登录时拒绝提交且不发请求', async () => { const auth = await import('../src/services/clientAuth'); vi.mocked(auth.getStoredAuthAccessToken).mockReturnValue(''); await expect( submitErrorReportBatch({ events: [], logs: [] }), ).rejects.toThrow('请先登录'); expect(fetchClientHttp).not.toHaveBeenCalled(); }); it('将 WebView console 输出写入普通文本日志 command', async () => { const consoleInfo = vi.spyOn(console, 'info').mockImplementation(() => {}); const uninstall = installWebviewLogBridge(); console.info('hello', { count: 1 }); await Promise.resolve(); expect(invoke).toHaveBeenCalledWith('append_application_log', { level: 'info', source: 'webview', message: 'hello {"count":1}', }); uninstall(); consoleInfo.mockRestore(); }); });