fix: 替换所有测试文件的 rm 为 retryRm,修复 Windows EBUSY

This commit is contained in:
2026-06-11 23:38:02 +08:00
parent 3cac492e78
commit 15ad256d1e
10 changed files with 82 additions and 121 deletions

View File

@@ -1,6 +1,7 @@
import { describe, it, expect, mock, beforeEach, afterEach } from "bun:test";
import { mkdir, access, rm } from "node:fs/promises";
import { describe, it, expect } from "bun:test";
import { mkdir, access } from "node:fs/promises";
import { join } from "node:path";
import { retryRm } from "./cleanup.ts";
const TMP_DIR = join(import.meta.dir, "__tmp_cleanup_test__");
@@ -14,106 +15,54 @@ async function dirExists(path: string): Promise<boolean> {
}
describe("retryRm", () => {
describe("正常删除", () => {
afterEach(async () => {
if (await dirExists(TMP_DIR)) {
await rm(TMP_DIR, { recursive: true, force: true });
it("删除存在的目录", async () => {
await mkdir(TMP_DIR, { recursive: true });
await retryRm(TMP_DIR);
expect(await dirExists(TMP_DIR)).toBe(false);
});
it("删除不存在的路径不报错force:true", async () => {
const nonExist = join(import.meta.dir, "__non_exist__");
await expect(retryRm(nonExist)).resolves.toBeUndefined();
});
it("EBUSY 后重试成功", async () => {
let callCount = 0;
const fakeRm = async () => {
callCount++;
if (callCount === 1) {
const err: any = new Error("EBUSY");
err.code = "EBUSY";
throw err;
}
});
it("删除存在的目录", async () => {
const { retryRm } = await import("./cleanup.ts");
await mkdir(TMP_DIR, { recursive: true });
await retryRm(TMP_DIR);
expect(await dirExists(TMP_DIR)).toBe(false);
});
it("删除不存在的路径不报错force:true", async () => {
const { retryRm } = await import("./cleanup.ts");
const nonExist = join(import.meta.dir, "__non_exist__");
await expect(retryRm(nonExist)).resolves.toBeUndefined();
});
};
await retryRm("/some/path", { maxRetries: 3, baseDelay: 10, _rm: fakeRm as any });
expect(callCount).toBe(2);
});
describe("EBUSY 重试后成功", () => {
let callCount: number;
beforeEach(() => {
callCount = 0;
mock.module("node:fs/promises", () => ({
rm: mock(async () => {
callCount++;
if (callCount === 1) {
const err: any = new Error("EBUSY");
err.code = "EBUSY";
throw err;
}
}),
}));
});
afterEach(() => {
mock.restore();
});
it("EBUSY 后重试成功", async () => {
const { retryRm } = await import("./cleanup.ts");
await retryRm("/some/path", { maxRetries: 3, baseDelay: 10 });
expect(callCount).toBe(2);
});
it("maxRetries 耗尽后抛出最后一个错误", async () => {
let callCount = 0;
const fakeRm = async () => {
callCount++;
const err: any = new Error("EBUSY");
err.code = "EBUSY";
throw err;
};
await expect(
retryRm("/some/path", { maxRetries: 2, baseDelay: 10, _rm: fakeRm as any }),
).rejects.toThrow("EBUSY");
expect(callCount).toBe(3);
});
describe("重试耗尽后抛出错误", () => {
let callCount: number;
beforeEach(() => {
callCount = 0;
mock.module("node:fs/promises", () => ({
rm: mock(async () => {
callCount++;
const err: any = new Error("EBUSY");
err.code = "EBUSY";
throw err;
}),
}));
});
afterEach(() => {
mock.restore();
});
it("maxRetries 耗尽后抛出最后一个错误", async () => {
const { retryRm } = await import("./cleanup.ts");
await expect(retryRm("/some/path", { maxRetries: 2, baseDelay: 10 })).rejects.toThrow(
"EBUSY",
);
expect(callCount).toBe(3);
});
});
describe("非 EBUSY/EPERM 错误不重试", () => {
let callCount: number;
beforeEach(() => {
callCount = 0;
mock.module("node:fs/promises", () => ({
rm: mock(async () => {
callCount++;
const err: any = new Error("ENOENT: no such file or directory");
err.code = "ENOENT";
throw err;
}),
}));
});
afterEach(() => {
mock.restore();
});
it("ENOENT 错误直接抛出不重试", async () => {
const { retryRm } = await import("./cleanup.ts");
await expect(retryRm("/some/path")).rejects.toThrow("ENOENT");
expect(callCount).toBe(1);
});
it("ENOENT 错误直接抛出不重试", async () => {
let callCount = 0;
const fakeRm = async () => {
callCount++;
const err: any = new Error("ENOENT: no such file or directory");
err.code = "ENOENT";
throw err;
};
await expect(retryRm("/some/path", { _rm: fakeRm as any })).rejects.toThrow("ENOENT");
expect(callCount).toBe(1);
});
});