test: make unsafe test coercion explicit
Move test coercion out of a hidden global and require each test to import the helper so review tools and runtime scripts can see the unsafe boundary. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -5,6 +5,7 @@ import { executeCompact } from "./executor"
|
||||
import type { AutoCompactState } from "./types"
|
||||
import * as recoveryStrategy from "./recovery-strategy"
|
||||
import * as messagesReader from "../session-recovery/storage/messages-reader"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
type TimerCallback = (...args: any[]) => void
|
||||
|
||||
@@ -37,7 +38,7 @@ function createFakeTimeouts(): FakeTimeouts {
|
||||
callback,
|
||||
args,
|
||||
})
|
||||
return testCoerce<ReturnType<typeof setTimeout>>(id)
|
||||
return unsafeTestValue<ReturnType<typeof setTimeout>>(id)
|
||||
}) as typeof setTimeout
|
||||
|
||||
globalThis.clearTimeout = ((id?: number) => {
|
||||
@@ -243,7 +244,7 @@ describe("executeCompact lock management", () => {
|
||||
await executeCompact(sessionID, msg, autoCompactState, mockClient, directory, pluginConfig)
|
||||
|
||||
// then: Toast should be shown
|
||||
const toastCalls = (testCoerce(mockClient.tui.showToast)).mock.calls
|
||||
const toastCalls = (unsafeTestValue(mockClient.tui.showToast)).mock.calls
|
||||
const blockedToast = toastCalls.find(
|
||||
(call: any) => call[0]?.body?.title === "Compact In Progress",
|
||||
)
|
||||
@@ -276,7 +277,7 @@ describe("executeCompact lock management", () => {
|
||||
await executeCompact(sessionID, msg, autoCompactState, mockClient, directory, pluginConfig)
|
||||
|
||||
// then: Should show failure toast
|
||||
const toastCalls = (testCoerce(mockClient.tui.showToast)).mock.calls
|
||||
const toastCalls = (unsafeTestValue(mockClient.tui.showToast)).mock.calls
|
||||
const failureToast = toastCalls.find(
|
||||
(call: any) => call[0]?.body?.title === "Auto Compact Failed",
|
||||
)
|
||||
|
||||
@@ -2,6 +2,7 @@ import { describe, test, expect, mock, beforeEach, afterAll } from "bun:test"
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import type { ExperimentalConfig } from "../../config"
|
||||
import * as originalDeduplicationRecovery from "./deduplication-recovery"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
const attemptDeduplicationRecoveryMock = mock(async () => {})
|
||||
|
||||
@@ -20,7 +21,7 @@ function createImmediateTimeouts(): () => void {
|
||||
|
||||
globalThis.setTimeout = ((callback: (...args: unknown[]) => void, _delay?: number, ...args: unknown[]) => {
|
||||
callback(...args)
|
||||
return testCoerce<ReturnType<typeof setTimeout>>(0)
|
||||
return unsafeTestValue<ReturnType<typeof setTimeout>>(0)
|
||||
}) as typeof setTimeout
|
||||
|
||||
globalThis.clearTimeout = ((_: ReturnType<typeof setTimeout>) => {}) as typeof clearTimeout
|
||||
|
||||
@@ -2,6 +2,7 @@ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test"
|
||||
import { runSummarizeRetryStrategy } from "./summarize-retry-strategy"
|
||||
import type { AutoCompactState, ParsedTokenLimitError, RetryState } from "./types"
|
||||
import type { OhMyOpenCodeConfig } from "../../config"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
type TimeoutCall = {
|
||||
handle: ReturnType<typeof setTimeout>
|
||||
@@ -95,7 +96,7 @@ describe("runSummarizeRetryStrategy", () => {
|
||||
//#given
|
||||
const timeoutCalls: TimeoutCall[] = []
|
||||
globalThis.setTimeout = ((_: (...args: unknown[]) => void, delay?: number) => {
|
||||
const handle = testCoerce<ReturnType<typeof setTimeout>>(timeoutCalls.length + 1)
|
||||
const handle = unsafeTestValue<ReturnType<typeof setTimeout>>(timeoutCalls.length + 1)
|
||||
timeoutCalls.push({ handle, delay: delay ?? 0 })
|
||||
return handle
|
||||
}) as typeof setTimeout
|
||||
@@ -132,7 +133,7 @@ describe("runSummarizeRetryStrategy", () => {
|
||||
let scheduledCallback: (() => void) | undefined
|
||||
globalThis.setTimeout = ((callback: (...args: unknown[]) => void, _delay?: number) => {
|
||||
scheduledCallback = () => callback()
|
||||
return testCoerce<ReturnType<typeof setTimeout>>(1)
|
||||
return unsafeTestValue<ReturnType<typeof setTimeout>>(1)
|
||||
}) as typeof setTimeout
|
||||
|
||||
autoCompactState.pendingCompact.add(sessionID)
|
||||
@@ -176,7 +177,7 @@ describe("runSummarizeRetryStrategy", () => {
|
||||
autoCompactState.emptyContentAttemptBySession.set(sessionID, 3)
|
||||
autoCompactState.retryTimerBySession.set(
|
||||
sessionID,
|
||||
testCoerce<ReturnType<typeof setTimeout>>(1),
|
||||
unsafeTestValue<ReturnType<typeof setTimeout>>(1),
|
||||
)
|
||||
|
||||
//#when
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import { createAtlasHook } from "./atlas-hook"
|
||||
import { clearBoulderState, writeBoulderState } from "../../features/boulder-state"
|
||||
import { _resetForTesting, clearSessionAgent, registerAgentName, setSessionAgent } from "../../features/claude-code-session-state"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
// Force process isolation in CI runner (globalThis.setTimeout override conflicts with other atlas tests)
|
||||
mock.module("../../shared/opencode-storage-detection", () => ({
|
||||
@@ -79,7 +80,7 @@ describe("atlas background task retry", () => {
|
||||
callback: () => (callback as LongTimerCallback)(...args),
|
||||
cleared: false,
|
||||
})
|
||||
return testCoerce<ReturnType<typeof setTimeout>>(id)
|
||||
return unsafeTestValue<ReturnType<typeof setTimeout>>(id)
|
||||
}
|
||||
|
||||
return originalSetTimeout(callback, delay, ...args)
|
||||
@@ -120,7 +121,7 @@ describe("atlas background task retry", () => {
|
||||
|
||||
let backgroundRunning = true
|
||||
const promptMock = mock(async () => ({}))
|
||||
const hook = createAtlasHook(testCoerce<PluginInput>({
|
||||
const hook = createAtlasHook(unsafeTestValue<PluginInput>({
|
||||
directory: testDir,
|
||||
client: {
|
||||
session: {
|
||||
@@ -130,7 +131,7 @@ describe("atlas background task retry", () => {
|
||||
},
|
||||
}), {
|
||||
directory: testDir,
|
||||
backgroundManager: testCoerce<NonNullable<Parameters<typeof createAtlasHook>[1]>["backgroundManager"] & {
|
||||
backgroundManager: unsafeTestValue<NonNullable<Parameters<typeof createAtlasHook>[1]>["backgroundManager"] & {
|
||||
getTasksByParentSession: (sessionID: string) => Array<{ status: string }>
|
||||
}>({
|
||||
getTasksByParentSession: () => backgroundRunning ? [{ status: "running" }] : [],
|
||||
@@ -161,7 +162,7 @@ describe("atlas background task retry", () => {
|
||||
|
||||
let backgroundRunning = true
|
||||
const promptMock = mock(async () => ({}))
|
||||
const hook = createAtlasHook(testCoerce<PluginInput>({
|
||||
const hook = createAtlasHook(unsafeTestValue<PluginInput>({
|
||||
directory: testDir,
|
||||
client: {
|
||||
session: {
|
||||
@@ -171,7 +172,7 @@ describe("atlas background task retry", () => {
|
||||
},
|
||||
}), {
|
||||
directory: testDir,
|
||||
backgroundManager: testCoerce<NonNullable<Parameters<typeof createAtlasHook>[1]>["backgroundManager"] & {
|
||||
backgroundManager: unsafeTestValue<NonNullable<Parameters<typeof createAtlasHook>[1]>["backgroundManager"] & {
|
||||
getTasksByParentSession: (sessionID: string) => Array<{ status: string }>
|
||||
}>({
|
||||
getTasksByParentSession: () => backgroundRunning ? [{ status: "running" }] : [],
|
||||
@@ -204,7 +205,7 @@ describe("atlas background task retry", () => {
|
||||
|
||||
let remainingRunningRetries = 2
|
||||
const promptMock = mock(async () => ({}))
|
||||
const hook = createAtlasHook(testCoerce<PluginInput>({
|
||||
const hook = createAtlasHook(unsafeTestValue<PluginInput>({
|
||||
directory: testDir,
|
||||
client: {
|
||||
session: {
|
||||
@@ -214,7 +215,7 @@ describe("atlas background task retry", () => {
|
||||
},
|
||||
}), {
|
||||
directory: testDir,
|
||||
backgroundManager: testCoerce<NonNullable<Parameters<typeof createAtlasHook>[1]>["backgroundManager"] & {
|
||||
backgroundManager: unsafeTestValue<NonNullable<Parameters<typeof createAtlasHook>[1]>["backgroundManager"] & {
|
||||
getTasksByParentSession: (sessionID: string) => Array<{ status: string }>
|
||||
}>({
|
||||
getTasksByParentSession: () => {
|
||||
@@ -258,7 +259,7 @@ describe("atlas background task retry", () => {
|
||||
const promptAsyncMock = mock(async () => ({}))
|
||||
let backgroundCheckCount = 0
|
||||
|
||||
const hook = createAtlasHook(testCoerce<PluginInput>({
|
||||
const hook = createAtlasHook(unsafeTestValue<PluginInput>({
|
||||
directory: testDir,
|
||||
client: {
|
||||
session: {
|
||||
@@ -268,7 +269,7 @@ describe("atlas background task retry", () => {
|
||||
},
|
||||
}), {
|
||||
directory: testDir,
|
||||
backgroundManager: testCoerce<NonNullable<Parameters<typeof createAtlasHook>[1]>["backgroundManager"] & {
|
||||
backgroundManager: unsafeTestValue<NonNullable<Parameters<typeof createAtlasHook>[1]>["backgroundManager"] & {
|
||||
getTasksByParentSession: (sessionID: string) => Array<{ status: string }>
|
||||
}>({
|
||||
getTasksByParentSession: () => {
|
||||
@@ -313,7 +314,7 @@ describe("atlas background task retry", () => {
|
||||
|
||||
let backgroundRunning = true
|
||||
const promptAsyncMock = mock(async () => ({}))
|
||||
const hook = createAtlasHook(testCoerce<PluginInput>({
|
||||
const hook = createAtlasHook(unsafeTestValue<PluginInput>({
|
||||
directory: testDir,
|
||||
client: {
|
||||
session: {
|
||||
@@ -323,7 +324,7 @@ describe("atlas background task retry", () => {
|
||||
},
|
||||
}), {
|
||||
directory: testDir,
|
||||
backgroundManager: testCoerce<NonNullable<Parameters<typeof createAtlasHook>[1]>["backgroundManager"] & {
|
||||
backgroundManager: unsafeTestValue<NonNullable<Parameters<typeof createAtlasHook>[1]>["backgroundManager"] & {
|
||||
getTasksByParentSession: (sessionID: string) => Array<{ status: string }>
|
||||
}>({
|
||||
getTasksByParentSession: () => backgroundRunning ? [{ status: "running" }] : [],
|
||||
@@ -366,7 +367,7 @@ describe("atlas background task retry", () => {
|
||||
let backgroundRunning = true
|
||||
let descendantAgent = "atlas"
|
||||
const promptAsyncMock = mock(async () => ({}))
|
||||
const hook = createAtlasHook(testCoerce<PluginInput>({
|
||||
const hook = createAtlasHook(unsafeTestValue<PluginInput>({
|
||||
directory: testDir,
|
||||
client: {
|
||||
session: {
|
||||
@@ -386,7 +387,7 @@ describe("atlas background task retry", () => {
|
||||
},
|
||||
}), {
|
||||
directory: testDir,
|
||||
backgroundManager: testCoerce<NonNullable<Parameters<typeof createAtlasHook>[1]>["backgroundManager"] & {
|
||||
backgroundManager: unsafeTestValue<NonNullable<Parameters<typeof createAtlasHook>[1]>["backgroundManager"] & {
|
||||
getTasksByParentSession: (sessionID: string) => Array<{ status: string }>
|
||||
}>({
|
||||
getTasksByParentSession: (currentSessionID: string) => {
|
||||
@@ -424,7 +425,7 @@ describe("atlas background task retry", () => {
|
||||
|
||||
const deferredPrompt = createDeferred<{}>()
|
||||
const promptAsyncMock = mock(() => deferredPrompt.promise)
|
||||
const hook = createAtlasHook(testCoerce<PluginInput>({
|
||||
const hook = createAtlasHook(unsafeTestValue<PluginInput>({
|
||||
directory: testDir,
|
||||
client: {
|
||||
session: {
|
||||
@@ -462,7 +463,7 @@ describe("atlas background task retry", () => {
|
||||
promptAsyncMock.mockImplementationOnce(() => deferredPrompt.promise)
|
||||
promptAsyncMock.mockImplementationOnce(async () => ({}))
|
||||
|
||||
const hook = createAtlasHook(testCoerce<PluginInput>({
|
||||
const hook = createAtlasHook(unsafeTestValue<PluginInput>({
|
||||
directory: testDir,
|
||||
client: {
|
||||
session: {
|
||||
@@ -472,7 +473,7 @@ describe("atlas background task retry", () => {
|
||||
},
|
||||
}), {
|
||||
directory: testDir,
|
||||
backgroundManager: testCoerce<NonNullable<Parameters<typeof createAtlasHook>[1]>["backgroundManager"] & {
|
||||
backgroundManager: unsafeTestValue<NonNullable<Parameters<typeof createAtlasHook>[1]>["backgroundManager"] & {
|
||||
getTasksByParentSession: (sessionID: string) => Array<{ status: string }>
|
||||
}>({
|
||||
getTasksByParentSession: () => [],
|
||||
@@ -515,7 +516,7 @@ describe("atlas background task retry", () => {
|
||||
})
|
||||
promptAsyncMock.mockImplementationOnce(async () => ({}))
|
||||
|
||||
const hook = createAtlasHook(testCoerce<PluginInput>({
|
||||
const hook = createAtlasHook(unsafeTestValue<PluginInput>({
|
||||
directory: testDir,
|
||||
client: {
|
||||
session: {
|
||||
@@ -525,7 +526,7 @@ describe("atlas background task retry", () => {
|
||||
},
|
||||
}), {
|
||||
directory: testDir,
|
||||
backgroundManager: testCoerce<NonNullable<Parameters<typeof createAtlasHook>[1]>["backgroundManager"] & {
|
||||
backgroundManager: unsafeTestValue<NonNullable<Parameters<typeof createAtlasHook>[1]>["backgroundManager"] & {
|
||||
getTasksByParentSession: (sessionID: string) => Array<{ status: string }>
|
||||
}>({
|
||||
getTasksByParentSession: () => backgroundRunning ? [{ status: "running" }] : [],
|
||||
|
||||
@@ -2,6 +2,7 @@ import { describe, test, expect, beforeEach, afterEach, mock } from "bun:test"
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import { registerAgentName, _resetForTesting } from "../../features/claude-code-session-state"
|
||||
import { injectBoulderContinuation } from "./boulder-continuation-injector"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
describe("injectBoulderContinuation", () => {
|
||||
beforeEach(() => {
|
||||
@@ -20,7 +21,7 @@ describe("injectBoulderContinuation", () => {
|
||||
const promptAsyncMock = mock(async (_request: unknown) => undefined)
|
||||
const messagesMock = mock(async () => ({ data: [] }))
|
||||
|
||||
const ctx = testCoerce<PluginInput>({
|
||||
const ctx = unsafeTestValue<PluginInput>({
|
||||
directory: "/tmp",
|
||||
client: {
|
||||
session: {
|
||||
@@ -60,7 +61,7 @@ describe("injectBoulderContinuation", () => {
|
||||
const messagesMock = mock(async () => ({ data: [] }))
|
||||
const sessionState = { promptFailureCount: 2, lastContinuationInjectedAt: 123 }
|
||||
|
||||
const ctx = testCoerce<PluginInput>({
|
||||
const ctx = unsafeTestValue<PluginInput>({
|
||||
directory: "/tmp",
|
||||
client: {
|
||||
session: {
|
||||
@@ -78,7 +79,7 @@ describe("injectBoulderContinuation", () => {
|
||||
remaining: 1,
|
||||
total: 2,
|
||||
agent: "atlas",
|
||||
backgroundManager: testCoerce<Parameters<typeof injectBoulderContinuation>[0]["backgroundManager"]>({
|
||||
backgroundManager: unsafeTestValue<Parameters<typeof injectBoulderContinuation>[0]["backgroundManager"]>({
|
||||
getTasksByParentSession: () => [{ status: "running" }],
|
||||
}),
|
||||
sessionState,
|
||||
@@ -98,7 +99,7 @@ describe("injectBoulderContinuation", () => {
|
||||
const messagesMock = mock(async () => ({ data: [] }))
|
||||
const sessionState = { promptFailureCount: 1, lastContinuationInjectedAt: 456 }
|
||||
|
||||
const ctx = testCoerce<PluginInput>({
|
||||
const ctx = unsafeTestValue<PluginInput>({
|
||||
directory: "/tmp",
|
||||
client: {
|
||||
session: {
|
||||
@@ -116,7 +117,7 @@ describe("injectBoulderContinuation", () => {
|
||||
remaining: 1,
|
||||
total: 2,
|
||||
agent: "atlas",
|
||||
backgroundManager: testCoerce<Parameters<typeof injectBoulderContinuation>[0]["backgroundManager"]>({
|
||||
backgroundManager: unsafeTestValue<Parameters<typeof injectBoulderContinuation>[0]["backgroundManager"]>({
|
||||
getTasksByParentSession: () => [{ status: "pending" }],
|
||||
}),
|
||||
sessionState,
|
||||
@@ -134,7 +135,7 @@ describe("injectBoulderContinuation", () => {
|
||||
const promptAsyncMock = mock(async (_request: unknown) => undefined)
|
||||
const messagesMock = mock(async () => ({ data: [] }))
|
||||
|
||||
const ctx = testCoerce<PluginInput>({
|
||||
const ctx = unsafeTestValue<PluginInput>({
|
||||
directory: "/tmp",
|
||||
client: {
|
||||
session: {
|
||||
@@ -189,7 +190,7 @@ describe("injectBoulderContinuation", () => {
|
||||
}],
|
||||
}))
|
||||
|
||||
const ctx = testCoerce<PluginInput>({
|
||||
const ctx = unsafeTestValue<PluginInput>({
|
||||
directory: "/tmp",
|
||||
client: {
|
||||
session: {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs"
|
||||
import { tmpdir } from "node:os"
|
||||
import { join } from "node:path"
|
||||
import { clearBoulderState, readBoulderState, writeBoulderState } from "../../features/boulder-state"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
const { createAtlasHook } = await import("./index")
|
||||
|
||||
@@ -49,7 +50,7 @@ describe("atlas hook idle-event complete boulder", () => {
|
||||
},
|
||||
})
|
||||
|
||||
const hook = createAtlasHook(testCoerce<Parameters<typeof createAtlasHook>[0]>({
|
||||
const hook = createAtlasHook(unsafeTestValue<Parameters<typeof createAtlasHook>[0]>({
|
||||
directory: testDirectory,
|
||||
client: {
|
||||
session: {
|
||||
|
||||
@@ -7,6 +7,7 @@ import { join } from "node:path"
|
||||
import { clearBoulderState, readBoulderState, writeBoulderState } from "../../features/boulder-state"
|
||||
import type { BoulderState } from "../../features/boulder-state"
|
||||
import { _resetForTesting, registerAgentName, setSessionAgent, subagentSessions } from "../../features/claude-code-session-state"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
const { createAtlasHook } = await import("./index")
|
||||
|
||||
@@ -32,7 +33,7 @@ describe("atlas hook idle-event session lineage", () => {
|
||||
}
|
||||
|
||||
function createHook(parentSessionIDs?: Record<string, string | undefined>) {
|
||||
return createAtlasHook(testCoerce<Parameters<typeof createAtlasHook>[0]>({
|
||||
return createAtlasHook(unsafeTestValue<Parameters<typeof createAtlasHook>[0]>({
|
||||
directory: testDirectory,
|
||||
client: {
|
||||
session: {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { randomUUID } from "node:crypto"
|
||||
import { clearBoulderState, readBoulderState, writeBoulderState } from "../../features/boulder-state"
|
||||
import { _resetForTesting, registerAgentName } from "../../features/claude-code-session-state"
|
||||
import type { BoulderState } from "../../features/boulder-state"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
const TEST_STORAGE_ROOT = join(tmpdir(), `atlas-persisted-lineage-storage-${randomUUID()}`)
|
||||
const TEST_MESSAGE_STORAGE = join(TEST_STORAGE_ROOT, "message")
|
||||
@@ -58,7 +59,7 @@ describe("atlas hook idle-event persisted lineage", () => {
|
||||
parentSessionIDs?: Record<string, string | undefined>,
|
||||
messagesBySession?: Record<string, Array<{ info: { agent: string; providerID: string; modelID: string } }>>,
|
||||
) {
|
||||
return createAtlasHook(testCoerce<Parameters<typeof createAtlasHook>[0]>({
|
||||
return createAtlasHook(unsafeTestValue<Parameters<typeof createAtlasHook>[0]>({
|
||||
directory: testDirectory,
|
||||
client: {
|
||||
session: {
|
||||
@@ -173,7 +174,7 @@ describe("atlas hook idle-event persisted lineage", () => {
|
||||
},
|
||||
})
|
||||
|
||||
const hook = createAtlasHook(testCoerce<Parameters<typeof createAtlasHook>[0]>({
|
||||
const hook = createAtlasHook(unsafeTestValue<Parameters<typeof createAtlasHook>[0]>({
|
||||
directory: testDirectory,
|
||||
client: {
|
||||
session: {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { createBoulderState, readBoulderState, writeBoulderState } from "../../f
|
||||
import { _resetForTesting, registerAgentName } from "../../features/claude-code-session-state"
|
||||
import { handleAtlasSessionIdle } from "./idle-event"
|
||||
import type { SessionState } from "./types"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
describe("handleAtlasSessionIdle completion nudge", () => {
|
||||
const SESSION_ID = "session-main-1"
|
||||
@@ -76,7 +77,7 @@ describe("handleAtlasSessionIdle completion nudge", () => {
|
||||
return { data: {} }
|
||||
})
|
||||
|
||||
const ctx = testCoerce<PluginInput>({
|
||||
const ctx = unsafeTestValue<PluginInput>({
|
||||
directory: testDirectory,
|
||||
client: {
|
||||
session: {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { describe, expect, mock, test } from "bun:test"
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import { resolveRecentPromptContextForSession } from "./recent-model-resolver"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
describe("resolveRecentPromptContextForSession", () => {
|
||||
test("uses message time.created rather than SDK array order for recent prompt context", async () => {
|
||||
// given
|
||||
const ctx = testCoerce<PluginInput>({
|
||||
const ctx = unsafeTestValue<PluginInput>({
|
||||
client: {
|
||||
session: {
|
||||
messages: mock(async () => ({
|
||||
|
||||
@@ -8,6 +8,7 @@ import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import type { Project } from "@opencode-ai/sdk"
|
||||
import { readBoulderState, writeBoulderState } from "../../features/boulder-state"
|
||||
import { createToolExecuteBeforeHandler } from "./tool-execute-before"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
const isCallerOrchestratorMock = mock(async () => true)
|
||||
const collectGitDiffStatsMock = mock(() => ({
|
||||
@@ -80,7 +81,7 @@ describe("createToolExecuteAfterHandler background launch detection", () => {
|
||||
|
||||
function createHandler(parentSessionIDs?: Record<string, string | undefined>) {
|
||||
const project = createProject()
|
||||
const client = testCoerce<PluginInput["client"]>({
|
||||
const client = unsafeTestValue<PluginInput["client"]>({
|
||||
session: {
|
||||
get: async (input: SessionGetInput) => createSessionGetResult(parentSessionIDs?.[input.path.id]),
|
||||
},
|
||||
@@ -141,7 +142,7 @@ describe("createToolExecuteAfterHandler background launch detection", () => {
|
||||
const childSessionID = "ses_child123"
|
||||
const planPath = join(testDirectory, "background-launch-plan.md")
|
||||
const project = createProject()
|
||||
const client = testCoerce<PluginInput["client"]>({
|
||||
const client = unsafeTestValue<PluginInput["client"]>({
|
||||
session: {
|
||||
get: async () => createSessionGetResult(undefined),
|
||||
},
|
||||
@@ -215,7 +216,7 @@ describe("createToolExecuteAfterHandler background launch detection", () => {
|
||||
const childSessionID = "ses_child_lookup_failure"
|
||||
const planPath = join(testDirectory, "background-launch-plan.md")
|
||||
const project = createProject()
|
||||
const client = testCoerce<PluginInput["client"]>({
|
||||
const client = unsafeTestValue<PluginInput["client"]>({
|
||||
session: {
|
||||
get: async () => createSessionGetResult(undefined),
|
||||
},
|
||||
@@ -288,7 +289,7 @@ describe("createToolExecuteAfterHandler background launch detection", () => {
|
||||
const childSessionID = "ses_outside_lineage"
|
||||
const planPath = join(testDirectory, "background-launch-plan.md")
|
||||
const project = createProject()
|
||||
const client = testCoerce<PluginInput["client"]>({
|
||||
const client = unsafeTestValue<PluginInput["client"]>({
|
||||
session: {
|
||||
get: async () => createSessionGetResult(undefined),
|
||||
},
|
||||
@@ -358,7 +359,7 @@ describe("createToolExecuteAfterHandler background launch detection", () => {
|
||||
const childSessionID = "ses_unrelated_child"
|
||||
const planPath = join(testDirectory, "background-launch-plan.md")
|
||||
const project = createProject()
|
||||
const client = testCoerce<PluginInput["client"]>({
|
||||
const client = unsafeTestValue<PluginInput["client"]>({
|
||||
session: {
|
||||
get: async () => createSessionGetResult(undefined),
|
||||
},
|
||||
@@ -431,7 +432,7 @@ describe("createToolExecuteAfterHandler background launch detection", () => {
|
||||
const planPathA = join(testDirectory, "background-launch-work-a.md")
|
||||
const planPathB = join(testDirectory, "background-launch-work-b.md")
|
||||
const project = createProject()
|
||||
const client = testCoerce<PluginInput["client"]>({
|
||||
const client = unsafeTestValue<PluginInput["client"]>({
|
||||
session: {
|
||||
get: async () => createSessionGetResult(undefined),
|
||||
},
|
||||
|
||||
@@ -15,7 +15,7 @@ mock.module("../constants", () => ({
|
||||
const current = mockState.candidates
|
||||
// Forward array methods/properties to the mutable candidates list
|
||||
// so getCachedVersion's `for (... of ...)` sees fresh data per test.
|
||||
const value = (testCoerce<Record<PropertyKey, unknown>>(current))[prop]
|
||||
const value = (unsafeTestValue<Record<PropertyKey, unknown>>(current))[prop]
|
||||
if (typeof value === "function") {
|
||||
return (value as (...args: unknown[]) => unknown).bind(current)
|
||||
}
|
||||
@@ -29,6 +29,7 @@ mock.module("./package-json-locator", () => ({
|
||||
}))
|
||||
|
||||
import { getCachedVersion } from "./cached-version"
|
||||
import { unsafeTestValue } from "../../../../test-support/unsafe-test-value"
|
||||
|
||||
describe("getCachedVersion (GH-3257)", () => {
|
||||
let cacheRoot: string
|
||||
|
||||
@@ -3,6 +3,7 @@ import { createCategorySkillReminderHook } from "./index"
|
||||
import { updateSessionAgent, clearSessionAgent, _resetForTesting } from "../../features/claude-code-session-state"
|
||||
import type { AvailableSkill } from "../../agents/dynamic-agent-prompt-builder"
|
||||
import * as sharedModule from "../../shared"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
describe("category-skill-reminder hook", () => {
|
||||
let logCalls: Array<{ msg: string; data?: unknown }>
|
||||
@@ -21,7 +22,7 @@ describe("category-skill-reminder hook", () => {
|
||||
})
|
||||
|
||||
function createMockPluginInput() {
|
||||
return testCoerce({
|
||||
return unsafeTestValue({
|
||||
client: {
|
||||
tui: {
|
||||
showToast: async () => {},
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { describe, it, expect, mock, beforeEach, afterEach } from "bun:test"
|
||||
import type { HookHttp } from "./types"
|
||||
import * as sharedModule from "../../shared"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
const mockFetch = mock(() =>
|
||||
Promise.resolve(new Response(JSON.stringify({}), { status: 200 }))
|
||||
@@ -31,7 +32,7 @@ describe("executeHttpHook TLS security", () => {
|
||||
let logCalls: Array<{ message: string; data?: unknown }>
|
||||
|
||||
beforeEach(() => {
|
||||
globalThis.fetch = testCoerce<typeof fetch>(mockFetch)
|
||||
globalThis.fetch = unsafeTestValue<typeof fetch>(mockFetch)
|
||||
mockFetch.mockReset()
|
||||
mockFetch.mockImplementation(() =>
|
||||
Promise.resolve(new Response(JSON.stringify({}), { status: 200 }))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { describe, it, expect, mock, beforeEach, afterEach } from "bun:test"
|
||||
import type { HookHttp } from "./types"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
const mockFetch = mock(() =>
|
||||
Promise.resolve(new Response(JSON.stringify({}), { status: 200 }))
|
||||
@@ -9,7 +10,7 @@ const originalFetch = globalThis.fetch
|
||||
|
||||
describe("executeHttpHook", () => {
|
||||
beforeEach(() => {
|
||||
globalThis.fetch = testCoerce<typeof fetch>(mockFetch)
|
||||
globalThis.fetch = unsafeTestValue<typeof fetch>(mockFetch)
|
||||
mockFetch.mockReset()
|
||||
mockFetch.mockImplementation(() =>
|
||||
Promise.resolve(new Response(JSON.stringify({}), { status: 200 }))
|
||||
@@ -33,7 +34,7 @@ describe("executeHttpHook", () => {
|
||||
await executeHttpHook(hook, stdinData)
|
||||
|
||||
expect(mockFetch).toHaveBeenCalledTimes(1)
|
||||
const [url, options] = testCoerce<[string, RequestInit]>(mockFetch.mock.calls[0])
|
||||
const [url, options] = unsafeTestValue<[string, RequestInit]>(mockFetch.mock.calls[0])
|
||||
expect(url).toBe("http://localhost:8080/hooks/pre-tool-use")
|
||||
expect(options.method).toBe("POST")
|
||||
expect(options.body).toBe(stdinData)
|
||||
@@ -44,7 +45,7 @@ describe("executeHttpHook", () => {
|
||||
|
||||
await executeHttpHook(hook, stdinData)
|
||||
|
||||
const [, options] = testCoerce<[string, RequestInit]>(mockFetch.mock.calls[0])
|
||||
const [, options] = unsafeTestValue<[string, RequestInit]>(mockFetch.mock.calls[0])
|
||||
const headers = options.headers as Record<string, string>
|
||||
expect(headers["Content-Type"]).toBe("application/json")
|
||||
})
|
||||
@@ -72,7 +73,7 @@ describe("executeHttpHook", () => {
|
||||
|
||||
await executeHttpHook(hook, "{}")
|
||||
|
||||
const [, options] = testCoerce<[string, RequestInit]>(mockFetch.mock.calls[0])
|
||||
const [, options] = unsafeTestValue<[string, RequestInit]>(mockFetch.mock.calls[0])
|
||||
const headers = options.headers as Record<string, string>
|
||||
expect(headers["Authorization"]).toBe("Bearer secret-123")
|
||||
})
|
||||
@@ -88,7 +89,7 @@ describe("executeHttpHook", () => {
|
||||
|
||||
await executeHttpHook(hook, "{}")
|
||||
|
||||
const [, options] = testCoerce<[string, RequestInit]>(mockFetch.mock.calls[0])
|
||||
const [, options] = unsafeTestValue<[string, RequestInit]>(mockFetch.mock.calls[0])
|
||||
const headers = options.headers as Record<string, string>
|
||||
expect(headers["Authorization"]).toBe("Bearer secret-123")
|
||||
})
|
||||
@@ -104,7 +105,7 @@ describe("executeHttpHook", () => {
|
||||
|
||||
await executeHttpHook(hook, "{}")
|
||||
|
||||
const [, options] = testCoerce<[string, RequestInit]>(mockFetch.mock.calls[0])
|
||||
const [, options] = unsafeTestValue<[string, RequestInit]>(mockFetch.mock.calls[0])
|
||||
const headers = options.headers as Record<string, string>
|
||||
expect(headers["Authorization"]).toBe("Bearer ")
|
||||
})
|
||||
@@ -121,7 +122,7 @@ describe("executeHttpHook", () => {
|
||||
|
||||
await executeHttpHook(hook, "{}")
|
||||
|
||||
const [, options] = testCoerce<[string, RequestInit]>(mockFetch.mock.calls[0])
|
||||
const [, options] = unsafeTestValue<[string, RequestInit]>(mockFetch.mock.calls[0])
|
||||
expect(options.signal).toBeDefined()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
describe("tool-input-cache", () => {
|
||||
const originalSetInterval = globalThis.setInterval
|
||||
@@ -33,11 +34,11 @@ describe("tool-input-cache", () => {
|
||||
|
||||
test("#given cleanup timer started #when stop cleanup runs #then interval is cleared and cache is emptied", async () => {
|
||||
//#given
|
||||
const intervalHandle = testCoerce<ReturnType<typeof setInterval>>({ unref: mock(() => {}) })
|
||||
const intervalHandle = unsafeTestValue<ReturnType<typeof setInterval>>({ unref: mock(() => {}) })
|
||||
const setIntervalMock = mock(() => intervalHandle)
|
||||
const clearIntervalMock = mock(() => {})
|
||||
globalThis.setInterval = testCoerce<typeof setInterval>(setIntervalMock)
|
||||
globalThis.clearInterval = testCoerce<typeof clearInterval>(clearIntervalMock)
|
||||
globalThis.setInterval = unsafeTestValue<typeof setInterval>(setIntervalMock)
|
||||
globalThis.clearInterval = unsafeTestValue<typeof clearInterval>(clearIntervalMock)
|
||||
|
||||
const modulePath = new URL("./tool-input-cache.ts", import.meta.url).pathname
|
||||
const cacheModule = await import(`${modulePath}?stop-clear`)
|
||||
|
||||
@@ -5,6 +5,7 @@ import { tmpdir } from "node:os"
|
||||
|
||||
import { processWithCli } from "./cli-runner"
|
||||
import type { PendingCall } from "./types"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
function createMockInput() {
|
||||
return {
|
||||
@@ -74,7 +75,7 @@ done
|
||||
const originalSetTimeout = globalThis.setTimeout
|
||||
globalThis.setTimeout = ((fn: (...args: unknown[]) => void, _ms?: number) => {
|
||||
fn()
|
||||
return testCoerce<ReturnType<typeof setTimeout>>(0)
|
||||
return unsafeTestValue<ReturnType<typeof setTimeout>>(0)
|
||||
}) as typeof setTimeout
|
||||
|
||||
try {
|
||||
@@ -102,7 +103,7 @@ done
|
||||
const originalSetTimeout = globalThis.setTimeout
|
||||
globalThis.setTimeout = ((fn: (...args: unknown[]) => void, _ms?: number) => {
|
||||
fn()
|
||||
return testCoerce<ReturnType<typeof setTimeout>>(0)
|
||||
return unsafeTestValue<ReturnType<typeof setTimeout>>(0)
|
||||
}) as typeof setTimeout
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { describe, test, expect } from "bun:test"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
describe("pending-calls cleanup interval", () => {
|
||||
test("starts cleanup once and unrefs timer", async () => {
|
||||
@@ -7,13 +8,13 @@ describe("pending-calls cleanup interval", () => {
|
||||
const setIntervalCalls: number[] = []
|
||||
let unrefCalled = 0
|
||||
|
||||
globalThis.setInterval = testCoerce<typeof setInterval>(((
|
||||
globalThis.setInterval = unsafeTestValue<typeof setInterval>(((
|
||||
_handler: TimerHandler,
|
||||
timeout?: number,
|
||||
..._args: unknown[]
|
||||
) => {
|
||||
setIntervalCalls.push(timeout as number)
|
||||
return testCoerce<ReturnType<typeof setInterval>>({
|
||||
return unsafeTestValue<ReturnType<typeof setInterval>>({
|
||||
unref: () => {
|
||||
unrefCalled += 1
|
||||
},
|
||||
@@ -43,16 +44,16 @@ describe("pending-calls cleanup interval", () => {
|
||||
let intervalHandle: ReturnType<typeof setInterval> | undefined
|
||||
let clearCalls = 0
|
||||
|
||||
globalThis.setInterval = testCoerce<typeof setInterval>(((
|
||||
globalThis.setInterval = unsafeTestValue<typeof setInterval>(((
|
||||
_handler: TimerHandler,
|
||||
_timeout?: number,
|
||||
..._args: unknown[]
|
||||
) => {
|
||||
intervalHandle = testCoerce<ReturnType<typeof setInterval>>({ unref: () => {} })
|
||||
intervalHandle = unsafeTestValue<ReturnType<typeof setInterval>>({ unref: () => {} })
|
||||
return intervalHandle
|
||||
}))
|
||||
|
||||
globalThis.clearInterval = testCoerce<typeof clearInterval>(((handle?: ReturnType<typeof setInterval>) => {
|
||||
globalThis.clearInterval = unsafeTestValue<typeof clearInterval>(((handle?: ReturnType<typeof setInterval>) => {
|
||||
if (handle === intervalHandle) {
|
||||
clearCalls += 1
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { describe, it, expect, beforeEach } from "bun:test"
|
||||
import { createEditErrorRecoveryHook, EDIT_ERROR_REMINDER, EDIT_ERROR_PATTERNS } from "./index"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
describe("createEditErrorRecoveryHook", () => {
|
||||
let hook: ReturnType<typeof createEditErrorRecoveryHook>
|
||||
|
||||
beforeEach(() => {
|
||||
hook = createEditErrorRecoveryHook(testCoerce({}))
|
||||
hook = createEditErrorRecoveryHook(unsafeTestValue({}))
|
||||
})
|
||||
|
||||
describe("tool.execute.after", () => {
|
||||
@@ -108,7 +109,7 @@ describe("createEditErrorRecoveryHook", () => {
|
||||
const input = createInput("Edit")
|
||||
const output = {
|
||||
title: "Edit",
|
||||
output: testCoerce<string>(undefined),
|
||||
output: unsafeTestValue<string>(undefined),
|
||||
metadata: {},
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, expect, test, beforeEach, afterEach } from "bun:test"
|
||||
import { createKeywordDetectorHook } from "./index"
|
||||
import { _resetForTesting, setMainSession } from "../../features/claude-code-session-state"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
type StartLoopCall = {
|
||||
sessionID: string
|
||||
@@ -11,7 +12,7 @@ type StartLoopCall = {
|
||||
type CancelLoopCall = { sessionID: string }
|
||||
|
||||
function createMockPluginInput() {
|
||||
return testCoerce({
|
||||
return unsafeTestValue({
|
||||
client: {
|
||||
tui: {
|
||||
showToast: async () => {},
|
||||
|
||||
@@ -4,6 +4,7 @@ import { createKeywordDetectorHook } from "./index"
|
||||
import { setMainSession, _resetForTesting } from "../../features/claude-code-session-state"
|
||||
import * as sharedModule from "../../shared"
|
||||
import * as sessionState from "../../features/claude-code-session-state"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
describe("keyword-detector hyperplan-ultrawork combo", () => {
|
||||
let logSpy: ReturnType<typeof spyOn>
|
||||
@@ -22,7 +23,7 @@ describe("keyword-detector hyperplan-ultrawork combo", () => {
|
||||
|
||||
function createMockPluginInput(options: { toastCalls?: string[] } = {}) {
|
||||
const toastCalls = options.toastCalls ?? []
|
||||
return testCoerce<PluginInput>({
|
||||
return unsafeTestValue<PluginInput>({
|
||||
client: {
|
||||
tui: {
|
||||
showToast: async (opts: { body: { title: string } }) => {
|
||||
|
||||
@@ -7,6 +7,7 @@ import { setMainSession, updateSessionAgent, clearSessionAgent, _resetForTesting
|
||||
import { ContextCollector } from "../../features/context-injector"
|
||||
import * as sharedModule from "../../shared"
|
||||
import * as sessionState from "../../features/claude-code-session-state"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
type ToastOptions = { body: { title: string } }
|
||||
|
||||
@@ -881,7 +882,7 @@ describe("keyword-detector team mode", () => {
|
||||
})
|
||||
|
||||
function createMockPluginInput() {
|
||||
return testCoerce<PluginInput>({
|
||||
return unsafeTestValue<PluginInput>({
|
||||
client: {
|
||||
tui: {
|
||||
showToast: async () => {},
|
||||
@@ -1063,7 +1064,7 @@ describe("keyword-detector disabled_keywords config", () => {
|
||||
|
||||
function createMockPluginInput(options: { toastCalls?: string[] } = {}) {
|
||||
const toastCalls = options.toastCalls ?? []
|
||||
return testCoerce<PluginInput>({
|
||||
return unsafeTestValue<PluginInput>({
|
||||
client: {
|
||||
tui: {
|
||||
showToast: async (opts: { body: { title: string } }) => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { PluginInput } from "@opencode-ai/plugin"
|
||||
|
||||
import { createKeywordDetectorHook } from "./index"
|
||||
import { _resetForTesting, setMainSession } from "../../features/claude-code-session-state"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
type StartLoopCall = {
|
||||
sessionID: string
|
||||
@@ -11,7 +12,7 @@ type StartLoopCall = {
|
||||
}
|
||||
|
||||
function createMockPluginInput(toastCalls: string[] = []) {
|
||||
return testCoerce<PluginInput>({
|
||||
return unsafeTestValue<PluginInput>({
|
||||
client: {
|
||||
tui: {
|
||||
showToast: async (opts: { body: { title: string } }) => {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { createKeywordDetectorHook } from "./index"
|
||||
import { _resetForTesting, setMainSession } from "../../features/claude-code-session-state"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
function createMockPluginInput(toastMessages: string[]) {
|
||||
return testCoerce({
|
||||
return unsafeTestValue({
|
||||
client: {
|
||||
tui: {
|
||||
showToast: async (opts: { body: { message: string } }) => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
declare const require: (name: string) => any
|
||||
const { beforeEach, describe, expect, mock, test, afterAll } = require("bun:test")
|
||||
|
||||
@@ -86,7 +87,7 @@ describe("model fallback hook", () => {
|
||||
})
|
||||
|
||||
test("applies pending fallback on chat.message by overriding model", async () => {
|
||||
const hook = testCoerce<{
|
||||
const hook = unsafeTestValue<{
|
||||
"chat.message"?: (
|
||||
input: { sessionID: string },
|
||||
output: { message: Record<string, unknown>; parts: Array<{ type: string; text?: string }> },
|
||||
@@ -122,7 +123,7 @@ describe("model fallback hook", () => {
|
||||
})
|
||||
|
||||
test("preserves fallback progression across repeated session.error retries", async () => {
|
||||
const hook = testCoerce<{
|
||||
const hook = unsafeTestValue<{
|
||||
"chat.message"?: (
|
||||
input: { sessionID: string },
|
||||
output: { message: Record<string, unknown>; parts: Array<{ type: string; text?: string }> },
|
||||
@@ -212,7 +213,7 @@ describe("model fallback hook", () => {
|
||||
const sessionID = "ses_model_fallback_noop_skip"
|
||||
clearPendingModelFallback(modelFallback, sessionID)
|
||||
|
||||
const hook = testCoerce<{
|
||||
const hook = unsafeTestValue<{
|
||||
"chat.message"?: (
|
||||
input: { sessionID: string },
|
||||
output: { message: Record<string, unknown>; parts: Array<{ type: string; text?: string }> },
|
||||
@@ -254,7 +255,7 @@ describe("model fallback hook", () => {
|
||||
const sessionID = "ses_model_fallback_noop_variant_skip"
|
||||
clearPendingModelFallback(modelFallback, sessionID)
|
||||
|
||||
const hook = testCoerce<{
|
||||
const hook = unsafeTestValue<{
|
||||
"chat.message"?: (
|
||||
input: { sessionID: string },
|
||||
output: { message: Record<string, unknown>; parts: Array<{ type: string; text?: string }> },
|
||||
@@ -299,7 +300,7 @@ describe("model fallback hook", () => {
|
||||
clearPendingModelFallback(modelFallback, sessionID)
|
||||
readConnectedProvidersCacheMock.mockReturnValue(["provider-x"])
|
||||
|
||||
const hook = testCoerce<{
|
||||
const hook = unsafeTestValue<{
|
||||
"chat.message"?: (
|
||||
input: { sessionID: string },
|
||||
output: { message: Record<string, unknown>; parts: Array<{ type: string; text?: string }> },
|
||||
@@ -355,7 +356,7 @@ describe("model fallback hook", () => {
|
||||
|
||||
test("shows toast when fallback is applied", async () => {
|
||||
const toastCalls: Array<{ title: string; message: string }> = []
|
||||
const hook = testCoerce<{
|
||||
const hook = unsafeTestValue<{
|
||||
"chat.message"?: (
|
||||
input: { sessionID: string },
|
||||
output: { message: Record<string, unknown>; parts: Array<{ type: string; text?: string }> },
|
||||
@@ -393,7 +394,7 @@ describe("model fallback hook", () => {
|
||||
const sessionID = "ses_model_fallback_ghcp"
|
||||
clearPendingModelFallback(modelFallback, sessionID)
|
||||
|
||||
const hook = testCoerce<{
|
||||
const hook = unsafeTestValue<{
|
||||
"chat.message"?: (
|
||||
input: { sessionID: string },
|
||||
output: { message: Record<string, unknown>; parts: Array<{ type: string; text?: string }> },
|
||||
@@ -434,7 +435,7 @@ describe("model fallback hook", () => {
|
||||
const sessionID = "ses_model_fallback_google"
|
||||
clearPendingModelFallback(modelFallback, sessionID)
|
||||
|
||||
const hook = testCoerce<{
|
||||
const hook = unsafeTestValue<{
|
||||
"chat.message"?: (
|
||||
input: { sessionID: string },
|
||||
output: { message: Record<string, unknown>; parts: Array<{ type: string; text?: string }> },
|
||||
|
||||
@@ -4,6 +4,7 @@ import { describe, expect, spyOn, test } from "bun:test"
|
||||
import { _resetForTesting, updateSessionAgent } from "../../features/claude-code-session-state"
|
||||
import { getAgentDisplayName } from "../../shared/agent-display-names"
|
||||
import { createNoHephaestusNonGptHook } from "./index"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
const HEPHAESTUS_DISPLAY = getAgentDisplayName("hephaestus")
|
||||
const SISYPHUS_DISPLAY = getAgentDisplayName("sisyphus")
|
||||
@@ -19,7 +20,7 @@ describe("no-hephaestus-non-gpt hook", () => {
|
||||
test("shows toast on every chat.message when hephaestus uses non-gpt model", async () => {
|
||||
// given - hephaestus with claude model
|
||||
const showToast = spyOn({ fn: async (_input: unknown) => ({}) }, "fn")
|
||||
const hook = createNoHephaestusNonGptHook(testCoerce({
|
||||
const hook = createNoHephaestusNonGptHook(unsafeTestValue({
|
||||
client: { tui: { showToast } },
|
||||
}))
|
||||
|
||||
@@ -54,7 +55,7 @@ describe("no-hephaestus-non-gpt hook", () => {
|
||||
test("shows warning and does not switch agent when allow_non_gpt_model is enabled", async () => {
|
||||
// given - hephaestus with claude model and opt-out enabled
|
||||
const showToast = spyOn({ fn: async (_input: unknown) => ({}) }, "fn")
|
||||
const hook = createNoHephaestusNonGptHook(testCoerce({
|
||||
const hook = createNoHephaestusNonGptHook(unsafeTestValue({
|
||||
client: { tui: { showToast } },
|
||||
}), {
|
||||
allowNonGptModel: true,
|
||||
@@ -83,7 +84,7 @@ describe("no-hephaestus-non-gpt hook", () => {
|
||||
test("does not show toast when hephaestus uses gpt model", async () => {
|
||||
// given - hephaestus with gpt model
|
||||
const showToast = spyOn({ fn: async (_input: unknown) => ({}) }, "fn")
|
||||
const hook = createNoHephaestusNonGptHook(testCoerce({
|
||||
const hook = createNoHephaestusNonGptHook(unsafeTestValue({
|
||||
client: { tui: { showToast } },
|
||||
}))
|
||||
|
||||
@@ -104,7 +105,7 @@ describe("no-hephaestus-non-gpt hook", () => {
|
||||
test("does not show toast for non-hephaestus agent", async () => {
|
||||
// given - sisyphus with claude model (non-gpt)
|
||||
const showToast = spyOn({ fn: async (_input: unknown) => ({}) }, "fn")
|
||||
const hook = createNoHephaestusNonGptHook(testCoerce({
|
||||
const hook = createNoHephaestusNonGptHook(unsafeTestValue({
|
||||
client: { tui: { showToast } },
|
||||
}))
|
||||
|
||||
@@ -127,7 +128,7 @@ describe("no-hephaestus-non-gpt hook", () => {
|
||||
_resetForTesting()
|
||||
updateSessionAgent("ses_4", HEPHAESTUS_DISPLAY)
|
||||
const showToast = spyOn({ fn: async (_input: unknown) => ({}) }, "fn")
|
||||
const hook = createNoHephaestusNonGptHook(testCoerce({
|
||||
const hook = createNoHephaestusNonGptHook(unsafeTestValue({
|
||||
client: { tui: { showToast } },
|
||||
}))
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import { _resetForTesting, updateSessionAgent } from "../../features/claude-code-session-state"
|
||||
import { getAgentDisplayName } from "../../shared/agent-display-names"
|
||||
import { createNoSisyphusGptHook } from "./index"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
const SISYPHUS_DISPLAY = getAgentDisplayName("sisyphus")
|
||||
const HEPHAESTUS_DISPLAY = getAgentDisplayName("hephaestus")
|
||||
@@ -22,7 +23,7 @@ function createOutput(): HookOutput {
|
||||
}
|
||||
|
||||
function createHookContext(showToast: (input: unknown) => Promise<unknown>): PluginInput {
|
||||
return testCoerce<PluginInput>({
|
||||
return unsafeTestValue<PluginInput>({
|
||||
client: { tui: { showToast } },
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
import { describe, it, expect } from "bun:test";
|
||||
import { createQuestionLabelTruncatorHook } from "./index";
|
||||
|
||||
@@ -23,10 +24,10 @@ describe("createQuestionLabelTruncatorHook", () => {
|
||||
};
|
||||
|
||||
// when
|
||||
await hook["tool.execute.before"]?.(testCoerce(input), testCoerce(output));
|
||||
await hook["tool.execute.before"]?.(unsafeTestValue(input), unsafeTestValue(output));
|
||||
|
||||
// then
|
||||
const truncatedLabel = (testCoerce(output.args)).questions[0].options[0].label;
|
||||
const truncatedLabel = (unsafeTestValue(output.args)).questions[0].options[0].label;
|
||||
expect(truncatedLabel.length).toBeLessThanOrEqual(30);
|
||||
expect(truncatedLabel).toBe("This is a very long label t...");
|
||||
expect(truncatedLabel.endsWith("...")).toBe(true);
|
||||
@@ -50,10 +51,10 @@ describe("createQuestionLabelTruncatorHook", () => {
|
||||
};
|
||||
|
||||
// when
|
||||
await hook["tool.execute.before"]?.(testCoerce(input), testCoerce(output));
|
||||
await hook["tool.execute.before"]?.(unsafeTestValue(input), unsafeTestValue(output));
|
||||
|
||||
// then
|
||||
const resultLabel = (testCoerce(output.args)).questions[0].options[0].label;
|
||||
const resultLabel = (unsafeTestValue(output.args)).questions[0].options[0].label;
|
||||
expect(resultLabel).toBe(shortLabel);
|
||||
});
|
||||
|
||||
@@ -74,10 +75,10 @@ describe("createQuestionLabelTruncatorHook", () => {
|
||||
};
|
||||
|
||||
// when
|
||||
await hook["tool.execute.before"]?.(testCoerce(input), testCoerce(output));
|
||||
await hook["tool.execute.before"]?.(unsafeTestValue(input), unsafeTestValue(output));
|
||||
|
||||
// then
|
||||
const resultLabel = (testCoerce(output.args)).questions[0].options[0].label;
|
||||
const resultLabel = (unsafeTestValue(output.args)).questions[0].options[0].label;
|
||||
expect(resultLabel).toBe(exactLabel);
|
||||
});
|
||||
|
||||
@@ -90,7 +91,7 @@ describe("createQuestionLabelTruncatorHook", () => {
|
||||
const originalArgs = { ...output.args };
|
||||
|
||||
// when
|
||||
await hook["tool.execute.before"]?.(testCoerce(input), testCoerce(output));
|
||||
await hook["tool.execute.before"]?.(unsafeTestValue(input), unsafeTestValue(output));
|
||||
|
||||
// then
|
||||
expect(output.args).toEqual(originalArgs);
|
||||
@@ -120,11 +121,11 @@ describe("createQuestionLabelTruncatorHook", () => {
|
||||
};
|
||||
|
||||
// when
|
||||
await hook["tool.execute.before"]?.(testCoerce(input), testCoerce(output));
|
||||
await hook["tool.execute.before"]?.(unsafeTestValue(input), unsafeTestValue(output));
|
||||
|
||||
// then
|
||||
const q1opts = (testCoerce(output.args)).questions[0].options;
|
||||
const q2opts = (testCoerce(output.args)).questions[1].options;
|
||||
const q1opts = (unsafeTestValue(output.args)).questions[0].options;
|
||||
const q2opts = (unsafeTestValue(output.args)).questions[1].options;
|
||||
|
||||
expect(q1opts[0].label).toBe("Very long label number one ...");
|
||||
expect(q1opts[0].label.length).toBeLessThanOrEqual(30);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/// <reference types="bun-types" />
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
export type SessionMessage = {
|
||||
info?: { role?: string }
|
||||
@@ -16,7 +17,7 @@ export function createPluginInput(messages: SessionMessage[]): PluginInput {
|
||||
$: {} as PluginInput["$"],
|
||||
} as PluginInput
|
||||
|
||||
const messagesFunction = testCoerce<PluginInput["client"]["session"]["messages"]>(async () => ({ data: messages }))
|
||||
const messagesFunction = unsafeTestValue<PluginInput["client"]["session"]["messages"]>(async () => ({ data: messages }))
|
||||
pluginInput.client.session.messages = messagesFunction
|
||||
|
||||
return pluginInput
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/// <reference types="bun-types" />
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { createRalphLoopHook } from "./index"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
function createDeferred(): {
|
||||
promise: Promise<void>
|
||||
@@ -44,7 +45,7 @@ describe("ralph-loop reset strategy race condition", () => {
|
||||
const selectSessionDeferred = createDeferred()
|
||||
|
||||
const hook = createRalphLoopHook(
|
||||
testCoerce<Parameters<typeof createRalphLoopHook>[0]>({
|
||||
unsafeTestValue<Parameters<typeof createRalphLoopHook>[0]>({
|
||||
directory: process.cwd(),
|
||||
client: {
|
||||
session: {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { join } from "node:path"
|
||||
import { createRalphLoopHook } from "./index"
|
||||
import { ULTRAWORK_VERIFICATION_PROMISE } from "./constants"
|
||||
import { clearState, writeState } from "./storage"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
describe("ulw-loop verification", () => {
|
||||
const testDir = join(tmpdir(), `ulw-loop-verification-${Date.now()}`)
|
||||
@@ -15,7 +16,7 @@ describe("ulw-loop verification", () => {
|
||||
let oracleTranscriptPath: string
|
||||
|
||||
function createMockPluginInput() {
|
||||
return testCoerce<Parameters<typeof createRalphLoopHook>[0]>({
|
||||
return unsafeTestValue<Parameters<typeof createRalphLoopHook>[0]>({
|
||||
client: {
|
||||
session: {
|
||||
promptAsync: async (opts: { path: { id: string }; body: { parts: Array<{ type: string; text: string }> } }) => {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { afterEach, describe, expect, test } from "bun:test"
|
||||
|
||||
import { getFallbackModelsForSession } from "./fallback-models"
|
||||
import { SessionCategoryRegistry } from "../../shared/session-category-registry"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
describe("runtime-fallback fallback-models", () => {
|
||||
afterEach(() => {
|
||||
@@ -12,7 +13,7 @@ describe("runtime-fallback fallback-models", () => {
|
||||
//#given
|
||||
const sessionID = "ses_runtime_fallback_category"
|
||||
SessionCategoryRegistry.register(sessionID, "quick")
|
||||
const pluginConfig = testCoerce({
|
||||
const pluginConfig = unsafeTestValue({
|
||||
categories: {
|
||||
quick: {
|
||||
fallback_models: ["openai/gpt-5.2", "anthropic/claude-opus-4-7"],
|
||||
@@ -29,7 +30,7 @@ describe("runtime-fallback fallback-models", () => {
|
||||
|
||||
test("uses agent-specific fallback_models when agent is resolved", () => {
|
||||
//#given
|
||||
const pluginConfig = testCoerce({
|
||||
const pluginConfig = unsafeTestValue({
|
||||
agents: {
|
||||
oracle: {
|
||||
fallback_models: ["openai/gpt-5.2", "anthropic/claude-opus-4-7"],
|
||||
@@ -46,7 +47,7 @@ describe("runtime-fallback fallback-models", () => {
|
||||
|
||||
test("does not fall back to another agent chain when agent cannot be resolved", () => {
|
||||
//#given
|
||||
const pluginConfig = testCoerce({
|
||||
const pluginConfig = unsafeTestValue({
|
||||
agents: {
|
||||
sisyphus: {
|
||||
fallback_models: ["quotio/gpt-5.2", "quotio/glm-5", "quotio/kimi-k2.5"],
|
||||
|
||||
@@ -2,6 +2,7 @@ import { describe, expect, test, beforeEach, afterEach, mock } from "bun:test"
|
||||
import type { RuntimeFallbackConfig, OhMyOpenCodeConfig } from "../../config"
|
||||
import * as loggerModule from "../../shared/logger"
|
||||
import { SessionCategoryRegistry } from "../../shared/session-category-registry"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
type RuntimeFallbackModule = typeof import("./hook")
|
||||
|
||||
@@ -41,7 +42,7 @@ describe("runtime-fallback", () => {
|
||||
abort?: (args: unknown) => Promise<unknown>
|
||||
}
|
||||
}) {
|
||||
return testCoerce({
|
||||
return unsafeTestValue({
|
||||
client: {
|
||||
tui: {
|
||||
showToast: async (opts: { body: { title: string; message: string; variant: string; duration: number } }) => {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { afterEach, beforeEach, describe, expect, jest, spyOn, test } from "bun:
|
||||
import * as sender from "./session-notification-sender"
|
||||
import * as utils from "./session-notification-utils"
|
||||
import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import { unsafeTestValue } from "../../test-support/unsafe-test-value"
|
||||
|
||||
|
||||
|
||||
@@ -80,7 +81,7 @@ describe("session-notification-sender", () => {
|
||||
describe("#when calling ctx.$ for notifications", () => {
|
||||
test("#then should call .quiet() on all shell commands to suppress stdout/stderr", async () => {
|
||||
const quietCalls: string[] = []
|
||||
const mockCtx = testCoerce<PluginInput>({
|
||||
const mockCtx = unsafeTestValue<PluginInput>({
|
||||
$: (cmd: TemplateStringsArray, ...values: unknown[]) => {
|
||||
const cmdStr = cmd.reduce((acc, part, i) => acc + part + (values[i] ?? ""), "")
|
||||
const result = { stdout: Buffer.from(""), stderr: Buffer.from(""), exitCode: 0 }
|
||||
@@ -107,7 +108,7 @@ describe("session-notification-sender", () => {
|
||||
spyOn(utils, "getTerminalNotifierPath").mockResolvedValue(null)
|
||||
|
||||
const quietCalls: string[] = []
|
||||
const mockCtx = testCoerce<PluginInput>({
|
||||
const mockCtx = unsafeTestValue<PluginInput>({
|
||||
$: (cmd: TemplateStringsArray, ...values: unknown[]) => {
|
||||
const cmdStr = cmd.reduce((acc, part, i) => acc + part + (values[i] ?? ""), "")
|
||||
const result = { stdout: Buffer.from(""), stderr: Buffer.from(""), exitCode: 0 }
|
||||
@@ -142,7 +143,7 @@ describe("session-notification-sender", () => {
|
||||
spyOn(utils, "getCmuxPath").mockResolvedValue("/usr/local/bin/cmux")
|
||||
|
||||
const calls: string[] = []
|
||||
const mockCtx = testCoerce<PluginInput>({
|
||||
const mockCtx = unsafeTestValue<PluginInput>({
|
||||
$: createShellPromise((cmdStr) => { calls.push(cmdStr) }),
|
||||
})
|
||||
|
||||
@@ -157,7 +158,7 @@ describe("session-notification-sender", () => {
|
||||
test("#then should fall back to terminal-notifier when cmux fails", async () => {
|
||||
spyOn(utils, "getCmuxPath").mockResolvedValue("/usr/local/bin/cmux")
|
||||
|
||||
const mockCtx = testCoerce<PluginInput>({
|
||||
const mockCtx = unsafeTestValue<PluginInput>({
|
||||
$: createThrowingShellPromise((cmdStr) => cmdStr.includes("cmux notify")),
|
||||
})
|
||||
|
||||
@@ -180,7 +181,7 @@ describe("session-notification-sender", () => {
|
||||
spyOn(utils, "getCmuxPath").mockResolvedValue("/usr/local/bin/cmux")
|
||||
|
||||
const trackingCalls: string[] = []
|
||||
const mockCtx = testCoerce<PluginInput>({
|
||||
const mockCtx = unsafeTestValue<PluginInput>({
|
||||
$: createThrowingShellPromise((cmdStr) => cmdStr.includes("cmux notify") || cmdStr.includes("terminal-notifier")),
|
||||
})
|
||||
|
||||
@@ -200,7 +201,7 @@ describe("session-notification-sender", () => {
|
||||
|
||||
test("#then should skip cmux when not available and use terminal-notifier", async () => {
|
||||
const calls: string[] = []
|
||||
const mockCtx = testCoerce<PluginInput>({
|
||||
const mockCtx = unsafeTestValue<PluginInput>({
|
||||
$: createShellPromise((cmdStr) => { calls.push(cmdStr) }),
|
||||
})
|
||||
|
||||
@@ -213,7 +214,7 @@ describe("session-notification-sender", () => {
|
||||
|
||||
test("#then should call .quiet() on linux notify-send", async () => {
|
||||
const quietCalls: string[] = []
|
||||
const mockCtx = testCoerce<PluginInput>({
|
||||
const mockCtx = unsafeTestValue<PluginInput>({
|
||||
$: (cmd: TemplateStringsArray, ...values: unknown[]) => {
|
||||
const cmdStr = cmd.reduce((acc, part, i) => acc + part + (values[i] ?? ""), "")
|
||||
const result = { stdout: Buffer.from(""), stderr: Buffer.from(""), exitCode: 0 }
|
||||
@@ -246,7 +247,7 @@ describe("session-notification-sender", () => {
|
||||
|
||||
test("#then should call .quiet() on win32 powershell", async () => {
|
||||
const quietCalls: string[] = []
|
||||
const mockCtx = testCoerce<PluginInput>({
|
||||
const mockCtx = unsafeTestValue<PluginInput>({
|
||||
$: (cmd: TemplateStringsArray, ...values: unknown[]) => {
|
||||
const cmdStr = cmd.reduce((acc, part, i) => acc + part + (values[i] ?? ""), "")
|
||||
const result = { stdout: Buffer.from(""), stderr: Buffer.from(""), exitCode: 0 }
|
||||
@@ -283,7 +284,7 @@ describe("session-notification-sender", () => {
|
||||
describe("#when calling ctx.$ for sound playback", () => {
|
||||
test("#then should call .quiet() on darwin afplay", async () => {
|
||||
const quietCalls: string[] = []
|
||||
const mockCtx = testCoerce<PluginInput>({
|
||||
const mockCtx = unsafeTestValue<PluginInput>({
|
||||
$: (cmd: TemplateStringsArray, ...values: unknown[]) => {
|
||||
const cmdStr = cmd.reduce((acc, part, i) => acc + part + (values[i] ?? ""), "")
|
||||
const result = { stdout: Buffer.from(""), stderr: Buffer.from(""), exitCode: 0 }
|
||||
@@ -316,7 +317,7 @@ describe("session-notification-sender", () => {
|
||||
|
||||
test("#then should call .quiet() on linux paplay", async () => {
|
||||
const quietCalls: string[] = []
|
||||
const mockCtx = testCoerce<PluginInput>({
|
||||
const mockCtx = unsafeTestValue<PluginInput>({
|
||||
$: (cmd: TemplateStringsArray, ...values: unknown[]) => {
|
||||
const cmdStr = cmd.reduce((acc, part, i) => acc + part + (values[i] ?? ""), "")
|
||||
const result = { stdout: Buffer.from(""), stderr: Buffer.from(""), exitCode: 0 }
|
||||
@@ -351,7 +352,7 @@ describe("session-notification-sender", () => {
|
||||
spyOn(utils, "getPaplayPath").mockResolvedValue(null)
|
||||
|
||||
const quietCalls: string[] = []
|
||||
const mockCtx = testCoerce<PluginInput>({
|
||||
const mockCtx = unsafeTestValue<PluginInput>({
|
||||
$: (cmd: TemplateStringsArray, ...values: unknown[]) => {
|
||||
const cmdStr = cmd.reduce((acc, part, i) => acc + part + (values[i] ?? ""), "")
|
||||
const result = { stdout: Buffer.from(""), stderr: Buffer.from(""), exitCode: 0 }
|
||||
@@ -384,7 +385,7 @@ describe("session-notification-sender", () => {
|
||||
|
||||
test("#then should call .quiet() on win32 powershell sound", async () => {
|
||||
const quietCalls: string[] = []
|
||||
const mockCtx = testCoerce<PluginInput>({
|
||||
const mockCtx = unsafeTestValue<PluginInput>({
|
||||
$: (cmd: TemplateStringsArray, ...values: unknown[]) => {
|
||||
const cmdStr = cmd.reduce((acc, part, i) => acc + part + (values[i] ?? ""), "")
|
||||
const result = { stdout: Buffer.from(""), stderr: Buffer.from(""), exitCode: 0 }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { describe, expect, it } from "bun:test"
|
||||
import { unsafeTestValue } from "../../../../test-support/unsafe-test-value"
|
||||
async function importFreshReaders() {
|
||||
const token = `${Date.now()}-${Math.random()}`
|
||||
const [{ readMessagesFromSDK, readMessages }, { readPartsFromSDK, readParts }] = await Promise.all([
|
||||
@@ -13,7 +14,7 @@ function createMockClient(handlers: {
|
||||
messages?: (sessionID: string) => unknown[]
|
||||
message?: (sessionID: string, messageID: string) => unknown
|
||||
}) {
|
||||
return testCoerce({
|
||||
return unsafeTestValue({
|
||||
session: {
|
||||
messages: async (opts: { path: { id: string } }) => {
|
||||
if (handlers.messages) {
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
import type { BoulderState } from "../../features/boulder-state"
|
||||
import * as sessionState from "../../features/claude-code-session-state"
|
||||
import * as worktreeDetector from "./worktree-detector"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
describe("start-work hook", () => {
|
||||
let testDir: string
|
||||
@@ -738,7 +739,7 @@ You are starting a Sisyphus work session.
|
||||
const promptAsyncMock = spyOn({
|
||||
promptAsync: async (_request: unknown) => undefined,
|
||||
}, "promptAsync")
|
||||
const ctx = testCoerce<Parameters<typeof createAtlasHook>[0]>({
|
||||
const ctx = unsafeTestValue<Parameters<typeof createAtlasHook>[0]>({
|
||||
directory: testDir,
|
||||
client: {
|
||||
session: {
|
||||
@@ -784,18 +785,18 @@ You are starting a Sisyphus work session.
|
||||
promptAsync: async (_request: unknown) => undefined,
|
||||
}, "promptAsync")
|
||||
|
||||
globalThis.setTimeout = testCoerce<typeof setTimeout>(((callback: Function, delay?: number, ...args: unknown[]) => {
|
||||
globalThis.setTimeout = unsafeTestValue<typeof setTimeout>(((callback: Function, delay?: number, ...args: unknown[]) => {
|
||||
const normalized = typeof delay === "number" ? delay : 0
|
||||
if (normalized >= 5000) {
|
||||
const id = nextTimerId++
|
||||
capturedTimers.set(id, { callback: () => callback(...args), cleared: false })
|
||||
return testCoerce<ReturnType<typeof setTimeout>>(id)
|
||||
return unsafeTestValue<ReturnType<typeof setTimeout>>(id)
|
||||
}
|
||||
|
||||
return originalSetTimeout(callback as Parameters<typeof originalSetTimeout>[0], delay)
|
||||
}))
|
||||
|
||||
globalThis.clearTimeout = testCoerce<typeof clearTimeout>(((id?: number | ReturnType<typeof setTimeout>) => {
|
||||
globalThis.clearTimeout = unsafeTestValue<typeof clearTimeout>(((id?: number | ReturnType<typeof setTimeout>) => {
|
||||
if (typeof id === "number" && capturedTimers.has(id)) {
|
||||
capturedTimers.get(id)!.cleared = true
|
||||
capturedTimers.delete(id)
|
||||
@@ -807,7 +808,7 @@ You are starting a Sisyphus work session.
|
||||
|
||||
Date.now = () => fakeNow
|
||||
|
||||
const ctx = testCoerce<Parameters<typeof createAtlasHook>[0]>({
|
||||
const ctx = unsafeTestValue<Parameters<typeof createAtlasHook>[0]>({
|
||||
directory: testDir,
|
||||
client: {
|
||||
session: {
|
||||
@@ -820,7 +821,7 @@ You are starting a Sisyphus work session.
|
||||
const startWorkHook = createStartWorkHook(ctx)
|
||||
const atlasHook = createAtlasHook(ctx, {
|
||||
directory: testDir,
|
||||
backgroundManager: testCoerce<NonNullable<Parameters<typeof createAtlasHook>[1]>["backgroundManager"]>({
|
||||
backgroundManager: unsafeTestValue<NonNullable<Parameters<typeof createAtlasHook>[1]>["backgroundManager"]>({
|
||||
getTasksByParentSession: () => backgroundRunning ? [{ status: "running" }] : [],
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { PluginInput } from "@opencode-ai/plugin"
|
||||
import type { BackgroundManager, BackgroundTask } from "../../features/background-agent"
|
||||
import { readContinuationMarker } from "../../features/run-continuation-state"
|
||||
import { createStopContinuationGuardHook } from "./index"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
type CancelCall = {
|
||||
taskId: string
|
||||
@@ -31,7 +32,7 @@ describe("stop-continuation-guard", () => {
|
||||
})
|
||||
|
||||
function createMockPluginInput() {
|
||||
return testCoerce<PluginInput>({
|
||||
return unsafeTestValue<PluginInput>({
|
||||
client: {
|
||||
tui: {
|
||||
showToast: async () => ({}),
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { describe, it, expect } from "bun:test"
|
||||
import { createTaskResumeInfoHook } from "./index"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
describe("createTaskResumeInfoHook", () => {
|
||||
const hook = createTaskResumeInfoHook()
|
||||
@@ -19,7 +20,7 @@ describe("createTaskResumeInfoHook", () => {
|
||||
const input = createInput("task")
|
||||
const output = {
|
||||
title: "delegate_task",
|
||||
output: testCoerce<string>(undefined),
|
||||
output: unsafeTestValue<string>(undefined),
|
||||
metadata: {},
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user