update
This commit is contained in:
89
tests/unit/main/utils/error-payload-str.test.ts
Normal file
89
tests/unit/main/utils/error-payload-str.test.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
// tests/unit/main/utils/error-payload-str.test.ts
|
||||
//
|
||||
// 守 errPayloadStr 的单行日志契约(2026-10-20,r201):
|
||||
//
|
||||
// unknown → string,永不 undefined、永不 throw。
|
||||
//
|
||||
// 它是 errPayload 的"字符串版",直接喂 log.error / IPC 错误消息。关键点:
|
||||
// 1. Error 有 stack → "message\nstack";无 stack → 只有 message。
|
||||
// 2. cause 链拼成 "…\nCaused by: …",让根因在日志里**可见**
|
||||
// (AppError / ValidationError 都用 { cause } 把根因传下去)。
|
||||
// 3. cause 为 null / undefined → 不拼,避免 "Caused by: null" 噪音。
|
||||
// 4. cause 环引用 → 截断成 '[Circular cause]',**不**无限递归。
|
||||
// 5. 永不返回 undefined —— toast / dialog 渲染要稳定。
|
||||
//
|
||||
// 同族:errnoCode → tests/unit/main/utils/errno-code.test.ts
|
||||
// error.ts 全量 → tests/unit/utils/error.test.ts
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { errPayloadStr } from '../../../../src/main/utils/error';
|
||||
|
||||
describe('errPayloadStr — 单行日志串(r201)', () => {
|
||||
it('Error 带 stack → message + "\\n" + stack', () => {
|
||||
const e = new Error('boom');
|
||||
const s = errPayloadStr(e);
|
||||
expect(s).toBe(`boom\n${e.stack}`);
|
||||
expect(s.startsWith('boom\n')).toBe(true);
|
||||
});
|
||||
|
||||
it('Error 无 stack → 只有 message', () => {
|
||||
const e = new Error('boom');
|
||||
Object.defineProperty(e, 'stack', { value: undefined, configurable: true });
|
||||
expect(errPayloadStr(e)).toBe('boom');
|
||||
});
|
||||
|
||||
it('字符串原样返回', () => {
|
||||
expect(errPayloadStr('plain')).toBe('plain');
|
||||
});
|
||||
|
||||
it('null / undefined → 字面量,永不 undefined', () => {
|
||||
expect(errPayloadStr(null)).toBe('null');
|
||||
expect(errPayloadStr(undefined)).toBe('undefined');
|
||||
expect(typeof errPayloadStr(new Error('x'))).toBe('string');
|
||||
});
|
||||
|
||||
it('普通对象 → [object Object]', () => {
|
||||
expect(errPayloadStr({})).toBe('[object Object]');
|
||||
});
|
||||
|
||||
it('null-proto 对象不炸(降级链兜住)', () => {
|
||||
expect(() => errPayloadStr(Object.create(null))).not.toThrow();
|
||||
expect(errPayloadStr(Object.create(null))).toBe('[object Object]');
|
||||
});
|
||||
|
||||
it('cause 链被拼成 "Caused by:" 序列', () => {
|
||||
const a = new Error('A');
|
||||
const b = new Error('B', { cause: a });
|
||||
const c = new Error('C', { cause: b });
|
||||
const s = errPayloadStr(c);
|
||||
expect(s.startsWith('C\n')).toBe(true);
|
||||
expect(s).toContain('Caused by: B\n');
|
||||
expect(s).toContain('Caused by: A\n');
|
||||
});
|
||||
|
||||
it('cause === null → 不拼 "Caused by: null"', () => {
|
||||
const e = new Error('x', { cause: null });
|
||||
expect(errPayloadStr(e)).not.toContain('Caused by');
|
||||
});
|
||||
|
||||
it('cause 环引用 → 截断为 [Circular cause],不栈溢出', () => {
|
||||
const e = new Error('loop') as Error & { cause?: unknown };
|
||||
e.cause = e;
|
||||
let s = '';
|
||||
expect(() => {
|
||||
s = errPayloadStr(e);
|
||||
}).not.toThrow();
|
||||
expect(s).toContain('[Circular cause]');
|
||||
});
|
||||
|
||||
it('互相引用的两节点环 → 同样终止', () => {
|
||||
const a = new Error('A') as Error & { cause?: unknown };
|
||||
const b = new Error('B', { cause: a });
|
||||
a.cause = b;
|
||||
let s = '';
|
||||
expect(() => {
|
||||
s = errPayloadStr(a);
|
||||
}).not.toThrow();
|
||||
expect(s).toContain('[Circular cause]');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user