diff --git a/src/agents/context.test.ts b/src/agents/context.test.ts index b114b6b0d19..f0482514312 100644 --- a/src/agents/context.test.ts +++ b/src/agents/context.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it } from "vitest"; +import { describe, expect, it, vi } from "vitest"; import { ANTHROPIC_CONTEXT_1M_TOKENS, applyConfiguredContextWindows, @@ -7,6 +7,8 @@ import { } from "./context.js"; import { createSessionManagerRuntimeRegistry } from "./pi-hooks/session-manager-runtime-registry.js"; +vi.mock("../config/config.js", () => ({ loadConfig: () => ({}) })); + function testModelContextWindow(id: string, contextWindow: number) { return { id, diff --git a/src/agents/runtime-plan/build.test.ts b/src/agents/runtime-plan/build.test.ts index 77ec3a18450..5f702cef48d 100644 --- a/src/agents/runtime-plan/build.test.ts +++ b/src/agents/runtime-plan/build.test.ts @@ -1,7 +1,21 @@ -import { describe, expect, it } from "vitest"; +import { describe, expect, it, vi } from "vitest"; import { createParameterFreeTool } from "../../../test/helpers/agents/schema-normalization-runtime-contract.js"; import { buildAgentRuntimePlan } from "./build.js"; +vi.mock("../../plugins/provider-hook-runtime.js", () => ({ + __testing: {}, + clearProviderRuntimeHookCache: vi.fn(), + prepareProviderExtraParams: vi.fn(() => undefined), + resetProviderRuntimeHookCacheForTest: vi.fn(), + resolveProviderAuthProfileId: vi.fn(() => undefined), + resolveProviderExtraParamsForTransport: vi.fn(() => undefined), + resolveProviderFollowupFallbackRoute: vi.fn(() => undefined), + resolveProviderHookPlugin: vi.fn(() => undefined), + resolveProviderPluginsForHooks: vi.fn(() => []), + resolveProviderRuntimePlugin: vi.fn(() => undefined), + wrapProviderStreamFn: vi.fn(() => undefined), +})); + describe("AgentRuntimePlan", () => { it("records resolved model, auth, transport, tool, delivery, and observability policy", () => { const plan = buildAgentRuntimePlan({ diff --git a/src/auto-reply/reply/get-reply.imports.test.ts b/src/auto-reply/reply/get-reply.imports.test.ts index aadf149ffdc..0b27efc5fa9 100644 --- a/src/auto-reply/reply/get-reply.imports.test.ts +++ b/src/auto-reply/reply/get-reply.imports.test.ts @@ -1,31 +1,55 @@ -import { describe, expect, it, vi } from "vitest"; -import { importFreshModule } from "../../../test/helpers/import-fresh.ts"; +import { readFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import ts from "typescript"; +import { describe, expect, it } from "vitest"; + +const getReplyPath = resolve(dirname(fileURLToPath(import.meta.url)), "get-reply.ts"); +const lazyRuntimeSpecifiers = [ + "./session-reset-model.runtime.js", + "./stage-sandbox-media.runtime.js", +] as const; + +function readGetReplyModuleImports() { + const sourceText = readFileSync(getReplyPath, "utf8"); + const sourceFile = ts.createSourceFile(getReplyPath, sourceText, ts.ScriptTarget.Latest, true); + const staticImports = new Set(); + const dynamicImports = new Set(); + + function visit(node: ts.Node) { + if ( + ts.isImportDeclaration(node) && + ts.isStringLiteral(node.moduleSpecifier) && + !node.importClause?.isTypeOnly + ) { + staticImports.add(node.moduleSpecifier.text); + } + + if ( + ts.isCallExpression(node) && + node.expression.kind === ts.SyntaxKind.ImportKeyword && + node.arguments.length === 1 && + ts.isStringLiteral(node.arguments[0]) + ) { + dynamicImports.add(node.arguments[0].text); + } + + ts.forEachChild(node, visit); + } + + visit(sourceFile); + return { dynamicImports, staticImports }; +} describe("get-reply module imports", () => { - it("does not load reset-model runtime on module import", async () => { - const resetModelRuntimeLoads = vi.fn(); - const sandboxMediaRuntimeLoads = vi.fn(); - vi.doMock("./session-reset-model.runtime.js", async () => { - resetModelRuntimeLoads(); - return await vi.importActual( - "./session-reset-model.runtime.js", - ); - }); - vi.doMock("./stage-sandbox-media.runtime.js", async () => { - sandboxMediaRuntimeLoads(); - return await vi.importActual( - "./stage-sandbox-media.runtime.js", - ); - }); + it("keeps heavy runtime boundaries on dynamic imports", () => { + const { dynamicImports, staticImports } = readGetReplyModuleImports(); - await importFreshModule( - import.meta.url, - "./get-reply.js?scope=no-runtime-imports", - ); - - expect(resetModelRuntimeLoads).not.toHaveBeenCalled(); - expect(sandboxMediaRuntimeLoads).not.toHaveBeenCalled(); - vi.doUnmock("./session-reset-model.runtime.js"); - vi.doUnmock("./stage-sandbox-media.runtime.js"); + for (const specifier of lazyRuntimeSpecifiers) { + expect(staticImports.has(specifier), `${specifier} should stay lazy`).toBe(false); + expect(dynamicImports.has(specifier), `${specifier} should remain dynamically imported`).toBe( + true, + ); + } }); }); diff --git a/test/extension-package-tsc-boundary.test.ts b/test/extension-package-tsc-boundary.test.ts index 0acc613e50a..413a9322af3 100644 --- a/test/extension-package-tsc-boundary.test.ts +++ b/test/extension-package-tsc-boundary.test.ts @@ -8,7 +8,6 @@ const CHECK_EXTENSION_PACKAGE_BOUNDARY_BIN = resolve( "scripts/check-extension-package-tsc-boundary.mjs", ); const SHOULD_RUN_BOUNDARY_SCRIPT_WRAPPER = - process.env.GITHUB_ACTIONS !== "true" || process.env.OPENCLAW_RUN_EXTENSION_PACKAGE_BOUNDARY_TEST === "1"; function runNode(args: string[], timeout: number) { @@ -20,8 +19,8 @@ function runNode(args: string[], timeout: number) { }); } -// The CI check-additional job runs this script directly. Avoid duplicating the cold -// 97-extension compile inside the full node test shard. +// The CI check-additional job and package scripts run this script directly. Keep this +// wrapper opt-in so full Vitest runs do not duplicate the cold extension compile. describe.skipIf(!SHOULD_RUN_BOUNDARY_SCRIPT_WRAPPER)( "opt-in extension package TypeScript boundaries", () => {