|
1 | 1 | import fs from "node:fs/promises"; |
2 | 2 | import os from "node:os"; |
3 | 3 | import path from "node:path"; |
| 4 | +import { MAX_TIMER_TIMEOUT_MS } from "openclaw/plugin-sdk/number-runtime"; |
4 | 5 | import { afterEach, describe, expect, it, vi } from "vitest"; |
5 | 6 |
|
6 | 7 | const { runtimeRegistry } = vi.hoisted(() => ({ |
@@ -90,7 +91,7 @@ vi.mock("./process-reaper.js", () => ({ |
90 | 91 |
|
91 | 92 | import { getAcpRuntimeBackend } from "../runtime-api.js"; |
92 | 93 | import type { OpenClawPluginServiceContext } from "../runtime-api.js"; |
93 | | -import { createAcpxRuntimeService } from "./service.js"; |
| 94 | +import { createAcpxRuntimeService, resolveAcpxTimerTimeoutMs } from "./service.js"; |
94 | 95 |
|
95 | 96 | const tempDirs: string[] = []; |
96 | 97 | const previousEnv = { |
@@ -196,6 +197,11 @@ function readFirstRuntimeFactoryInput(runtimeFactory: { mock: { calls: Array<Arr |
196 | 197 | } |
197 | 198 |
|
198 | 199 | describe("createAcpxRuntimeService", () => { |
| 200 | + it("caps configured timeout seconds to timer-safe milliseconds", () => { |
| 201 | + expect(resolveAcpxTimerTimeoutMs(0.001)).toBe(1); |
| 202 | + expect(resolveAcpxTimerTimeoutMs(Number.MAX_SAFE_INTEGER)).toBe(MAX_TIMER_TIMEOUT_MS); |
| 203 | + }); |
| 204 | + |
199 | 205 | it("registers and unregisters the embedded backend", async () => { |
200 | 206 | const workspaceDir = await makeTempDir(); |
201 | 207 | const ctx = createServiceContext(workspaceDir); |
@@ -580,6 +586,36 @@ describe("createAcpxRuntimeService", () => { |
580 | 586 | await service.stop?.(ctx); |
581 | 587 | }); |
582 | 588 |
|
| 589 | + it("caps oversized plugin timeouts before constructing the default acpx runtime", async () => { |
| 590 | + process.env.OPENCLAW_ACPX_RUNTIME_STARTUP_PROBE = "0"; |
| 591 | + const workspaceDir = await makeTempDir(); |
| 592 | + const ctx = createServiceContext(workspaceDir); |
| 593 | + const service = createAcpxRuntimeService({ |
| 594 | + pluginConfig: { timeoutSeconds: Number.MAX_SAFE_INTEGER }, |
| 595 | + }); |
| 596 | + |
| 597 | + await service.start(ctx); |
| 598 | + |
| 599 | + const backend = getAcpRuntimeBackend("acpx"); |
| 600 | + if (!backend) { |
| 601 | + throw new Error("expected ACPX runtime backend"); |
| 602 | + } |
| 603 | + const backendRuntime = backend.runtime as { |
| 604 | + ensureSession(input: { agent: string; mode: string; sessionKey: string }): Promise<unknown>; |
| 605 | + }; |
| 606 | + |
| 607 | + await backendRuntime.ensureSession({ |
| 608 | + agent: "codex", |
| 609 | + mode: "oneshot", |
| 610 | + sessionKey: "agent:codex:acp:test", |
| 611 | + }); |
| 612 | + |
| 613 | + const [options] = acpxRuntimeConstructorMock.mock.calls[0] ?? []; |
| 614 | + expect(options).toHaveProperty("timeoutMs", MAX_TIMER_TIMEOUT_MS); |
| 615 | + |
| 616 | + await service.stop?.(ctx); |
| 617 | + }); |
| 618 | + |
583 | 619 | it("runs the embedded runtime probe at startup when explicitly enabled and reports health", async () => { |
584 | 620 | process.env.OPENCLAW_ACPX_RUNTIME_STARTUP_PROBE = "1"; |
585 | 621 | const workspaceDir = await makeTempDir(); |
|
0 commit comments