AGC认证载体序列化用例对齐
- 载体用例断言 message 为载荷序列化;Tauri 缺陷抛真 Error 时是 {},但上报仍优先用其自身 message/stack
- 上报用例的 message 断言改为序列化结果,并新增真 Error 优先分支用例
This commit is contained in:
@@ -144,7 +144,10 @@ describe('认证失败的上报判据', () => {
|
||||
await waitFor(() => expect(reportCalls()).toHaveLength(1));
|
||||
expect(reportCalls()[0]?.[1]).toMatchObject({
|
||||
source: 'unhandledrejection',
|
||||
message: '未知客户端错误',
|
||||
message: JSON.stringify({
|
||||
type: 'authNetworkFailure',
|
||||
reason: 'unreachable',
|
||||
}),
|
||||
});
|
||||
} finally {
|
||||
uninstall();
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
* 认证命令失败的 JS 侧载体。
|
||||
*
|
||||
* `invokeClientAuth` 是薄包装:Rust 的结构化拒绝原样装进 `ClientAuthErrorWrapper.error`,不读变体
|
||||
* 字段(`serverMessage` 之类不保证每个变体都有)、不注入上下文、载体自身 `message` 留空;分流只按
|
||||
* `error.error.type`。另外用编译期用例钉住变体全集:无字段变体直接取固定文案,带载荷变体先
|
||||
* 字段(`serverMessage` 之类不保证每个变体都有)、不注入上下文,载体构造时把整份载荷序列化进
|
||||
* `Error.message`;分流只按 `error.error.type`。另外用编译期用例钉住变体全集:无字段变体直接取固定文案,带载荷变体先
|
||||
* `as` 取具名类型,可枚举的细分再按类型化 reason 分流;漏列任何变体或 reason,
|
||||
* `expectNever(...)` 都编译不过。
|
||||
*/
|
||||
@@ -147,7 +147,7 @@ describe('认证命令失败的 JS 侧载体', () => {
|
||||
|
||||
expect(failure).toBeInstanceOf(ClientAuthErrorWrapper);
|
||||
expect((failure as ClientAuthErrorWrapper).error).toBe(rejection);
|
||||
expect((failure as Error).message).toBe('');
|
||||
expect((failure as Error).message).toBe(JSON.stringify(rejection));
|
||||
});
|
||||
|
||||
it('认证桥未安装时保持原样抛出,不包装成命令失败', async () => {
|
||||
@@ -175,7 +175,8 @@ describe('认证命令失败的 JS 侧载体', () => {
|
||||
).catch((error: unknown) => error);
|
||||
|
||||
expect(failure).toBeInstanceOf(ClientAuthErrorWrapper);
|
||||
expect((failure as Error).message).toBe('');
|
||||
// Error 没有可枚举属性,序列化就是空对象;上报链路对真 Error 优先用其自身 message/stack。
|
||||
expect((failure as Error).message).toBe('{}');
|
||||
expect((failure as ClientAuthErrorWrapper).error).toBe(rejection);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -84,6 +84,7 @@ import {
|
||||
submitErrorReportBatch,
|
||||
subscribeClientErrorEvents,
|
||||
} from '../src/services/errorReporting';
|
||||
import type { ClientAuthError } from '../src/services/generated/ClientAuthError';
|
||||
|
||||
describe('客户端错误报告池', () => {
|
||||
afterEach(() => {
|
||||
@@ -209,7 +210,7 @@ describe('客户端错误报告池', () => {
|
||||
expect(await getPendingClientErrorEvents()).toEqual([event]);
|
||||
});
|
||||
|
||||
it('解包 ClientAuthErrorWrapper 里的原始拒绝值,上下文只认显式入参', async () => {
|
||||
it('解包 ClientAuthErrorWrapper,把载体序列化后的 message 原样上报,上下文只认显式入参', async () => {
|
||||
const original = { type: 'authNetworkFailure', reason: 'unreachable' };
|
||||
|
||||
await captureClientError(new ClientAuthErrorWrapper(original), {
|
||||
@@ -219,13 +220,31 @@ describe('客户端错误报告池', () => {
|
||||
|
||||
expect(invoke).toHaveBeenCalledWith('report_client_error', {
|
||||
source: 'unhandledrejection',
|
||||
message: '未知客户端错误',
|
||||
message: JSON.stringify(original),
|
||||
stack: expect.any(String),
|
||||
action: 'login',
|
||||
page: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('包装的真 Error 仍优先用它的 message/stack,而不是空对象序列化', async () => {
|
||||
// Tauri 在映射外抛真 Error 属于它的缺陷:载体照装,但取文案时仍用这颗 Error 自己。
|
||||
const original = new Error('IPC 桥接异常');
|
||||
|
||||
await captureClientError(
|
||||
new ClientAuthErrorWrapper(original as unknown as ClientAuthError),
|
||||
{ source: 'unhandledrejection' },
|
||||
);
|
||||
|
||||
expect(invoke).toHaveBeenCalledWith('report_client_error', {
|
||||
source: 'unhandledrejection',
|
||||
message: 'IPC 桥接异常',
|
||||
stack: original.stack,
|
||||
action: undefined,
|
||||
page: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('保留 API 路由但隐藏 URL origin 与查询参数', () => {
|
||||
const sanitized = normalizeDiagnosticText(
|
||||
'请求超时:https://dev.genarrative.world/api/llm/models?token=secret#fragment',
|
||||
|
||||
Reference in New Issue
Block a user