9f225b7a41
新增统一事件合同、非阻塞明文队列、批次封存及保留清理 接入会话前台时长、项目创建打开、创作提交和双智能体运行结果 按策划阶段推进及已接入成果路径记录变化、预览就绪与保存 补齐身份隔离、异常会话和同一宿主创作链路验证 同步团队方案与验收证据,本期不上传、不加密、不扩展资源操作采集
92 lines
2.9 KiB
TypeScript
92 lines
2.9 KiB
TypeScript
// @vitest-environment jsdom
|
|
import { afterEach, expect, it, vi } from 'vitest';
|
|
|
|
import type { TauriInvoke } from '../src/app/types';
|
|
import { beginDirectRunAnalytics } from '../src/services/clientAnalytics';
|
|
|
|
afterEach(() => vi.restoreAllMocks());
|
|
|
|
it('自动重试保持业务回合,只确认最后一次尝试且确认不等待桥接', async () => {
|
|
const first = '11111111-1111-4111-8111-111111111111';
|
|
const last = '22222222-2222-4222-8222-222222222222';
|
|
vi.spyOn(crypto, 'randomUUID')
|
|
.mockReturnValueOnce(first)
|
|
.mockReturnValueOnce(last);
|
|
const invoke = vi.fn(() => new Promise(() => {}));
|
|
const analytics = beginDirectRunAnalytics(invoke as TauriInvoke, () => 1);
|
|
const attempts: string[] = [];
|
|
const operation = async () => {
|
|
attempts.push(analytics.nextAttempt()!);
|
|
if (attempts.length === 1) throw new Error('authentication-required');
|
|
return '完成';
|
|
};
|
|
let result: string;
|
|
try {
|
|
result = await operation().catch(() => operation());
|
|
expect(invoke).not.toHaveBeenCalled();
|
|
} finally {
|
|
analytics.settle();
|
|
}
|
|
expect(result).toBe('完成');
|
|
expect(attempts).toEqual([first, last]);
|
|
expect(invoke).toHaveBeenCalledTimes(1);
|
|
expect(invoke).toHaveBeenCalledWith('settle_direct_run_analytics', {
|
|
attemptId: last,
|
|
discard: false,
|
|
});
|
|
analytics.settle();
|
|
expect(invoke).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('最终 UUID 失败不能回退确认第一次失败', () => {
|
|
vi.spyOn(crypto, 'randomUUID')
|
|
.mockReturnValueOnce('11111111-1111-4111-8111-111111111111')
|
|
.mockImplementationOnce(() => {
|
|
throw new Error('UUID unavailable');
|
|
});
|
|
const invoke = vi.fn();
|
|
const analytics = beginDirectRunAnalytics(invoke as TauriInvoke, () => 1);
|
|
expect(analytics.nextAttempt()).toBeDefined();
|
|
expect(analytics.nextAttempt()).toBeUndefined();
|
|
analytics.settle();
|
|
expect(invoke).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('账号代次变化只丢弃候选,不提交成功失败结论', () => {
|
|
let generation = 1;
|
|
const invoke = vi.fn(async () => undefined);
|
|
const analytics = beginDirectRunAnalytics(
|
|
invoke as TauriInvoke,
|
|
() => generation,
|
|
);
|
|
const attemptId = analytics.nextAttempt();
|
|
generation = 2;
|
|
analytics.settle();
|
|
expect(invoke).toHaveBeenCalledWith('settle_direct_run_analytics', {
|
|
attemptId,
|
|
discard: true,
|
|
});
|
|
});
|
|
|
|
it.each(['sync', 'async'])(
|
|
'确认桥接 %s 失败不会覆盖最终业务错误',
|
|
async (mode) => {
|
|
const invoke = vi.fn(() => {
|
|
if (mode === 'sync') throw new Error('bridge');
|
|
return Promise.reject(new Error('bridge'));
|
|
});
|
|
const analytics = beginDirectRunAnalytics(invoke as TauriInvoke, () => 1);
|
|
const failure = new Error('最终执行失败');
|
|
const operation = async () => {
|
|
try {
|
|
analytics.nextAttempt();
|
|
throw failure;
|
|
} finally {
|
|
analytics.settle();
|
|
}
|
|
};
|
|
await expect(operation()).rejects.toBe(failure);
|
|
expect(invoke).toHaveBeenCalledTimes(1);
|
|
},
|
|
);
|