382f9b61fa
- message-builder.test.ts: improve mock isolation
- background-update-check.ts: update for test compatibility
- execute-http-hook-security.test.ts: narrow mock targets
- recover-tool-result-missing.test.ts: prevent barrel contamination
🤖 GENERATED WITH ASSISTANCE OF OhMyOpenCode
108 lines
3.6 KiB
TypeScript
108 lines
3.6 KiB
TypeScript
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test"
|
|
|
|
const replaceEmptyTextPartsAsync = mock(() => Promise.resolve(false))
|
|
const injectTextPartAsync = mock(() => Promise.resolve(false))
|
|
const findMessagesWithEmptyTextPartsFromSDK = mock(() => Promise.resolve([] as string[]))
|
|
|
|
mock.module("../../shared/normalize-sdk-response", () => ({
|
|
normalizeSDKResponse: (response: { data?: unknown[] }) => response.data ?? [],
|
|
}))
|
|
|
|
mock.module("../../shared/logger", () => ({
|
|
log: () => {},
|
|
}))
|
|
|
|
mock.module("../../shared/opencode-storage-detection", () => ({
|
|
isSqliteBackend: () => true,
|
|
}))
|
|
|
|
mock.module("../session-recovery/storage", () => ({
|
|
findEmptyMessages: () => [],
|
|
findMessagesWithEmptyTextParts: () => [],
|
|
injectTextPart: () => false,
|
|
replaceEmptyTextParts: () => false,
|
|
}))
|
|
|
|
mock.module("../session-recovery/storage/empty-text", () => ({
|
|
replaceEmptyTextPartsAsync,
|
|
findMessagesWithEmptyTextPartsFromSDK,
|
|
}))
|
|
|
|
mock.module("../session-recovery/storage/text-part-injector", () => ({
|
|
injectTextPartAsync,
|
|
}))
|
|
|
|
async function importFreshMessageBuilder(): Promise<typeof import("./message-builder")> {
|
|
const module = await import(`./message-builder?test=${Date.now()}-${Math.random()}`)
|
|
mock.restore()
|
|
return module
|
|
}
|
|
|
|
afterAll(() => {
|
|
mock.restore()
|
|
})
|
|
|
|
describe("sanitizeEmptyMessagesBeforeSummarize", () => {
|
|
beforeEach(() => {
|
|
replaceEmptyTextPartsAsync.mockReset()
|
|
replaceEmptyTextPartsAsync.mockResolvedValue(false)
|
|
injectTextPartAsync.mockReset()
|
|
injectTextPartAsync.mockResolvedValue(false)
|
|
findMessagesWithEmptyTextPartsFromSDK.mockReset()
|
|
findMessagesWithEmptyTextPartsFromSDK.mockResolvedValue([])
|
|
})
|
|
|
|
test("#given sqlite message with tool content and empty text part #when sanitizing #then it fixes the mixed-content message", async () => {
|
|
const { sanitizeEmptyMessagesBeforeSummarize, PLACEHOLDER_TEXT } = await importFreshMessageBuilder()
|
|
const client = {
|
|
session: {
|
|
messages: mock(() => Promise.resolve({
|
|
data: [
|
|
{
|
|
info: { id: "msg-1" },
|
|
parts: [
|
|
{ type: "tool_result", text: "done" },
|
|
{ type: "text", text: "" },
|
|
],
|
|
},
|
|
],
|
|
})),
|
|
},
|
|
} as never
|
|
findMessagesWithEmptyTextPartsFromSDK.mockResolvedValue(["msg-1"])
|
|
replaceEmptyTextPartsAsync.mockResolvedValue(true)
|
|
|
|
const fixedCount = await sanitizeEmptyMessagesBeforeSummarize("ses-1", client)
|
|
|
|
expect(fixedCount).toBe(1)
|
|
expect(replaceEmptyTextPartsAsync).toHaveBeenCalledWith(client, "ses-1", "msg-1", PLACEHOLDER_TEXT)
|
|
expect(injectTextPartAsync).not.toHaveBeenCalled()
|
|
})
|
|
|
|
test("#given sqlite message with mixed content and failed replacement #when sanitizing #then it injects the placeholder text part", async () => {
|
|
const { sanitizeEmptyMessagesBeforeSummarize, PLACEHOLDER_TEXT } = await importFreshMessageBuilder()
|
|
const client = {
|
|
session: {
|
|
messages: mock(() => Promise.resolve({
|
|
data: [
|
|
{
|
|
info: { id: "msg-2" },
|
|
parts: [
|
|
{ type: "tool_use", text: "call" },
|
|
{ type: "text", text: "" },
|
|
],
|
|
},
|
|
],
|
|
})),
|
|
},
|
|
} as never
|
|
findMessagesWithEmptyTextPartsFromSDK.mockResolvedValue(["msg-2"])
|
|
injectTextPartAsync.mockResolvedValue(true)
|
|
|
|
const fixedCount = await sanitizeEmptyMessagesBeforeSummarize("ses-2", client)
|
|
|
|
expect(fixedCount).toBe(1)
|
|
expect(injectTextPartAsync).toHaveBeenCalledWith(client, "ses-2", "msg-2", PLACEHOLDER_TEXT)
|
|
})
|
|
})
|