Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 21 additions & 9 deletions apps/desktop/src/main/skillhub/registry/__tests__/lock.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,31 @@ describe('withLock', () => {
expect(counter).toBe(100);
});

it('不同 key 并行,耗时近似单次而非 N 倍', async () => {
const delay = 20; // ms
const start = Date.now();
it('不同 key 并行:三个临界区在某一刻同时开着(而非首尾相接)', async () => {
// **不断言墙钟耗时**。原来是 `elapsed < delay * 2.5`(20ms × 2.5 = 50ms),Windows CI
// 两分片并跑时 52ms 就把 main 打红了 —— 三个 20ms 的 sleep 串行才 60ms,阈值离"串行"
// 只差 10ms,等于拿调度抖动当被测行为。
//
// 改成直接量"并行"本身:记下每个临界区的进入 / 离开时刻,断言
// `max(进入) < min(离开)`,即存在一个瞬间三者同时在临界区内。串行执行下
// 第二个的进入必然晚于第一个的离开,这条一定不成立。与机器快慢无关。
const enter: number[] = [];
const leave: number[] = [];
const section = async (): Promise<void> => {
enter.push(Date.now());
await new Promise<void>((r) => setTimeout(r, 20));
leave.push(Date.now());
};

await Promise.all([
withLock('key-a', () => new Promise<void>((r) => setTimeout(r, delay))),
withLock('key-b', () => new Promise<void>((r) => setTimeout(r, delay))),
withLock('key-c', () => new Promise<void>((r) => setTimeout(r, delay))),
withLock('key-a', section),
withLock('key-b', section),
withLock('key-c', section),
]);

const elapsed = Date.now() - start;
// 并行时应该约 delay ms 完成(而非 3*delay)
expect(elapsed).toBeLessThan(delay * 2.5);
expect(enter).toHaveLength(3);
expect(leave).toHaveLength(3);
expect(Math.max(...enter)).toBeLessThan(Math.min(...leave));
});

it('前一个 task 失败,后续 task 仍可执行', async () => {
Expand Down
11 changes: 8 additions & 3 deletions packages/device-link/src/__tests__/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3139,7 +3139,10 @@ describe('DeviceLinkClient', () => {
client.start();
await tick(5);
expect(sockets.length).toBe(0); // 第一轮卡在 getToken,没建 socket
await tick(30); // 10ms 超时 + ≤5ms 退避后第二轮拿到 token
// 负载下(Windows CI 分片并跑)事件循环调度可能远超名义毫秒数:单次固定 tick(30) 不足以
// 保证 10ms getToken 超时 + ≤5ms 退避 + 第二轮 getToken 都已落地。有界等待到 socket
// 出现,断言语义不变(挂死的第一轮必须被超时掀掉、第二轮必须真的建出连接)。
for (let i = 0; i < 40 && sockets.length < 1; i++) await tick(10);
expect(sockets.length).toBe(1);
sockets[0].ack();
expect(client.getStatus()).toBe('online');
Expand Down Expand Up @@ -3223,8 +3226,10 @@ describe('DeviceLinkClient', () => {
await tick();
const first = h.current();
first.emit('open'); // upgrade 成功但对端不回 hello-ack(半开/服务假活)
await tick(50);
// watchdog 触发新建连接(测试窗口内后续连接可能再次超时,只断言 ≥2)
// 负载下(Windows CI 分片并跑)事件循环调度可能远超名义毫秒数:单次固定 tick(50)
// 不足以保证 15ms 握手看门狗 + 退避重连都已落地。有界等待到第二个 socket 出现,
// 断言语义不变(watchdog 必须触发新建连接;测试窗口内后续连接可能再次超时,只断言 ≥2)。
for (let i = 0; i < 40 && h.sockets.length < 2; i++) await tick(10);
expect(h.sockets.length).toBeGreaterThanOrEqual(2);
expect(first.terminated || first.closed !== null).toBe(true); // 旧 socket 被回收
// 负载下(全量并跑)事件循环调度可能远超名义毫秒数:current() 拿到的
Expand Down
13 changes: 12 additions & 1 deletion packages/maker-core/src/contacts/__tests__/manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,20 @@
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import DatabaseCtor from 'better-sqlite3';

// 本文件每个用例都在 os.tmpdir() 里真开 better-sqlite3 落库(建目录 → 建表 → v1→v2 迁移
// → FTS5 重建 → 删目录)。Windows CI 上两个分片并跑,叠上 Defender 对新建文件的实时扫描,
// 这些**同步** IO 会超过 vitest 默认的 5s —— 于是这个纯正确性用例以「Test timed out in
// 5000ms」把 main 打红(2026-08-04 实测:manager.test.ts 的 v1→v2 迁移用例;此前值班日志
// 也记过同一文件 4 个用例同时 5s 超时)。
// packages/maker-core 没有自己的 vitest 配置,拿不到 apps/desktop 那份
// `testTimeout: win32 ? 20_000 : 5_000`,所以在这里按仓内既有写法(git-integration 系列
// 用例的 vi.setConfig)单独放宽。**只放宽时间,不放宽任何断言** —— 它测的是迁移正确性,
// 从来不是"迁移够快"。
vi.setConfig({ testTimeout: process.platform === 'win32' ? 30_000 : 5_000 });
Comment thread
MagicLizi marked this conversation as resolved.

import { MakerContactsManager } from '../manager.js';
import type { Logger } from '../../interfaces/logger.js';

Expand Down