fix(ci): isolate timer-sensitive tests

This commit is contained in:
Peter Steinberger
2026-05-31 03:30:35 -04:00
parent 122ae5db9e
commit 1fd9fe2b33
4 changed files with 44 additions and 8 deletions

View File

@@ -9,10 +9,13 @@ describe("isCliBindingFlushed", () => {
const workspaceDir = "/tmp/openclaw-workspace";
beforeEach(() => {
vi.useRealTimers();
restoreCliRunnerTestDeps();
});
afterEach(() => {
vi.clearAllTimers();
vi.useRealTimers();
restoreCliRunnerTestDeps();
});
@@ -34,14 +37,21 @@ describe("isCliBindingFlushed", () => {
});
it("retries up to three times before giving up", async () => {
vi.useFakeTimers();
const probe = vi.fn(async () => false);
setCliRunnerTestDeps({ claudeCliSessionTranscriptHasContent: probe });
expect(await isCliBindingFlushed("sid-cold", "claude-cli", workspaceDir)).toBe(false);
const resultPromise = isCliBindingFlushed("sid-cold", "claude-cli", workspaceDir);
await vi.advanceTimersByTimeAsync(0);
await vi.advanceTimersByTimeAsync(50);
await vi.advanceTimersByTimeAsync(150);
await expect(resultPromise).resolves.toBe(false);
expect(probe).toHaveBeenCalledTimes(3);
});
it("succeeds when the transcript becomes visible on a later retry", async () => {
vi.useFakeTimers();
let calls = 0;
const probe = vi.fn(async () => {
calls += 1;
@@ -49,7 +59,11 @@ describe("isCliBindingFlushed", () => {
});
setCliRunnerTestDeps({ claudeCliSessionTranscriptHasContent: probe });
expect(await isCliBindingFlushed("sid-late", "claude-cli", workspaceDir)).toBe(true);
const resultPromise = isCliBindingFlushed("sid-late", "claude-cli", workspaceDir);
await vi.advanceTimersByTimeAsync(0);
await vi.advanceTimersByTimeAsync(50);
await expect(resultPromise).resolves.toBe(true);
expect(probe).toHaveBeenCalledTimes(2);
});
@@ -72,6 +86,7 @@ describe("isCliBindingFlushed", () => {
expect(errored).not.toHaveBeenCalled();
expect(probe).toHaveBeenCalledTimes(3);
} finally {
vi.clearAllTimers();
vi.useRealTimers();
}
});

View File

@@ -1,4 +1,4 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { MAX_TIMER_TIMEOUT_MS } from "../shared/number-coercion.js";
import { loadModelCatalogForBrowse } from "./model-catalog-browse.js";
@@ -24,7 +24,12 @@ function config(params: { providerWildcard?: boolean } = {}): OpenClawConfig {
}
describe("loadModelCatalogForBrowse", () => {
beforeEach(() => {
vi.useRealTimers();
});
afterEach(() => {
vi.clearAllTimers();
vi.useRealTimers();
});
@@ -65,6 +70,7 @@ describe("loadModelCatalogForBrowse", () => {
});
it("returns an empty catalog when read-only catalog loading times out", async () => {
vi.useFakeTimers();
const onTimeout = vi.fn();
const loadCatalog = vi.fn(
() =>
@@ -80,9 +86,10 @@ describe("loadModelCatalogForBrowse", () => {
onTimeout,
});
await vi.advanceTimersByTimeAsync(5);
await expect(resultPromise).resolves.toEqual([]);
expect(onTimeout).toHaveBeenCalledExactlyOnceWith(5);
await new Promise((resolve) => setTimeout(resolve, 15));
await vi.advanceTimersByTimeAsync(10);
});
it("uses the default timeout when timeoutMs is non-finite", async () => {

View File

@@ -1,4 +1,4 @@
import { describe, expect, it, vi } from "vitest";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { MAX_TIMER_TIMEOUT_MS } from "../shared/number-coercion.js";
import { createTypingCallbacks } from "./typing.js";
@@ -16,6 +16,7 @@ async function withFakeTimers(run: () => Promise<void>) {
try {
await run();
} finally {
vi.clearAllTimers();
vi.useRealTimers();
}
}
@@ -56,6 +57,15 @@ function createTypingHarness(overrides: TypingCallbackOverrides = {}) {
}
describe("createTypingCallbacks", () => {
beforeEach(() => {
vi.useRealTimers();
});
afterEach(() => {
vi.clearAllTimers();
vi.useRealTimers();
});
it("invokes start on reply start", async () => {
const { start, onStartError, callbacks } = createTypingHarness();

View File

@@ -1,4 +1,4 @@
import { describe, expect, it, vi } from "vitest";
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { MsgContext } from "../auto-reply/templating.js";
import type { OpenClawConfig } from "../config/types.js";
import { MIN_AUDIO_FILE_BYTES } from "./defaults.js";
@@ -78,6 +78,10 @@ async function runAudioCapabilityWithTranscriber(params: {
}
describe("runCapability skips tiny audio files", () => {
beforeEach(() => {
vi.useRealTimers();
});
it("skips audio transcription when file is smaller than MIN_AUDIO_FILE_BYTES", async () => {
await withAudioFixture({
filePrefix: "openclaw-tiny-audio",
@@ -172,7 +176,7 @@ describe("runCapability skips tiny audio files", () => {
media,
cache,
transcribeAudio: async () => {
throw new Error("upstream 500");
throw Object.assign(new Error("HTTP 400 validation failed"), { status: 400 });
},
});
@@ -189,7 +193,7 @@ describe("runCapability skips tiny audio files", () => {
throw new Error("expected failed audio decision attempt");
}
expect(attempt.outcome).toBe("failed");
expect(attempt.reason).toContain("upstream 500");
expect(attempt.reason).toContain("HTTP 400 validation failed");
},
});
});