Skip to content

Commit 3721c88

Browse files
anandgupta42claude
andauthored
fix: replace mock.module() with spyOn() to fix 149 test failures (#153)
* fix: replace `mock.module()` with `spyOn()` in enhance-prompt tests to prevent global module cache poisoning `mock.module()` in bun replaces modules process-wide with no restore mechanism. The enhance-prompt test file was mocking 7 core modules (`@/util/log`, `@/session/message-v2`, `@/session/llm`, etc.) with incomplete stubs, causing 149 test failures when the full suite runs. Switch to `spyOn()` for the 3 modules that actually need mocking (`Config`, `Provider`, `LLM`) and remove the 4 unnecessary mocks entirely. Spies are restored in `afterAll`, preventing cross-file leaks. Closes #17647 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use `ubuntu-latest` runner for `pr-management` workflow The `blacksmith-4vcpu-ubuntu-2404` self-hosted runner is unavailable, causing the `check-duplicates` job to hang indefinitely waiting for a runner. Switch to GitHub-hosted `ubuntu-latest`. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 4fada35 commit 3721c88

File tree

2 files changed

+49
-69
lines changed

2 files changed

+49
-69
lines changed

.github/workflows/pr-management.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ on:
66

77
jobs:
88
check-duplicates:
9-
runs-on: blacksmith-4vcpu-ubuntu-2404
9+
runs-on: ubuntu-latest
1010
permissions:
1111
contents: read
1212
pull-requests: write

packages/opencode/test/altimate/enhance-prompt.test.ts

Lines changed: 48 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,57 @@
1-
import { describe, expect, test, mock, beforeEach } from "bun:test"
1+
import { describe, expect, test, spyOn, beforeEach, afterAll } from "bun:test"
22
import { clean, stripThinkTags } from "../../src/altimate/enhance-prompt"
3+
import { Config } from "@/config/config"
4+
import { Provider } from "@/provider/provider"
5+
import { LLM } from "@/session/llm"
6+
7+
// ---------------------------------------------------------------------------
8+
// Spy-based mocking — scoped to this file and cleaned up in afterAll.
9+
// NEVER use mock.module() for shared infrastructure modules (@/util/log,
10+
// @/config/config, @/session/llm, etc.) because bun's mock.module() replaces
11+
// the module globally for the entire process with no restore mechanism,
12+
// breaking every other test file that imports the real module.
13+
// ---------------------------------------------------------------------------
314

4-
// Mock Config for isAutoEnhanceEnabled tests
515
let mockConfig: any = {}
6-
mock.module("@/config/config", () => ({
7-
Config: {
8-
get: () => Promise.resolve(mockConfig),
9-
},
10-
}))
11-
12-
// Mock Provider and LLM for enhancePrompt tests
1316
let mockStreamResult: string | undefined = "enhanced result"
1417
let mockStreamShouldThrow = false
15-
mock.module("@/provider/provider", () => ({
16-
Provider: {
17-
defaultModel: () =>
18-
Promise.resolve({ providerID: "test-provider", modelID: "test-model" }),
19-
getSmallModel: () =>
20-
Promise.resolve({ providerID: "test-provider", id: "test-small", modelID: "test-small" }),
21-
getModel: () =>
22-
Promise.resolve({ providerID: "test-provider", id: "test-model", modelID: "test-model" }),
23-
},
24-
}))
25-
26-
mock.module("@/session/llm", () => ({
27-
LLM: {
28-
stream: () => {
29-
if (mockStreamShouldThrow) return Promise.reject(new Error("stream init failed"))
30-
return Promise.resolve({
31-
// fullStream must be an async iterable (consumed by for-await in enhancePrompt)
32-
fullStream: {
33-
[Symbol.asyncIterator]: () => ({
34-
next: () => Promise.resolve({ done: true, value: undefined }),
35-
}),
36-
},
37-
text: mockStreamResult !== undefined
38-
? Promise.resolve(mockStreamResult)
39-
: Promise.reject(new Error("stream text failed")),
40-
})
18+
19+
const configSpy = spyOn(Config as any, "get").mockImplementation(() => Promise.resolve(mockConfig))
20+
21+
const defaultModelSpy = spyOn(Provider as any, "defaultModel").mockImplementation(() =>
22+
Promise.resolve({ providerID: "test-provider", modelID: "test-model" }),
23+
)
24+
const getSmallModelSpy = spyOn(Provider as any, "getSmallModel").mockImplementation(() =>
25+
Promise.resolve({ providerID: "test-provider", id: "test-small", modelID: "test-small" }),
26+
)
27+
const getModelSpy = spyOn(Provider as any, "getModel").mockImplementation(() =>
28+
Promise.resolve({ providerID: "test-provider", id: "test-model", modelID: "test-model" }),
29+
)
30+
31+
const streamSpy = spyOn(LLM as any, "stream").mockImplementation(() => {
32+
if (mockStreamShouldThrow) return Promise.reject(new Error("stream init failed"))
33+
return Promise.resolve({
34+
fullStream: {
35+
[Symbol.asyncIterator]: () => ({
36+
next: () => Promise.resolve({ done: true, value: undefined }),
37+
}),
4138
},
42-
},
43-
}))
44-
45-
mock.module("@/util/log", () => ({
46-
Log: {
47-
create: () => ({
48-
info: () => {},
49-
error: () => {},
50-
debug: () => {},
51-
}),
52-
},
53-
}))
54-
55-
mock.module("@/agent/agent", () => ({
56-
Agent: {},
57-
}))
58-
59-
mock.module("@/session/message-v2", () => ({
60-
MessageV2: {},
61-
}))
62-
63-
let idCounter = 0
64-
mock.module("@/session/schema", () => ({
65-
MessageID: {
66-
ascending: () => `msg-${++idCounter}`,
67-
},
68-
SessionID: {
69-
descending: () => `session-${++idCounter}`,
70-
},
71-
}))
72-
73-
// Import after mocking
39+
text:
40+
mockStreamResult !== undefined
41+
? Promise.resolve(mockStreamResult)
42+
: Promise.reject(new Error("stream text failed")),
43+
})
44+
})
45+
46+
afterAll(() => {
47+
configSpy.mockRestore()
48+
defaultModelSpy.mockRestore()
49+
getSmallModelSpy.mockRestore()
50+
getModelSpy.mockRestore()
51+
streamSpy.mockRestore()
52+
})
53+
54+
// Import enhancePrompt/isAutoEnhanceEnabled after spies are in place
7455
const { enhancePrompt, isAutoEnhanceEnabled } = await import("../../src/altimate/enhance-prompt")
7556

7657
describe("enhance-prompt clean()", () => {
@@ -141,7 +122,6 @@ describe("enhance-prompt clean()", () => {
141122
})
142123

143124
test("handles nested quotes inside code fences", () => {
144-
// After fence stripping, quote stripping also triggers on surrounding quotes
145125
expect(clean('```\n\'inner quoted\'\n```')).toBe("inner quoted")
146126
})
147127
})

0 commit comments

Comments
 (0)