fix(ci): resolve mock.module() cross-file leakage in test suite
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -33,7 +33,9 @@ mock.module("../session-recovery/storage/text-part-injector", () => ({
|
||||
}))
|
||||
|
||||
async function importFreshMessageBuilder(): Promise<typeof import("./message-builder")> {
|
||||
return import(`./message-builder?test=${Date.now()}-${Math.random()}`)
|
||||
const module = await import(`./message-builder?test=${Date.now()}-${Math.random()}`)
|
||||
mock.restore()
|
||||
return module
|
||||
}
|
||||
|
||||
afterAll(() => {
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
import { afterAll, describe, expect, it, mock } from "bun:test"
|
||||
import { afterEach, describe, expect, it, spyOn } from "bun:test"
|
||||
import type { LoadedSkill } from "../../features/opencode-skill-loader"
|
||||
import * as shared from "../../shared"
|
||||
import * as slashcommand from "../../tools/slashcommand"
|
||||
import { executeSlashCommand } from "./executor"
|
||||
|
||||
mock.module("../../shared", () => ({
|
||||
resolveCommandsInText: async (content: string) => content,
|
||||
resolveFileReferencesInText: async (content: string) => content,
|
||||
}))
|
||||
let resolveCommandsInTextSpy: { mockRestore: () => void } | undefined
|
||||
let resolveFileReferencesInTextSpy: { mockRestore: () => void } | undefined
|
||||
let discoverCommandsSyncSpy: { mockRestore: () => void } | undefined
|
||||
|
||||
mock.module("../../tools/slashcommand", () => ({
|
||||
discoverCommandsSync: () => [
|
||||
function setupExecutorSpies(): void {
|
||||
resolveCommandsInTextSpy = spyOn(shared, "resolveCommandsInText")
|
||||
.mockImplementation(async (content: string) => content)
|
||||
resolveFileReferencesInTextSpy = spyOn(shared, "resolveFileReferencesInText")
|
||||
.mockImplementation(async (content: string) => content)
|
||||
discoverCommandsSyncSpy = spyOn(slashcommand, "discoverCommandsSync").mockReturnValue([
|
||||
{
|
||||
name: "shadowed",
|
||||
metadata: { name: "shadowed", description: "builtin" },
|
||||
@@ -20,18 +26,19 @@ mock.module("../../tools/slashcommand", () => ({
|
||||
content: "project template",
|
||||
scope: "project",
|
||||
},
|
||||
],
|
||||
}))
|
||||
])
|
||||
}
|
||||
|
||||
mock.module("../../features/opencode-skill-loader", () => ({
|
||||
discoverAllSkills: async (): Promise<LoadedSkill[]> => [],
|
||||
}))
|
||||
function restoreExecutorSpies(): void {
|
||||
resolveCommandsInTextSpy?.mockRestore()
|
||||
resolveFileReferencesInTextSpy?.mockRestore()
|
||||
discoverCommandsSyncSpy?.mockRestore()
|
||||
resolveCommandsInTextSpy = undefined
|
||||
resolveFileReferencesInTextSpy = undefined
|
||||
discoverCommandsSyncSpy = undefined
|
||||
}
|
||||
|
||||
afterAll(() => {
|
||||
mock.restore()
|
||||
})
|
||||
|
||||
const { executeSlashCommand } = await import("./executor")
|
||||
afterEach(restoreExecutorSpies)
|
||||
|
||||
function createRestrictedSkill(): LoadedSkill {
|
||||
return {
|
||||
@@ -49,6 +56,7 @@ function createRestrictedSkill(): LoadedSkill {
|
||||
describe("executeSlashCommand resolution semantics", () => {
|
||||
it("returns project command when project and builtin names collide", async () => {
|
||||
//#given
|
||||
setupExecutorSpies()
|
||||
const parsed = {
|
||||
command: "shadowed",
|
||||
args: "",
|
||||
@@ -67,6 +75,7 @@ describe("executeSlashCommand resolution semantics", () => {
|
||||
|
||||
it("blocks slash skill invocation when invoking agent is missing", async () => {
|
||||
//#given
|
||||
setupExecutorSpies()
|
||||
const parsed = {
|
||||
command: "restricted-skill",
|
||||
args: "",
|
||||
@@ -83,6 +92,7 @@ describe("executeSlashCommand resolution semantics", () => {
|
||||
|
||||
it("allows slash skill invocation when invoking agent matches restriction", async () => {
|
||||
//#given
|
||||
setupExecutorSpies()
|
||||
const parsed = {
|
||||
command: "restricted-skill",
|
||||
args: "",
|
||||
|
||||
@@ -53,7 +53,9 @@ function createEvent(type: string, parentID?: string) {
|
||||
}
|
||||
|
||||
async function importFreshModule() {
|
||||
return import(`./hook?t=${Date.now()}-${Math.random()}`)
|
||||
const module = await import(`./hook?t=${Date.now()}-${Math.random()}`)
|
||||
mock.restore()
|
||||
return module
|
||||
}
|
||||
|
||||
describe("createLegacyPluginToastHook", () => {
|
||||
|
||||
@@ -57,12 +57,13 @@ afterAll(() => {
|
||||
mock.restore()
|
||||
})
|
||||
|
||||
import {
|
||||
const {
|
||||
clearPendingModelFallback,
|
||||
createModelFallbackHook,
|
||||
setSessionFallbackChain,
|
||||
setPendingModelFallback,
|
||||
} from "./hook"
|
||||
} = await import("./hook")
|
||||
mock.restore()
|
||||
|
||||
describe("model fallback hook", () => {
|
||||
beforeEach(() => {
|
||||
@@ -452,3 +453,5 @@ describe("model fallback hook", () => {
|
||||
clearPendingModelFallback(sessionID)
|
||||
})
|
||||
})
|
||||
|
||||
export {}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
/// <reference types="bun-types" />
|
||||
|
||||
import { beforeEach, describe, expect, it, mock } from "bun:test"
|
||||
import { afterEach, beforeEach, describe, expect, it, mock, spyOn } from "bun:test"
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
|
||||
import type { ImageDimensions, ResizeResult } from "./types"
|
||||
import * as imageDimensions from "./image-dimensions"
|
||||
import * as imageResizer from "./image-resizer"
|
||||
import * as sessionModelState from "../../shared/session-model-state"
|
||||
import { createReadImageResizerHook } from "./hook"
|
||||
|
||||
const mockParseImageDimensions = mock((): ImageDimensions | null => null)
|
||||
const mockCalculateTargetDimensions = mock((): ImageDimensions | null => null)
|
||||
@@ -13,20 +17,17 @@ const mockGetSessionModel = mock((_sessionID: string) => ({
|
||||
modelID: "claude-sonnet-4-6",
|
||||
} as { providerID: string; modelID: string } | undefined))
|
||||
|
||||
mock.module("./image-dimensions", () => ({
|
||||
parseImageDimensions: mockParseImageDimensions,
|
||||
}))
|
||||
let parseImageDimensionsSpy: { mockRestore: () => void } | undefined
|
||||
let calculateTargetDimensionsSpy: { mockRestore: () => void } | undefined
|
||||
let resizeImageSpy: { mockRestore: () => void } | undefined
|
||||
let getSessionModelSpy: { mockRestore: () => void } | undefined
|
||||
|
||||
mock.module("./image-resizer", () => ({
|
||||
calculateTargetDimensions: mockCalculateTargetDimensions,
|
||||
resizeImage: mockResizeImage,
|
||||
}))
|
||||
|
||||
mock.module("../../shared/session-model-state", () => ({
|
||||
getSessionModel: mockGetSessionModel,
|
||||
}))
|
||||
|
||||
import { createReadImageResizerHook } from "./hook"
|
||||
function setupHookSpies(): void {
|
||||
parseImageDimensionsSpy = spyOn(imageDimensions, "parseImageDimensions").mockImplementation(mockParseImageDimensions)
|
||||
calculateTargetDimensionsSpy = spyOn(imageResizer, "calculateTargetDimensions").mockImplementation(mockCalculateTargetDimensions)
|
||||
resizeImageSpy = spyOn(imageResizer, "resizeImage").mockImplementation(mockResizeImage)
|
||||
getSessionModelSpy = spyOn(sessionModelState, "getSessionModel").mockImplementation(mockGetSessionModel)
|
||||
}
|
||||
|
||||
type ToolOutput = {
|
||||
title: string
|
||||
@@ -52,6 +53,7 @@ function createInput(tool: string): { tool: string; sessionID: string; callID: s
|
||||
|
||||
describe("createReadImageResizerHook", () => {
|
||||
beforeEach(() => {
|
||||
setupHookSpies()
|
||||
mockParseImageDimensions.mockReset()
|
||||
mockCalculateTargetDimensions.mockReset()
|
||||
mockResizeImage.mockReset()
|
||||
@@ -59,6 +61,17 @@ describe("createReadImageResizerHook", () => {
|
||||
mockGetSessionModel.mockReturnValue({ providerID: "anthropic", modelID: "claude-sonnet-4-6" })
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
parseImageDimensionsSpy?.mockRestore()
|
||||
calculateTargetDimensionsSpy?.mockRestore()
|
||||
resizeImageSpy?.mockRestore()
|
||||
getSessionModelSpy?.mockRestore()
|
||||
parseImageDimensionsSpy = undefined
|
||||
calculateTargetDimensionsSpy = undefined
|
||||
resizeImageSpy = undefined
|
||||
getSessionModelSpy = undefined
|
||||
})
|
||||
|
||||
it("skips non-Read tools", async () => {
|
||||
//#given
|
||||
const hook = createReadImageResizerHook(createMockContext())
|
||||
|
||||
@@ -22,6 +22,7 @@ afterAll(() => {
|
||||
})
|
||||
|
||||
const { recoverToolResultMissing } = await import("./recover-tool-result-missing")
|
||||
mock.restore()
|
||||
|
||||
function createMockClient(messages: MessageData[] = []) {
|
||||
const promptAsync = mock(() => Promise.resolve({}))
|
||||
|
||||
Reference in New Issue
Block a user