🏆 test: optimize test suite with FakeTimers and race condition fixes (#1284)

* fix: exclude prompt/permission from plan agent config

plan agent should only inherit model settings from prometheus,
not the prompt or permission. This ensures plan agent uses
OpenCode's default behavior while only overriding the model.

* test(todo-continuation-enforcer): use FakeTimers for 15x faster tests

- Add custom FakeTimers implementation (~100 lines)
- Replace all real setTimeout waits with fakeTimers.advanceBy()
- Test time: 104.6s → 7.01s

* test(callback-server): fix race conditions with Promise.all and Bun.fetch

- Use Bun.fetch.bind(Bun) to avoid globalThis.fetch mock interference
- Use Promise.all pattern for concurrent fetch/waitForCallback
- Add Bun.sleep(10) in afterEach for port release

* test(concurrency): replace placeholder assertions with getCount checks

Replace 6 meaningless expect(true).toBe(true) assertions with
actual getCount() verifications for test quality improvement

* refactor(config-handler): simplify planDemoteConfig creation

Remove unnecessary IIFE and destructuring, use direct spread instead

* test(executor): use FakeTimeouts for faster tests

- Add custom FakeTimeouts implementation
- Replace setTimeout waits with fakeTimeouts.advanceBy()
- Test time reduced from ~26s to ~6.8s

* test: fix gemini model mock for artistry unstable mode

* test: fix model list mock payload shape

* test: mock provider models for artistry category

---------

Co-authored-by: justsisyphus <justsisyphus@users.noreply.github.com>
This commit is contained in:
YeonGyu-Kim
2026-01-30 22:10:52 +09:00
committed by GitHub
parent b748fce051
commit df0a27dc4e
5 changed files with 285 additions and 76 deletions
@@ -1,11 +1,83 @@
import { describe, test, expect, mock, beforeEach, spyOn } from "bun:test"
import { afterEach, beforeEach, describe, expect, mock, spyOn, test } from "bun:test"
import { executeCompact } from "./executor"
import type { AutoCompactState } from "./types"
import * as storage from "./storage"
type TimerCallback = (...args: any[]) => void
interface FakeTimeouts {
advanceBy: (ms: number) => Promise<void>
restore: () => void
}
function createFakeTimeouts(): FakeTimeouts {
let now = 0
let nextId = 1
const timers = new Map<number, { id: number; time: number; callback: TimerCallback; args: any[] }>()
const cleared = new Set<number>()
const original = {
setTimeout: globalThis.setTimeout,
clearTimeout: globalThis.clearTimeout,
}
const normalizeDelay = (delay?: number) => {
if (typeof delay !== "number" || !Number.isFinite(delay)) return 0
return delay < 0 ? 0 : delay
}
globalThis.setTimeout = ((callback: TimerCallback, delay?: number, ...args: any[]) => {
const id = nextId++
timers.set(id, {
id,
time: now + normalizeDelay(delay),
callback,
args,
})
return id as unknown as ReturnType<typeof setTimeout>
}) as typeof setTimeout
globalThis.clearTimeout = ((id?: number) => {
if (typeof id !== "number") return
cleared.add(id)
timers.delete(id)
}) as typeof clearTimeout
const advanceBy = async (ms: number) => {
const target = now + Math.max(0, ms)
while (true) {
let next: { id: number; time: number; callback: TimerCallback; args: any[] } | undefined
for (const timer of timers.values()) {
if (timer.time <= target && (!next || timer.time < next.time)) {
next = timer
}
}
if (!next) break
now = next.time
timers.delete(next.id)
if (!cleared.has(next.id)) {
next.callback(...next.args)
}
cleared.delete(next.id)
await Promise.resolve()
}
now = target
await Promise.resolve()
}
const restore = () => {
globalThis.setTimeout = original.setTimeout
globalThis.clearTimeout = original.clearTimeout
}
return { advanceBy, restore }
}
describe("executeCompact lock management", () => {
let autoCompactState: AutoCompactState
let mockClient: any
let fakeTimeouts: FakeTimeouts
const sessionID = "test-session-123"
const directory = "/test/dir"
const msg = { providerID: "anthropic", modelID: "claude-opus-4-5" }
@@ -32,6 +104,12 @@ describe("executeCompact lock management", () => {
showToast: mock(() => Promise.resolve()),
},
}
fakeTimeouts = createFakeTimeouts()
})
afterEach(() => {
fakeTimeouts.restore()
})
test("clears lock on successful summarize completion", async () => {
@@ -216,7 +294,7 @@ describe("executeCompact lock management", () => {
await executeCompact(sessionID, msg, autoCompactState, mockClient, directory)
// Wait for setTimeout callback
await new Promise((resolve) => setTimeout(resolve, 600))
await fakeTimeouts.advanceBy(600)
// #then: Lock should be cleared
// The continuation happens in setTimeout, but lock is cleared in finally before that
@@ -288,7 +366,7 @@ describe("executeCompact lock management", () => {
await executeCompact(sessionID, msg, autoCompactState, mockClient, directory)
// Wait for setTimeout callback
await new Promise((resolve) => setTimeout(resolve, 600))
await fakeTimeouts.advanceBy(600)
// #then: Truncation was attempted
expect(truncateSpy).toHaveBeenCalled()