1
0

fix: 修复 Windows 平台测试兼容性问题

- 新增 tests/helpers.ts 的 rmRetry 工具函数,解决 SQLite 文件句柄未及时释放导致 afterAll 清理时 EBUSY 错误
- 修改通配符测试用例,使用 bun -e 替代 echo 命令,确保跨平台行为一致
This commit is contained in:
2026-05-12 22:11:34 +08:00
parent ad87be6956
commit 87d946a441
6 changed files with 47 additions and 7 deletions

View File

@@ -9,9 +9,9 @@ context: |
- 新增的逻辑必须编写完善的测试,并保证测试的正确性,不允许跳过任何测试
- 这是基于bun实现的前端后一体化项目使用bun作为唯一包管理器严禁使用pnpm、npm使用bunx运行工具严禁使用npx、pnpx
- src/server目录下是基于bun实现的后端代码
- src/web目录下是基于vite、react、TDesign实现的前端代码
- 后端库使用优先级Bun 内置 API > es-toolkit > 主流三方库 > 项目公共工具 > 自行实现
- 前端样式开发优先级TDesign组件 > 组件props > TDesign CSS tokens(--td-*) > styles.css CSS类 > 自行开发组件
- src/web目录下是基于vite、react、TDesign实现的前端代码
- 前端样式开发优先级TDesign组件 > 组件props > TDesign CSS tokens(--td-*) > styles.css CSS类 > 自行开发组件
- 前端严禁组件内联style属性、CSS覆盖TD内部类名、使用!important、硬编码色值
- Git提交: 仅中文; 格式"类型: 简短描述", 类型: feat/fix/refactor/docs/style/test/chore; 多行描述空行后写详细说明
- 禁止创建git操作task

View File

@@ -0,0 +1,25 @@
# Capability: windows-test-compat
## Purpose
确保测试在 Windows 平台上的兼容性,包括文件句柄释放后的目录清理重试机制和跨平台命令测试约定。
## Requirements
### Requirement: 测试临时目录清理 SHALL 支持重试
使用 SQLite 数据库的测试 SHALL 在 `afterAll` 中使用带重试的目录删除机制,确保在 Windows 上文件句柄未及时释放时不会导致测试失败。
#### Scenario: Windows 上 SQLite 文件句柄延迟释放
- **WHEN** 测试在 Windows 上运行,`store.close()` 后立即尝试删除临时目录
- **THEN** 删除操作 SHALL 自动重试(最多 3 次,间隔 200ms直到成功或耗尽重试次数
### Requirement: 命令检测器测试 SHALL 使用跨平台命令
命令检测器的测试 SHALL 使用 `bun -e` 脚本替代系统 `echo` 命令,确保测试断言在所有平台上行为一致。
#### Scenario: 验证非 shell 模式下特殊字符不被展开
- **WHEN** 通过 `Bun.spawn` 执行 `bun -e "console.log('*')"` 并检查 stdout 包含 `*`
- **THEN** 测试 SHALL 在 Windows 和 Linux 上均返回 `matched: true`

13
tests/helpers.ts Normal file
View File

@@ -0,0 +1,13 @@
import { rm } from "node:fs/promises";
export async function rmRetry(dir: string, retries = 10, delayMs = 500) {
for (let i = 0; i < retries; i++) {
try {
await rm(dir, { force: true, recursive: true });
return;
} catch (e) {
if (i === retries - 1) throw e;
await new Promise((r) => setTimeout(r, delayMs));
}
}
}

View File

@@ -1,5 +1,5 @@
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import { mkdir, rm } from "node:fs/promises";
import { mkdir } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
@@ -10,6 +10,7 @@ import { checkerRegistry } from "../../src/server/checker/runner";
import { CommandChecker } from "../../src/server/checker/runner/command/runner";
import { HttpChecker } from "../../src/server/checker/runner/http/runner";
import { ProbeStore } from "../../src/server/checker/store";
import { rmRetry } from "../helpers";
function ensureRegistered() {
if (!checkerRegistry.supportedTypes.includes("http")) {
@@ -100,7 +101,7 @@ describe("API 路由", () => {
afterAll(async () => {
store.close();
await rm(tempDir, { force: true, recursive: true });
await rmRetry(tempDir);
});
test("/health 返回健康检查", async () => {

View File

@@ -119,7 +119,7 @@ describe("CommandChecker", () => {
test("不使用 shell通配符不被展开", async () => {
const result = await checker.execute(
makeTarget({ args: ["*"], exec: "echo" }, { expect: { stdout: [{ contains: "*" }] } }),
makeTarget({ args: ["-e", "console.log('*')"], exec: "bun" }, { expect: { stdout: [{ contains: "*" }] } }),
makeCtx(),
);
expect(result.matched).toBe(true);

View File

@@ -1,5 +1,5 @@
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import { mkdir, rm } from "node:fs/promises";
import { mkdir } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
@@ -9,6 +9,7 @@ import { checkerRegistry } from "../../../src/server/checker/runner";
import { CommandChecker } from "../../../src/server/checker/runner/command/runner";
import { HttpChecker } from "../../../src/server/checker/runner/http/runner";
import { ProbeStore } from "../../../src/server/checker/store";
import { rmRetry } from "../../helpers";
function ensureRegistered() {
if (!checkerRegistry.supportedTypes.includes("http")) {
@@ -63,7 +64,7 @@ describe("ProbeStore", () => {
afterAll(async () => {
store.close();
await rm(tempDir, { force: true, recursive: true });
await rmRetry(tempDir);
});
test("初始化后无 targets", () => {