734ae175b0
Rust app_log 输出同时保留 stderr 并写入 AppData application.log。 将 WebView console 输出镜像到同一普通文本日志并保留滚动备份。 移除结构化错误事件写盘路径,事件仅在内存中合并并于提交时生成 events.jsonl。 清理旧 Tauri command、共享类型和同步更新诊断上传文档。
97 lines
3.0 KiB
TypeScript
97 lines
3.0 KiB
TypeScript
/** @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();
|
|
});
|
|
});
|