diff --git a/tests/ipcMain/helpers.ts b/tests/ipcMain/helpers.ts index c1d3e69b0..08c305dcf 100644 --- a/tests/ipcMain/helpers.ts +++ b/tests/ipcMain/helpers.ts @@ -20,6 +20,7 @@ import type { ToolPolicy } from "../../src/utils/tools/toolPolicy"; export const INIT_HOOK_WAIT_MS = 1500; // Wait for async init hook completion (local runtime) export const SSH_INIT_WAIT_MS = 7000; // SSH init includes sync + checkout + hook, takes longer export const HAIKU_MODEL = "anthropic:claude-haiku-4-5"; // Fast model for tests +export const GPT_5_MINI_MODEL = "openai:gpt-5-mini"; // Fastest model for performance-critical tests export const TEST_TIMEOUT_LOCAL_MS = 25000; // Recommended timeout for local runtime tests export const TEST_TIMEOUT_SSH_MS = 60000; // Recommended timeout for SSH runtime tests export const STREAM_TIMEOUT_LOCAL_MS = 15000; // Stream timeout for local runtime @@ -200,6 +201,8 @@ export async function sendMessageAndWait( { model, toolPolicy, + thinkingLevel: "off", // Disable reasoning for fast test execution + mode: "exec", // Execute commands directly, don't propose plans } ); diff --git a/tests/ipcMain/initWorkspace.test.ts b/tests/ipcMain/initWorkspace.test.ts index a9f542957..c639613e8 100644 --- a/tests/ipcMain/initWorkspace.test.ts +++ b/tests/ipcMain/initWorkspace.test.ts @@ -5,7 +5,6 @@ import { validateApiKeys, getApiKey, setupProviders, - preloadTestModules, type TestEnvironment, } from "./setup"; import { IPC_CHANNELS, getChatChannel } from "../../src/constants/ipc-constants"; @@ -460,9 +459,6 @@ let sshConfig: SSHServerConfig | undefined; describeIntegration("Init Queue - Runtime Matrix", () => { beforeAll(async () => { - // Preload AI SDK providers and tokenizers - await preloadTestModules(); - // Only start SSH server if Docker is available if (await isDockerAvailable()) { console.log("Starting SSH server container for init queue tests..."); diff --git a/tests/ipcMain/removeWorkspace.test.ts b/tests/ipcMain/removeWorkspace.test.ts index aa395b107..620775272 100644 --- a/tests/ipcMain/removeWorkspace.test.ts +++ b/tests/ipcMain/removeWorkspace.test.ts @@ -11,7 +11,6 @@ import { createTestEnvironment, cleanupTestEnvironment, shouldRunIntegrationTests, - preloadTestModules, type TestEnvironment, } from "./setup"; import { IPC_CHANNELS } from "../../src/constants/ipc-constants"; @@ -104,8 +103,6 @@ async function makeWorkspaceDirty(env: TestEnvironment, workspaceId: string): Pr describeIntegration("Workspace deletion integration tests", () => { beforeAll(async () => { - await preloadTestModules(); - // Check if Docker is available (required for SSH tests) if (!(await isDockerAvailable())) { throw new Error( diff --git a/tests/ipcMain/runtimeExecuteBash.test.ts b/tests/ipcMain/runtimeExecuteBash.test.ts index 4861bcced..f02d0f61e 100644 --- a/tests/ipcMain/runtimeExecuteBash.test.ts +++ b/tests/ipcMain/runtimeExecuteBash.test.ts @@ -22,7 +22,7 @@ import { createWorkspaceWithInit, sendMessageAndWait, extractTextFromEvents, - HAIKU_MODEL, + GPT_5_MINI_MODEL, TEST_TIMEOUT_LOCAL_MS, TEST_TIMEOUT_SSH_MS, } from "./helpers"; @@ -46,7 +46,7 @@ const describeIntegration = shouldRunIntegrationTests() ? describe : describe.sk // Validate API keys before running tests if (shouldRunIntegrationTests()) { - validateApiKeys(["ANTHROPIC_API_KEY"]); + validateApiKeys(["OPENAI_API_KEY"]); } // SSH server config (shared across all SSH tests) @@ -101,8 +101,8 @@ describeIntegration("Runtime Bash Execution", () => { try { // Setup provider await setupProviders(env.mockIpcRenderer, { - anthropic: { - apiKey: getApiKey("ANTHROPIC_API_KEY"), + openai: { + apiKey: getApiKey("OPENAI_API_KEY"), }, }); @@ -124,7 +124,7 @@ describeIntegration("Runtime Bash Execution", () => { env, workspaceId, 'Run the bash command "echo Hello World"', - HAIKU_MODEL, + GPT_5_MINI_MODEL, BASH_ONLY ); @@ -159,8 +159,8 @@ describeIntegration("Runtime Bash Execution", () => { try { // Setup provider await setupProviders(env.mockIpcRenderer, { - anthropic: { - apiKey: getApiKey("ANTHROPIC_API_KEY"), + openai: { + apiKey: getApiKey("OPENAI_API_KEY"), }, }); @@ -182,7 +182,7 @@ describeIntegration("Runtime Bash Execution", () => { env, workspaceId, 'Run bash command: export TEST_VAR="test123" && echo "Value: $TEST_VAR"', - HAIKU_MODEL, + GPT_5_MINI_MODEL, BASH_ONLY ); @@ -217,8 +217,8 @@ describeIntegration("Runtime Bash Execution", () => { try { // Setup provider await setupProviders(env.mockIpcRenderer, { - anthropic: { - apiKey: getApiKey("ANTHROPIC_API_KEY"), + openai: { + apiKey: getApiKey("OPENAI_API_KEY"), }, }); @@ -240,7 +240,7 @@ describeIntegration("Runtime Bash Execution", () => { env, workspaceId, 'Run bash: echo "Test with $dollar and \\"quotes\\" and `backticks`"', - HAIKU_MODEL, + GPT_5_MINI_MODEL, BASH_ONLY ); @@ -276,8 +276,8 @@ describeIntegration("Runtime Bash Execution", () => { try { // Setup provider await setupProviders(env.mockIpcRenderer, { - anthropic: { - apiKey: getApiKey("ANTHROPIC_API_KEY"), + openai: { + apiKey: getApiKey("OPENAI_API_KEY"), }, }); @@ -295,25 +295,26 @@ describeIntegration("Runtime Bash Execution", () => { try { // Create a test file with JSON content + // Using gpt-5-mini for speed (bash tool tests don't need reasoning power) await sendMessageAndWait( env, workspaceId, 'Run bash: echo \'{"test": "data"}\' > /tmp/test.json', - HAIKU_MODEL, + GPT_5_MINI_MODEL, BASH_ONLY ); - // Test command that pipes file through stdin-reading command (jq) + // Test command that pipes file through stdin-reading command (grep) // This would hang forever if stdin.close() was used instead of stdin.abort() // Regression test for: https://github.com/coder/cmux/issues/503 const startTime = Date.now(); const events = await sendMessageAndWait( env, workspaceId, - "Run bash with 3s timeout: cat /tmp/test.json | jq '.'", - HAIKU_MODEL, + "Run bash: cat /tmp/test.json | grep test", + GPT_5_MINI_MODEL, BASH_ONLY, - 15000 // 15s max wait - should complete in < 5s + 10000 // 10s timeout - should complete in ~4s per API call ); const duration = Date.now() - startTime; @@ -325,10 +326,9 @@ describeIntegration("Runtime Bash Execution", () => { expect(responseText).toContain("data"); // Verify command completed quickly (not hanging until timeout) - // Should complete in under 15 seconds for SSH, 10 seconds for local - // Generous timeouts to account for CI runner variability - // (actual hangs would hit bash tool's 180s timeout) - const maxDuration = type === "ssh" ? 15000 : 10000; + // With tokenizer preloading, both local and SSH complete in ~8s total + // Actual hangs would hit bash tool's 180s timeout + const maxDuration = 10000; expect(duration).toBeLessThan(maxDuration); // Verify bash tool was called diff --git a/tests/ipcMain/runtimeFileEditing.test.ts b/tests/ipcMain/runtimeFileEditing.test.ts index d9ed72d07..037b8db36 100644 --- a/tests/ipcMain/runtimeFileEditing.test.ts +++ b/tests/ipcMain/runtimeFileEditing.test.ts @@ -16,7 +16,6 @@ import { validateApiKeys, getApiKey, setupProviders, - preloadTestModules, type TestEnvironment, } from "./setup"; import { IPC_CHANNELS } from "../../src/constants/ipc-constants"; @@ -65,9 +64,6 @@ let sshConfig: SSHServerConfig | undefined; describeIntegration("Runtime File Editing Tools", () => { beforeAll(async () => { - // Preload AI SDK providers and tokenizers to avoid race conditions in concurrent tests - await preloadTestModules(); - // Check if Docker is available (required for SSH tests) if (!(await isDockerAvailable())) { throw new Error( diff --git a/tests/setup.ts b/tests/setup.ts index d598a9f14..13a4d1bc2 100644 --- a/tests/setup.ts +++ b/tests/setup.ts @@ -23,3 +23,18 @@ if (typeof globalThis.File === "undefined") { lastModified: number; }; } + +// Preload tokenizer and AI SDK modules for integration tests +// This eliminates ~10s initialization delay on first use +if (process.env.TEST_INTEGRATION === "1") { + // Store promise globally to ensure it blocks subsequent test execution + (globalThis as any).__cmuxPreloadPromise = (async () => { + const { preloadTestModules } = await import("./ipcMain/setup"); + await preloadTestModules(); + })(); + + // Add a global beforeAll to block until preload completes + beforeAll(async () => { + await (globalThis as any).__cmuxPreloadPromise; + }, 30000); // 30s timeout for preload +}