Skip to content

Commit 44b1e81

Browse files
anandgupta42claude
andcommitted
fix: eliminate flaky CI test failures in dispatcher, dbt, and tracer tests
Root causes: - `dispatcher.test.ts`: Bun's multi-file runner leaks `_ensureRegistered` hook from other files' `native/index.ts` imports. `reset()` clears handlers but not the hook, so `call()` triggers lazy registration instead of throwing. Fix: clear hook in `beforeEach`. - `dbt-first-execution.test.ts`: `mock.module` for DuckDB leaked across files. Fix: spread real module exports + ensure native/index.ts import. - `tracing-adversarial-final.test.ts`: 50ms snapshot waits too tight for CI under load. Fix: increase to 200ms/300ms. Result: 0 failures in full suite (4961 pass, 340 skip). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 93eba4b commit 44b1e81

3 files changed

Lines changed: 70 additions & 66 deletions

File tree

packages/opencode/test/altimate/dbt-first-execution.test.ts

Lines changed: 27 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,26 @@
1111
* Set DBT_TEST_PROJECT_ROOT env var to override the project path.
1212
*/
1313

14-
import { describe, expect, test, beforeAll, afterAll, beforeEach, mock } from "bun:test"
14+
import { describe, expect, test, beforeAll, afterAll, beforeEach, mock, spyOn } from "bun:test"
1515
import { existsSync, readFileSync } from "fs"
1616
import { join } from "path"
1717
import { homedir } from "os"
1818
import type { Connector } from "@altimateai/drivers/types"
19-
20-
// Mock DuckDB driver so tests don't require the native duckdb package
19+
import * as Dispatcher from "../../src/altimate/native/dispatcher"
20+
21+
// Import native/index.ts to ensure the lazy registration hook is set.
22+
// In Bun's multi-file runner, test execution order is unpredictable —
23+
// if dispatcher.test.ts runs first and clears the hook, this file's
24+
// Dispatcher.call() would fail with "No native handler" errors.
25+
// Re-importing here ensures the hook is always available.
26+
import "../../src/altimate/native"
27+
28+
// Mock DuckDB driver so tests don't require the native duckdb package.
29+
// NOTE: mock.module leaks across test files in Bun — we spread the real
30+
// module exports to minimize damage to other test files.
31+
import * as realDuckdb from "@altimateai/drivers/duckdb"
2132
mock.module("@altimateai/drivers/duckdb", () => ({
33+
...realDuckdb,
2234
connect: async (config: any) => ({
2335
execute: async (sql: string) => {
2436
// Simple mock: parse SELECT literals
@@ -108,15 +120,11 @@ const HAS_DBT = !!DBT_PROJECT
108120
// ---------------------------------------------------------------------------
109121
describe("dbt Profiles Auto-Discovery", () => {
110122
test("parseDbtProfiles finds connections from ~/.dbt/profiles.yml", async () => {
111-
const { parseDbtProfiles } = await import(
112-
"../../src/altimate/native/connections/dbt-profiles"
113-
)
123+
const { parseDbtProfiles } = await import("../../src/altimate/native/connections/dbt-profiles")
114124
const profiles = await parseDbtProfiles()
115125
console.log(` Found ${profiles.length} dbt profile connections`)
116126
if (profiles.length > 0) {
117-
console.log(
118-
` Types: ${profiles.map((p: any) => p.config?.type || p.type).join(", ")}`,
119-
)
127+
console.log(` Types: ${profiles.map((p: any) => p.config?.type || p.type).join(", ")}`)
120128
}
121129
expect(Array.isArray(profiles)).toBe(true)
122130
})
@@ -127,19 +135,15 @@ describe("dbt Profiles Auto-Discovery", () => {
127135
const r = await Dispatcher.call("dbt.profiles", {})
128136
expect(r.success).toBe(true)
129137
expect(Array.isArray(r.connections)).toBe(true)
130-
console.log(
131-
` dbt.profiles found ${r.connection_count} connection(s)`,
132-
)
138+
console.log(` dbt.profiles found ${r.connection_count} connection(s)`)
133139
})
134140

135141
test("warehouse.discover includes dbt profiles", async () => {
136142
const { Dispatcher } = await import("../../src/altimate/native")
137143
const r = await Dispatcher.call("warehouse.discover", {})
138144
// dbt_profiles may be in the result
139145
if ((r as any).dbt_profiles && (r as any).dbt_profiles.length > 0) {
140-
console.log(
141-
` warehouse.discover found ${(r as any).dbt_profiles.length} dbt profiles`,
142-
)
146+
console.log(` warehouse.discover found ${(r as any).dbt_profiles.length} dbt profiles`)
143147
}
144148
expect(r).toHaveProperty("containers")
145149
expect(r).toHaveProperty("container_count")
@@ -156,9 +160,7 @@ describe.skipIf(!HAS_DBT)("dbt-First SQL Execution E2E", () => {
156160
})
157161

158162
test("dbt adapter can be created from config", async () => {
159-
const { read: readConfig } = await import(
160-
"../../../dbt-tools/src/config"
161-
)
163+
const { read: readConfig } = await import("../../../dbt-tools/src/config")
162164
const cfg = await readConfig()
163165
if (!cfg) {
164166
console.log(" No dbt config — skipping adapter test")
@@ -173,14 +175,10 @@ describe.skipIf(!HAS_DBT)("dbt-First SQL Execution E2E", () => {
173175

174176
test("sql.execute without warehouse tries dbt first", async () => {
175177
// Reset registry so no native connections are configured
176-
const Registry = await import(
177-
"../../src/altimate/native/connections/registry"
178-
)
178+
const Registry = await import("../../src/altimate/native/connections/registry")
179179
Registry.reset()
180180

181-
const { resetDbtAdapter } = await import(
182-
"../../src/altimate/native/connections/register"
183-
)
181+
const { resetDbtAdapter } = await import("../../src/altimate/native/connections/register")
184182
resetDbtAdapter()
185183

186184
const { Dispatcher } = await import("../../src/altimate/native")
@@ -207,9 +205,7 @@ describe.skipIf(!HAS_DBT)("Direct dbt Adapter Execution", () => {
207205

208206
beforeAll(async () => {
209207
try {
210-
const { read: readConfig } = await import(
211-
"../../../dbt-tools/src/config"
212-
)
208+
const { read: readConfig } = await import("../../../dbt-tools/src/config")
213209
const cfg = await readConfig()
214210
if (!cfg) return
215211

@@ -231,10 +227,7 @@ describe.skipIf(!HAS_DBT)("Direct dbt Adapter Execution", () => {
231227
if (!adapter) return
232228
try {
233229
// Try a simple query that works on most dbt projects
234-
const r = await adapter.immediatelyExecuteSQL(
235-
"SELECT COUNT(*) AS cnt FROM information_schema.tables",
236-
"",
237-
)
230+
const r = await adapter.immediatelyExecuteSQL("SELECT COUNT(*) AS cnt FROM information_schema.tables", "")
238231
expect(r).toBeTruthy()
239232
console.log(` Tables count query succeeded`)
240233
} catch (e: any) {
@@ -258,14 +251,10 @@ describe.skipIf(!HAS_DBT)("Direct dbt Adapter Execution", () => {
258251
// ---------------------------------------------------------------------------
259252
describe("dbt Fallback Behavior", () => {
260253
test("when dbt not configured, falls back to native driver silently", async () => {
261-
const Registry = await import(
262-
"../../src/altimate/native/connections/registry"
263-
)
254+
const Registry = await import("../../src/altimate/native/connections/registry")
264255
Registry.reset()
265256

266-
const { resetDbtAdapter } = await import(
267-
"../../src/altimate/native/connections/register"
268-
)
257+
const { resetDbtAdapter } = await import("../../src/altimate/native/connections/register")
269258
resetDbtAdapter()
270259

271260
// Set up a native DuckDB connection as fallback
@@ -284,9 +273,7 @@ describe("dbt Fallback Behavior", () => {
284273
})
285274

286275
test("explicit warehouse param bypasses dbt entirely", async () => {
287-
const Registry = await import(
288-
"../../src/altimate/native/connections/registry"
289-
)
276+
const Registry = await import("../../src/altimate/native/connections/registry")
290277
Registry.reset()
291278
Registry.setConfigs({
292279
my_duck: { type: "duckdb", path: ":memory:" },

packages/opencode/test/altimate/dispatcher.test.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,22 @@ import { describe, expect, test, beforeEach, beforeAll, afterAll, mock } from "b
22
import * as Dispatcher from "../../src/altimate/native/dispatcher"
33

44
// Disable telemetry via env var instead of mock.module
5-
beforeAll(() => { process.env.ALTIMATE_TELEMETRY_DISABLED = "true" })
6-
afterAll(() => { delete process.env.ALTIMATE_TELEMETRY_DISABLED })
5+
beforeAll(() => {
6+
process.env.ALTIMATE_TELEMETRY_DISABLED = "true"
7+
})
8+
afterAll(() => {
9+
delete process.env.ALTIMATE_TELEMETRY_DISABLED
10+
})
711

812
describe("Dispatcher", () => {
913
beforeEach(() => {
1014
Dispatcher.reset()
15+
// Clear lazy registration hook to prevent other test files' imports
16+
// from triggering handler registration during these unit tests.
17+
// Without this, Bun's multi-file runner leaks the hook from files
18+
// that import native/index.ts, causing call() to resolve instead
19+
// of rejecting for unregistered methods.
20+
Dispatcher.setRegistrationHook(null as any)
1121
})
1222

1323
describe("register and hasNativeHandler", () => {
@@ -36,9 +46,7 @@ describe("Dispatcher", () => {
3646

3747
describe("call — no handler", () => {
3848
test("throws when no native handler registered", async () => {
39-
await expect(Dispatcher.call("ping", {} as any)).rejects.toThrow(
40-
"No native handler for ping",
41-
)
49+
await expect(Dispatcher.call("ping", {} as any)).rejects.toThrow("No native handler for ping")
4250
})
4351
})
4452

@@ -65,7 +73,9 @@ describe("Dispatcher", () => {
6573
})
6674

6775
test("tracks telemetry on error", async () => {
68-
Dispatcher.register("ping", async () => { throw new Error("fail") })
76+
Dispatcher.register("ping", async () => {
77+
throw new Error("fail")
78+
})
6979
await expect(Dispatcher.call("ping", {} as any)).rejects.toThrow("fail")
7080
// Telemetry is disabled — just verify no crash
7181
})

packages/opencode/test/altimate/tracing-adversarial-final.test.ts

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -223,15 +223,16 @@ describe("Orphaned generation — endTrace with unclosed generation", () => {
223223
test("snapshot mid-generation shows 'running', endTrace shows 'completed'", async () => {
224224
const tracer = Recap.withExporters([new FileExporter(tmpDir)])
225225
tracer.startTrace("s-run-complete", { prompt: "test" })
226-
await new Promise((r) => setTimeout(r, 50)) // wait for initial snapshot
226+
await new Promise((r) => setTimeout(r, 200)) // wait for initial snapshot
227227
tracer.logStepStart({ id: "1" })
228228
tracer.logToolCall({
229-
tool: "bash", callID: "c1",
229+
tool: "bash",
230+
callID: "c1",
230231
state: { status: "completed", input: {}, output: "ok", time: { start: 1, end: 2 } },
231232
})
232233

233234
// Wait for snapshot — should be "running"
234-
await new Promise((r) => setTimeout(r, 50))
235+
await new Promise((r) => setTimeout(r, 200))
235236
const snap = JSON.parse(await fs.readFile(tracer.getTracePath()!, "utf-8")) as TraceFile
236237
expect(snap.summary.status).toBe("running")
237238

@@ -269,7 +270,8 @@ describe("Worker race — events after endTrace", () => {
269270
const tracer = getOrCreateRecap("race-session")!
270271
tracer.logStepStart({ id: "1" })
271272
tracer.logToolCall({
272-
tool: "bash", callID: "c1",
273+
tool: "bash",
274+
callID: "c1",
273275
state: { status: "completed", input: {}, output: "ok", time: { start: 1, end: 2 } },
274276
})
275277
tracer.logStepFinish(ZERO_STEP)
@@ -294,7 +296,7 @@ describe("Worker race — events after endTrace", () => {
294296
}
295297

296298
// Wait for endTrace to complete
297-
await new Promise((r) => setTimeout(r, 50))
299+
await new Promise((r) => setTimeout(r, 200))
298300

299301
// Verify the late event was NOT added to the trace
300302
const filePath = path.join(tmpDir, "race-session.json")
@@ -325,7 +327,8 @@ describe("Worker race — events after endTrace", () => {
325327
const t1 = getOrCreateRecap("cycle-test")!
326328
t1.logStepStart({ id: "1" })
327329
t1.logToolCall({
328-
tool: "bash", callID: "c1",
330+
tool: "bash",
331+
callID: "c1",
329332
state: { status: "completed", input: {}, output: "cycle1", time: { start: 1, end: 2 } },
330333
})
331334
t1.logStepFinish(ZERO_STEP)
@@ -338,16 +341,15 @@ describe("Worker race — events after endTrace", () => {
338341

339342
t2.logStepStart({ id: "1" })
340343
t2.logToolCall({
341-
tool: "read", callID: "c2",
344+
tool: "read",
345+
callID: "c2",
342346
state: { status: "completed", input: {}, output: "cycle2", time: { start: 3, end: 4 } },
343347
})
344348
t2.logStepFinish(ZERO_STEP)
345349
await t2.endTrace()
346350

347351
// File should have cycle 2 data
348-
const traceFile: TraceFile = JSON.parse(
349-
await fs.readFile(path.join(tmpDir, "cycle-test.json"), "utf-8"),
350-
)
352+
const traceFile: TraceFile = JSON.parse(await fs.readFile(path.join(tmpDir, "cycle-test.json"), "utf-8"))
351353
expect(traceFile.spans.filter((s) => s.kind === "tool")).toHaveLength(1)
352354
expect(traceFile.spans.find((s) => s.kind === "tool")!.name).toBe("read")
353355
})
@@ -403,33 +405,35 @@ describe("buildTraceFile — status transitions", () => {
403405
const path1 = tracer.getTracePath()!
404406

405407
// Wait for initial snapshot — should be "completed" (no active generation)
406-
await new Promise((r) => setTimeout(r, 50))
408+
await new Promise((r) => setTimeout(r, 200))
407409
const snap0 = JSON.parse(await fs.readFile(path1, "utf-8")) as TraceFile
408410
expect(snap0.summary.status).toBe("completed")
409411

410412
// Start generation — internal state now has currentGenerationSpanId
411413
tracer.logStepStart({ id: "1" })
412414
tracer.logToolCall({
413-
tool: "bash", callID: "c1",
415+
tool: "bash",
416+
callID: "c1",
414417
state: { status: "completed", input: {}, output: "ok", time: { start: 1, end: 2 } },
415418
})
416-
await new Promise((r) => setTimeout(r, 50))
419+
await new Promise((r) => setTimeout(r, 200))
417420
const snap1 = JSON.parse(await fs.readFile(path1, "utf-8")) as TraceFile
418421
expect(snap1.summary.status).toBe("running")
419422

420423
// Finish generation — should go back to "completed"
421424
tracer.logStepFinish(ZERO_STEP)
422-
await new Promise((r) => setTimeout(r, 50))
425+
await new Promise((r) => setTimeout(r, 200))
423426
const snap2 = JSON.parse(await fs.readFile(path1, "utf-8")) as TraceFile
424427
expect(snap2.summary.status).toBe("completed")
425428

426429
// Start another generation
427430
tracer.logStepStart({ id: "2" })
428431
tracer.logToolCall({
429-
tool: "read", callID: "c2",
432+
tool: "read",
433+
callID: "c2",
430434
state: { status: "completed", input: {}, output: "ok", time: { start: 3, end: 4 } },
431435
})
432-
await new Promise((r) => setTimeout(r, 50))
436+
await new Promise((r) => setTimeout(r, 200))
433437
const snap3 = JSON.parse(await fs.readFile(path1, "utf-8")) as TraceFile
434438
expect(snap3.summary.status).toBe("running")
435439

@@ -460,7 +464,7 @@ describe("Exporter ordering", () => {
460464
const server = Bun.serve({
461465
port: 0,
462466
async fetch() {
463-
await new Promise((r) => setTimeout(r, 100))
467+
await new Promise((r) => setTimeout(r, 300))
464468
return Response.json({ url: "http://slow.com/trace/1" })
465469
},
466470
})
@@ -491,17 +495,20 @@ describe("Snapshot debounce under load", () => {
491495
for (let i = 0; i < 10; i++) {
492496
tracer.logStepStart({ id: `${i}` })
493497
tracer.logToolCall({
494-
tool: `tool-${i}`, callID: `c-${i}`,
498+
tool: `tool-${i}`,
499+
callID: `c-${i}`,
495500
state: { status: "completed", input: {}, output: `out-${i}`, time: { start: 1, end: 2 } },
496501
})
497502
tracer.logStepFinish({
498-
id: `${i}`, reason: "stop", cost: 0.001,
503+
id: `${i}`,
504+
reason: "stop",
505+
cost: 0.001,
499506
tokens: { input: 100, output: 50, reasoning: 0, cache: { read: 0, write: 0 } },
500507
})
501508
}
502509

503510
// Wait for all snapshots to settle
504-
await new Promise((r) => setTimeout(r, 100))
511+
await new Promise((r) => setTimeout(r, 300))
505512

506513
const filePath = await tracer.endTrace()
507514
const traceFile: TraceFile = JSON.parse(await fs.readFile(filePath!, "utf-8"))

0 commit comments

Comments
 (0)