fix(ci): make Windows unit tests deterministic

This commit is contained in:
Peter Steinberger
2026-02-15 03:46:43 +00:00
parent cb54a532f0
commit a47b08d551
2 changed files with 8 additions and 45 deletions

View File

@@ -99,9 +99,10 @@ async function execLaunchctl(
args: string[], args: string[],
): Promise<{ stdout: string; stderr: string; code: number }> { ): Promise<{ stdout: string; stderr: string; code: number }> {
try { try {
const { stdout, stderr } = await execFileAsync("launchctl", args, { const isWindows = process.platform === "win32";
encoding: "utf8", const file = isWindows ? (process.env.ComSpec ?? "cmd.exe") : "launchctl";
}); const fileArgs = isWindows ? ["/d", "/s", "/c", "launchctl", ...args] : args;
const { stdout, stderr } = await execFileAsync(file, fileArgs, { encoding: "utf8" });
return { return {
stdout: String(stdout ?? ""), stdout: String(stdout ?? ""),
stderr: String(stderr ?? ""), stderr: String(stderr ?? ""),

View File

@@ -62,36 +62,6 @@ function makeProcStat(pid: number, startTime: number) {
return `${pid} (node) ${fields.join(" ")}`; return `${pid} (node) ${fields.join(" ")}`;
} }
type PromiseSettlement<T> =
| { status: "resolved"; value: T }
| { status: "rejected"; reason: unknown };
async function settleWithFakeTimers<T>(
promise: Promise<T>,
params: { stepMs: number; maxSteps: number },
) {
const wrapped: Promise<PromiseSettlement<T>> = promise.then(
(value) => ({ status: "resolved", value }),
(reason) => ({ status: "rejected", reason }),
);
for (let step = 0; step < params.maxSteps; step += 1) {
const settled = await Promise.race([wrapped, Promise.resolve(null)]);
if (settled) {
return settled;
}
await vi.advanceTimersByTimeAsync(params.stepMs);
}
const final = await Promise.race([wrapped, Promise.resolve(null)]);
if (final) {
return final;
}
throw new Error(
`promise did not settle after ${params.maxSteps} steps of ${params.stepMs}ms fake time`,
);
}
describe("gateway lock", () => { describe("gateway lock", () => {
beforeAll(async () => { beforeAll(async () => {
fixtureRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-gateway-lock-")); fixtureRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-gateway-lock-"));
@@ -106,7 +76,7 @@ describe("gateway lock", () => {
}); });
it("blocks concurrent acquisition until release", async () => { it("blocks concurrent acquisition until release", async () => {
vi.useFakeTimers(); vi.useRealTimers();
const { env, cleanup } = await makeEnv(); const { env, cleanup } = await makeEnv();
const lock = await acquireGatewayLock({ const lock = await acquireGatewayLock({
env, env,
@@ -122,11 +92,7 @@ describe("gateway lock", () => {
timeoutMs: 80, timeoutMs: 80,
pollIntervalMs: 5, pollIntervalMs: 5,
}); });
const settlement = await settleWithFakeTimers(pending, { stepMs: 5, maxSteps: 40 }); await expect(pending).rejects.toBeInstanceOf(GatewayLockError);
expect(settlement.status).toBe("rejected");
expect((settlement as { status: "rejected"; reason: unknown }).reason).toBeInstanceOf(
GatewayLockError,
);
await lock?.release(); await lock?.release();
const lock2 = await acquireGatewayLock({ const lock2 = await acquireGatewayLock({
@@ -175,7 +141,7 @@ describe("gateway lock", () => {
}); });
it("keeps lock on linux when proc access fails unless stale", async () => { it("keeps lock on linux when proc access fails unless stale", async () => {
vi.useFakeTimers(); vi.useRealTimers();
const { env, cleanup } = await makeEnv(); const { env, cleanup } = await makeEnv();
const { lockPath, configPath } = resolveLockPath(env); const { lockPath, configPath } = resolveLockPath(env);
const payload = { const payload = {
@@ -202,11 +168,7 @@ describe("gateway lock", () => {
staleMs: 10_000, staleMs: 10_000,
platform: "linux", platform: "linux",
}); });
const settlement = await settleWithFakeTimers(pending, { stepMs: 5, maxSteps: 30 }); await expect(pending).rejects.toBeInstanceOf(GatewayLockError);
expect(settlement.status).toBe("rejected");
expect((settlement as { status: "rejected"; reason: unknown }).reason).toBeInstanceOf(
GatewayLockError,
);
spy.mockRestore(); spy.mockRestore();