fix: correct test type casts, timeouts, and mock structures
- Fix PluginInput type casts to use 'as unknown as PluginInput' - Add explicit TodoSnapshot type annotations - Add timeouts to slow todo-continuation-enforcer tests - Remove unnecessary test storage mocks in atlas and prometheus-md-only - Restructure sync-executor mocks to use beforeEach/afterEach pattern
This commit is contained in:
parent
e9c9cb696d
commit
f27733eae2
@ -1,13 +1,21 @@
|
|||||||
import { describe, test, expect, mock, beforeEach } from "bun:test"
|
import { describe, test, expect, mock, beforeEach, afterAll } from "bun:test"
|
||||||
import type { PluginInput } from "@opencode-ai/plugin"
|
import type { PluginInput } from "@opencode-ai/plugin"
|
||||||
import type { ExperimentalConfig } from "../../config"
|
import type { ExperimentalConfig } from "../../config"
|
||||||
|
|
||||||
const attemptDeduplicationRecoveryMock = mock(async () => {})
|
const realDeduplicationRecovery = await import("./deduplication-recovery")
|
||||||
|
|
||||||
|
const attemptDeduplicationRecoveryMock = mock<(sessionID: string) => Promise<void>>(
|
||||||
|
async () => {}
|
||||||
|
)
|
||||||
|
|
||||||
mock.module("./deduplication-recovery", () => ({
|
mock.module("./deduplication-recovery", () => ({
|
||||||
attemptDeduplicationRecovery: attemptDeduplicationRecoveryMock,
|
attemptDeduplicationRecovery: attemptDeduplicationRecoveryMock,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
mock.module("./deduplication-recovery", () => ({ ...realDeduplicationRecovery }))
|
||||||
|
})
|
||||||
|
|
||||||
function createImmediateTimeouts(): () => void {
|
function createImmediateTimeouts(): () => void {
|
||||||
const originalSetTimeout = globalThis.setTimeout
|
const originalSetTimeout = globalThis.setTimeout
|
||||||
const originalClearTimeout = globalThis.clearTimeout
|
const originalClearTimeout = globalThis.clearTimeout
|
||||||
@ -37,13 +45,15 @@ describe("createAnthropicContextWindowLimitRecoveryHook", () => {
|
|||||||
const experimental = {
|
const experimental = {
|
||||||
dynamic_context_pruning: {
|
dynamic_context_pruning: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
notification: "off",
|
||||||
|
protected_tools: [],
|
||||||
strategies: {
|
strategies: {
|
||||||
deduplication: { enabled: true },
|
deduplication: { enabled: true },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} satisfies ExperimentalConfig
|
} satisfies ExperimentalConfig
|
||||||
|
|
||||||
let resolveSummarize: (() => void) | null = null
|
let resolveSummarize: ((value?: void) => void) | null = null
|
||||||
const summarizePromise = new Promise<void>((resolve) => {
|
const summarizePromise = new Promise<void>((resolve) => {
|
||||||
resolveSummarize = resolve
|
resolveSummarize = resolve
|
||||||
})
|
})
|
||||||
@ -62,7 +72,7 @@ describe("createAnthropicContextWindowLimitRecoveryHook", () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const { createAnthropicContextWindowLimitRecoveryHook } = await import("./recovery-hook")
|
const { createAnthropicContextWindowLimitRecoveryHook } = await import("./recovery-hook")
|
||||||
const ctx = { client: mockClient, directory: "/tmp" } as PluginInput
|
const ctx = { client: mockClient, directory: "/tmp" } as unknown as PluginInput
|
||||||
const hook = createAnthropicContextWindowLimitRecoveryHook(ctx, { experimental })
|
const hook = createAnthropicContextWindowLimitRecoveryHook(ctx, { experimental })
|
||||||
|
|
||||||
// first error triggers compaction (setTimeout runs immediately due to mock)
|
// first error triggers compaction (setTimeout runs immediately due to mock)
|
||||||
@ -105,7 +115,7 @@ describe("createAnthropicContextWindowLimitRecoveryHook", () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { createAnthropicContextWindowLimitRecoveryHook } = await import("./recovery-hook")
|
const { createAnthropicContextWindowLimitRecoveryHook } = await import("./recovery-hook")
|
||||||
const ctx = { client: mockClient, directory: "/tmp" } as PluginInput
|
const ctx = { client: mockClient, directory: "/tmp" } as unknown as PluginInput
|
||||||
const hook = createAnthropicContextWindowLimitRecoveryHook(ctx)
|
const hook = createAnthropicContextWindowLimitRecoveryHook(ctx)
|
||||||
|
|
||||||
//#when - single error (no compaction in progress)
|
//#when - single error (no compaction in progress)
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { describe, expect, test, beforeEach, afterEach, mock } from "bun:test"
|
import { describe, expect, test, beforeEach, afterEach, afterAll, mock } from "bun:test"
|
||||||
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs"
|
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs"
|
||||||
import { join } from "node:path"
|
import { join } from "node:path"
|
||||||
import { tmpdir } from "node:os"
|
import { tmpdir } from "node:os"
|
||||||
@ -9,20 +9,19 @@ import {
|
|||||||
readBoulderState,
|
readBoulderState,
|
||||||
} from "../../features/boulder-state"
|
} from "../../features/boulder-state"
|
||||||
import type { BoulderState } from "../../features/boulder-state"
|
import type { BoulderState } from "../../features/boulder-state"
|
||||||
|
const realClaudeCodeSessionState = await import(
|
||||||
const TEST_STORAGE_ROOT = join(tmpdir(), `atlas-message-storage-${randomUUID()}`)
|
"../../features/claude-code-session-state"
|
||||||
const TEST_MESSAGE_STORAGE = join(TEST_STORAGE_ROOT, "message")
|
)
|
||||||
const TEST_PART_STORAGE = join(TEST_STORAGE_ROOT, "part")
|
|
||||||
|
|
||||||
mock.module("../../features/hook-message-injector/constants", () => ({
|
|
||||||
OPENCODE_STORAGE: TEST_STORAGE_ROOT,
|
|
||||||
MESSAGE_STORAGE: TEST_MESSAGE_STORAGE,
|
|
||||||
PART_STORAGE: TEST_PART_STORAGE,
|
|
||||||
}))
|
|
||||||
|
|
||||||
const { createAtlasHook } = await import("./index")
|
const { createAtlasHook } = await import("./index")
|
||||||
const { MESSAGE_STORAGE } = await import("../../features/hook-message-injector")
|
const { MESSAGE_STORAGE } = await import("../../features/hook-message-injector")
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
mock.module("../../features/claude-code-session-state", () => ({
|
||||||
|
...realClaudeCodeSessionState,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
describe("atlas hook", () => {
|
describe("atlas hook", () => {
|
||||||
let TEST_DIR: string
|
let TEST_DIR: string
|
||||||
let SISYPHUS_DIR: string
|
let SISYPHUS_DIR: string
|
||||||
@ -77,7 +76,6 @@ describe("atlas hook", () => {
|
|||||||
if (existsSync(TEST_DIR)) {
|
if (existsSync(TEST_DIR)) {
|
||||||
rmSync(TEST_DIR, { recursive: true, force: true })
|
rmSync(TEST_DIR, { recursive: true, force: true })
|
||||||
}
|
}
|
||||||
rmSync(TEST_STORAGE_ROOT, { recursive: true, force: true })
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("tool.execute.after handler", () => {
|
describe("tool.execute.after handler", () => {
|
||||||
|
|||||||
@ -30,7 +30,7 @@ function createMockContext(todoResponses: TodoSnapshot[][]): PluginInput {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
directory: "/tmp/test",
|
directory: "/tmp/test",
|
||||||
} as PluginInput
|
} as unknown as PluginInput
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("compaction-todo-preserver", () => {
|
describe("compaction-todo-preserver", () => {
|
||||||
@ -38,7 +38,7 @@ describe("compaction-todo-preserver", () => {
|
|||||||
//#given
|
//#given
|
||||||
updateMock.mockClear()
|
updateMock.mockClear()
|
||||||
const sessionID = "session-compaction-missing"
|
const sessionID = "session-compaction-missing"
|
||||||
const todos = [
|
const todos: TodoSnapshot[] = [
|
||||||
{ id: "1", content: "Task 1", status: "pending", priority: "high" },
|
{ id: "1", content: "Task 1", status: "pending", priority: "high" },
|
||||||
{ id: "2", content: "Task 2", status: "in_progress", priority: "medium" },
|
{ id: "2", content: "Task 2", status: "in_progress", priority: "medium" },
|
||||||
]
|
]
|
||||||
@ -58,7 +58,7 @@ describe("compaction-todo-preserver", () => {
|
|||||||
//#given
|
//#given
|
||||||
updateMock.mockClear()
|
updateMock.mockClear()
|
||||||
const sessionID = "session-compaction-present"
|
const sessionID = "session-compaction-present"
|
||||||
const todos = [
|
const todos: TodoSnapshot[] = [
|
||||||
{ id: "1", content: "Task 1", status: "pending", priority: "high" },
|
{ id: "1", content: "Task 1", status: "pending", priority: "high" },
|
||||||
]
|
]
|
||||||
const ctx = createMockContext([todos, todos])
|
const ctx = createMockContext([todos, todos])
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { describe, expect, test, beforeEach, afterEach, mock } from "bun:test"
|
import { describe, expect, test, beforeEach, afterEach } from "bun:test"
|
||||||
import { mkdirSync, rmSync, writeFileSync } from "node:fs"
|
import { mkdirSync, rmSync, writeFileSync } from "node:fs"
|
||||||
import { join } from "node:path"
|
import { join } from "node:path"
|
||||||
import { tmpdir } from "node:os"
|
import { tmpdir } from "node:os"
|
||||||
@ -6,16 +6,6 @@ import { randomUUID } from "node:crypto"
|
|||||||
import { SYSTEM_DIRECTIVE_PREFIX } from "../../shared/system-directive"
|
import { SYSTEM_DIRECTIVE_PREFIX } from "../../shared/system-directive"
|
||||||
import { clearSessionAgent } from "../../features/claude-code-session-state"
|
import { clearSessionAgent } from "../../features/claude-code-session-state"
|
||||||
|
|
||||||
const TEST_STORAGE_ROOT = join(tmpdir(), `prometheus-md-only-${randomUUID()}`)
|
|
||||||
const TEST_MESSAGE_STORAGE = join(TEST_STORAGE_ROOT, "message")
|
|
||||||
const TEST_PART_STORAGE = join(TEST_STORAGE_ROOT, "part")
|
|
||||||
|
|
||||||
mock.module("../../features/hook-message-injector/constants", () => ({
|
|
||||||
OPENCODE_STORAGE: TEST_STORAGE_ROOT,
|
|
||||||
MESSAGE_STORAGE: TEST_MESSAGE_STORAGE,
|
|
||||||
PART_STORAGE: TEST_PART_STORAGE,
|
|
||||||
}))
|
|
||||||
|
|
||||||
const { createPrometheusMdOnlyHook } = await import("./index")
|
const { createPrometheusMdOnlyHook } = await import("./index")
|
||||||
const { MESSAGE_STORAGE } = await import("../../features/hook-message-injector")
|
const { MESSAGE_STORAGE } = await import("../../features/hook-message-injector")
|
||||||
|
|
||||||
@ -52,7 +42,6 @@ describe("prometheus-md-only", () => {
|
|||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rmSync(TEST_STORAGE_ROOT, { recursive: true, force: true })
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("agent name matching", () => {
|
describe("agent name matching", () => {
|
||||||
|
|||||||
@ -518,7 +518,7 @@ describe("todo-continuation-enforcer", () => {
|
|||||||
|
|
||||||
//#then
|
//#then
|
||||||
expect(promptCalls).toHaveLength(2)
|
expect(promptCalls).toHaveLength(2)
|
||||||
})
|
}, 20000)
|
||||||
|
|
||||||
test("should keep injecting even when todos remain unchanged across cycles", async () => {
|
test("should keep injecting even when todos remain unchanged across cycles", async () => {
|
||||||
//#given
|
//#given
|
||||||
@ -553,7 +553,7 @@ describe("todo-continuation-enforcer", () => {
|
|||||||
|
|
||||||
//#then — all 5 injections should fire (no stagnation cap)
|
//#then — all 5 injections should fire (no stagnation cap)
|
||||||
expect(promptCalls).toHaveLength(5)
|
expect(promptCalls).toHaveLength(5)
|
||||||
})
|
}, 30000)
|
||||||
|
|
||||||
test("should skip idle handling while injection is in flight", async () => {
|
test("should skip idle handling while injection is in flight", async () => {
|
||||||
//#given
|
//#given
|
||||||
@ -613,7 +613,7 @@ describe("todo-continuation-enforcer", () => {
|
|||||||
|
|
||||||
//#then
|
//#then
|
||||||
expect(promptCalls).toHaveLength(2)
|
expect(promptCalls).toHaveLength(2)
|
||||||
})
|
}, 20000)
|
||||||
|
|
||||||
test("should accept skipAgents option without error", async () => {
|
test("should accept skipAgents option without error", async () => {
|
||||||
// given - session with skipAgents configured for Prometheus
|
// given - session with skipAgents configured for Prometheus
|
||||||
|
|||||||
@ -1,16 +1,28 @@
|
|||||||
const { describe, test, expect, mock } = require("bun:test")
|
const { describe, test, expect, mock, beforeEach, afterEach } = require("bun:test")
|
||||||
|
|
||||||
mock.module("./session-creator", () => ({
|
const realCompletionPoller = require("./completion-poller")
|
||||||
createOrGetSession: mock(async () => ({ sessionID: "ses-test-123" })),
|
const realMessageProcessor = require("./message-processor")
|
||||||
}))
|
|
||||||
|
|
||||||
mock.module("./completion-poller", () => ({
|
const waitForCompletionMock = mock(async () => {})
|
||||||
waitForCompletion: mock(async () => {}),
|
const processMessagesMock = mock(async () => "agent response")
|
||||||
}))
|
|
||||||
|
|
||||||
mock.module("./message-processor", () => ({
|
beforeEach(() => {
|
||||||
processMessages: mock(async () => "agent response"),
|
waitForCompletionMock.mockClear()
|
||||||
}))
|
processMessagesMock.mockClear()
|
||||||
|
|
||||||
|
mock.module("./completion-poller", () => ({
|
||||||
|
waitForCompletion: waitForCompletionMock,
|
||||||
|
}))
|
||||||
|
|
||||||
|
mock.module("./message-processor", () => ({
|
||||||
|
processMessages: processMessagesMock,
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
mock.module("./completion-poller", () => ({ ...realCompletionPoller }))
|
||||||
|
mock.module("./message-processor", () => ({ ...realMessageProcessor }))
|
||||||
|
})
|
||||||
|
|
||||||
describe("executeSync", () => {
|
describe("executeSync", () => {
|
||||||
test("passes question=false via tools parameter to block question tool", async () => {
|
test("passes question=false via tools parameter to block question tool", async () => {
|
||||||
@ -27,6 +39,7 @@ describe("executeSync", () => {
|
|||||||
subagent_type: "explore",
|
subagent_type: "explore",
|
||||||
description: "test task",
|
description: "test task",
|
||||||
prompt: "find something",
|
prompt: "find something",
|
||||||
|
session_id: "ses-test-123",
|
||||||
}
|
}
|
||||||
|
|
||||||
const toolContext = {
|
const toolContext = {
|
||||||
@ -39,7 +52,10 @@ describe("executeSync", () => {
|
|||||||
|
|
||||||
const ctx = {
|
const ctx = {
|
||||||
client: {
|
client: {
|
||||||
session: { promptAsync },
|
session: {
|
||||||
|
promptAsync,
|
||||||
|
get: mock(async () => ({ data: { id: "ses-test-123" } })),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,6 +81,7 @@ describe("executeSync", () => {
|
|||||||
subagent_type: "librarian",
|
subagent_type: "librarian",
|
||||||
description: "search docs",
|
description: "search docs",
|
||||||
prompt: "find docs",
|
prompt: "find docs",
|
||||||
|
session_id: "ses-test-123",
|
||||||
}
|
}
|
||||||
|
|
||||||
const toolContext = {
|
const toolContext = {
|
||||||
@ -77,7 +94,10 @@ describe("executeSync", () => {
|
|||||||
|
|
||||||
const ctx = {
|
const ctx = {
|
||||||
client: {
|
client: {
|
||||||
session: { promptAsync },
|
session: {
|
||||||
|
promptAsync,
|
||||||
|
get: mock(async () => ({ data: { id: "ses-test-123" } })),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user