diff --git a/extensions/alibaba/video-generation-provider.test.ts b/extensions/alibaba/video-generation-provider.test.ts index 590b19de6fe4..6709bfc1937c 100644 --- a/extensions/alibaba/video-generation-provider.test.ts +++ b/extensions/alibaba/video-generation-provider.test.ts @@ -1,4 +1,3 @@ -// Alibaba tests cover video generation provider plugin behavior. import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; @@ -9,6 +8,7 @@ import { import { getProviderHttpMocks, installProviderHttpMockCleanup, + requireFirstPostJsonRecordRequest as requireFirstPostJsonRequest, } from "openclaw/plugin-sdk/provider-http-test-mocks"; import { expectDashscopeVideoTaskPoll, @@ -16,6 +16,8 @@ import { expectSuccessfulDashscopeVideoResult, mockSuccessfulDashscopeVideoTask, } from "openclaw/plugin-sdk/provider-test-contracts"; +// Alibaba tests cover video generation provider plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { DASHSCOPE_WAN_VIDEO_MODELS, DEFAULT_DASHSCOPE_WAN_VIDEO_MODEL, @@ -50,20 +52,7 @@ function clearAlibabaAuthEnvironment(): void { } } -function requireRecord(value: unknown, label: string): Record { - if (value === null || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be a record`); - } - return value as Record; -} - -function requireFirstPostJsonRequest(label: string): Record { - const [call] = postJsonRequestMock.mock.calls; - if (!call) { - throw new Error(`expected ${label}`); - } - return requireRecord(call[0], label); -} +const requireRecord = createRequireRecord("record", "expected-label-record"); describe("alibaba video generation provider", () => { it("declares explicit mode capabilities", () => { @@ -274,7 +263,7 @@ describe("alibaba video generation provider", () => { }); expect(postJsonRequestMock).toHaveBeenCalledOnce(); - const request = requireFirstPostJsonRequest("DashScope request"); + const request = requireFirstPostJsonRequest(postJsonRequestMock, "DashScope request"); expect(request.url).toBe( "https://dashscope-intl.aliyuncs.com/api/v1/services/aigc/video-generation/video-synthesis", ); @@ -338,7 +327,10 @@ describe("alibaba video generation provider", () => { request: requestPolicy, }), ); - const request = requireFirstPostJsonRequest("DashScope request with request policy"); + const request = requireFirstPostJsonRequest( + postJsonRequestMock, + "DashScope request with request policy", + ); expect(request.allowPrivateNetwork).toBe(true); expect(request.dispatcherPolicy).toBe(dispatcherPolicy); expect(request.headers).toBeInstanceOf(Headers); diff --git a/extensions/amazon-bedrock-mantle/mantle-anthropic.runtime.test.ts b/extensions/amazon-bedrock-mantle/mantle-anthropic.runtime.test.ts index 27fdbb08abe9..6fa24510593e 100644 --- a/extensions/amazon-bedrock-mantle/mantle-anthropic.runtime.test.ts +++ b/extensions/amazon-bedrock-mantle/mantle-anthropic.runtime.test.ts @@ -1,5 +1,6 @@ -// Amazon Bedrock Mantle tests cover mantle anthropic plugin behavior. import type { Model } from "openclaw/plugin-sdk/llm"; +// Amazon Bedrock Mantle tests cover mantle anthropic plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { describe, expect, it, vi } from "vitest"; import { createMantleAnthropicStreamFn } from "./mantle-anthropic.runtime.js"; @@ -29,12 +30,7 @@ function createTestDeps() { }; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`Expected ${label} to be an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-object-capitalized"); function mockCallArg(mock: { mock: { calls: unknown[][] } }, index = 0, argIndex = 0): unknown { const call = mock.mock.calls[index]; diff --git a/extensions/amazon-bedrock/index.test.ts b/extensions/amazon-bedrock/index.test.ts index a8be60ebfc07..79e60ab46616 100644 --- a/extensions/amazon-bedrock/index.test.ts +++ b/extensions/amazon-bedrock/index.test.ts @@ -1,4 +1,3 @@ -// Amazon Bedrock tests cover index plugin behavior. import { readFileSync } from "node:fs"; import { resolve } from "node:path"; import { expectDefined } from "@openclaw/normalization-core"; @@ -10,6 +9,8 @@ import { registerSingleProviderPlugin, } from "openclaw/plugin-sdk/plugin-test-runtime"; import { withEnvAsync } from "openclaw/plugin-sdk/test-env"; +// Amazon Bedrock tests cover index plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { supportsBedrockPromptCaching } from "./bedrock-options.js"; import { resetBedrockDiscoveryCacheForTest } from "./discovery.js"; @@ -237,12 +238,7 @@ function runtimePluginConfig(config?: Record): OpenClawConfig { } as OpenClawConfig; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be a record`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-record"); function requireArray(value: unknown, label: string): unknown[] { if (!Array.isArray(value)) { diff --git a/extensions/anthropic/index.test.ts b/extensions/anthropic/index.test.ts index 7b0ed236591f..4e6ea3cca71f 100644 --- a/extensions/anthropic/index.test.ts +++ b/extensions/anthropic/index.test.ts @@ -1,4 +1,3 @@ -// Anthropic tests cover index plugin behavior. import { calculateCost, type Usage } from "openclaw/plugin-sdk/llm"; import type { ProviderResolveDynamicModelContext, @@ -9,6 +8,8 @@ import { capturePluginRegistration, registerSingleProviderPlugin, } from "openclaw/plugin-sdk/plugin-test-runtime"; +// Anthropic tests cover index plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterAll, beforeEach, describe, expect, it, vi } from "vitest"; const { readClaudeCliCredentialsForSetupMock, readClaudeCliCredentialsForRuntimeMock } = vi.hoisted( @@ -53,12 +54,7 @@ function createModelRegistry(models: ProviderRuntimeModel[]) { }; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function expectFields(value: unknown, fields: Record) { const record = requireRecord(value, "record"); diff --git a/extensions/browser/src/browser/routes/agent.existing-session.test.ts b/extensions/browser/src/browser/routes/agent.existing-session.test.ts index 349fe4b652c2..98413aad88b3 100644 --- a/extensions/browser/src/browser/routes/agent.existing-session.test.ts +++ b/extensions/browser/src/browser/routes/agent.existing-session.test.ts @@ -1,4 +1,5 @@ // Browser tests cover agent.existing session plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { EXISTING_SESSION_LIMITS } from "./existing-session-limits.js"; import { @@ -126,12 +127,7 @@ function getDialogHookPostHandler() { return handler; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function callArg(mock: unknown, callIndex: number, argIndex: number, label: string) { const calls = (mock as { mock?: { calls?: Array> } }).mock?.calls ?? []; diff --git a/extensions/browser/src/browser/server.agent-contract-form-layout-act-commands.test.ts b/extensions/browser/src/browser/server.agent-contract-form-layout-act-commands.test.ts index 73963d88142d..ad66780fb9b7 100644 --- a/extensions/browser/src/browser/server.agent-contract-form-layout-act-commands.test.ts +++ b/extensions/browser/src/browser/server.agent-contract-form-layout-act-commands.test.ts @@ -1,8 +1,9 @@ -// Browser tests cover server.agent contract form layout act commands plugin behavior. import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { expectDefined } from "@openclaw/normalization-core"; +// Browser tests cover server.agent contract form layout act commands plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeAll, describe, expect, it } from "vitest"; import "../test-support/browser-security.mock.js"; import { DEFAULT_DOWNLOAD_DIR, DEFAULT_TRACE_DIR, DEFAULT_UPLOAD_DIR } from "./paths.js"; @@ -176,16 +177,7 @@ async function withSymlinkPathEscape(params: { type MockWithCalls = { mock: { calls: unknown[][] } }; -function isRecord(value: unknown): value is Record { - return typeof value === "object" && value !== null && !Array.isArray(value); -} - -function requireRecord(value: unknown, label: string): Record { - if (!isRecord(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); function expectRecordFields(value: unknown, label: string, expected: Record) { const record = requireRecord(value, label); diff --git a/extensions/byteplus/video-generation-provider.test.ts b/extensions/byteplus/video-generation-provider.test.ts index 5fc39de412c9..989d516af094 100644 --- a/extensions/byteplus/video-generation-provider.test.ts +++ b/extensions/byteplus/video-generation-provider.test.ts @@ -1,5 +1,6 @@ // Byteplus tests cover video generation provider plugin behavior. import { expectExplicitVideoGenerationCapabilities } from "openclaw/plugin-sdk/provider-test-contracts"; +import { streamedJsonResponse } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeAll, describe, expect, it, vi } from "vitest"; // Submit/poll transport is mocked locally so each test can inject the BytePlus task JSON @@ -189,20 +190,6 @@ function streamedVideoResponse(bytes: string): Response { ); } -// BytePlus submit/poll task JSON is now read through the byte-bounded reader, so the -// mocked responses must expose a real readable body (not just a json() shortcut). -function streamedJsonResponse(payload: unknown): Response { - return new Response( - new ReadableStream({ - start(controller) { - controller.enqueue(new TextEncoder().encode(JSON.stringify(payload))); - controller.close(); - }, - }), - { status: 200, headers: { "content-type": "application/json" } }, - ); -} - // Builds a JSON body larger than the shared 16 MiB readProviderJsonResponse cap so the // bounded reader cancels the stream mid-flight; if the cap were removed the reader would // buffer the whole advertised payload before parsing. Tracks how many bytes were pulled diff --git a/extensions/codex/src/app-server/approval-bridge.test.ts b/extensions/codex/src/app-server/approval-bridge.test.ts index 559aa75db921..fa684ec4d5b2 100644 --- a/extensions/codex/src/app-server/approval-bridge.test.ts +++ b/extensions/codex/src/app-server/approval-bridge.test.ts @@ -1,4 +1,3 @@ -// Codex tests cover approval bridge plugin behavior. import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; @@ -11,6 +10,8 @@ import { runBeforeToolCallHook, type EmbeddedRunAttemptParams, } from "openclaw/plugin-sdk/agent-harness-runtime"; +// Codex tests cover approval bridge plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { handleCodexAppServerApprovalRequest } from "./approval-bridge.js"; import { @@ -46,12 +47,7 @@ const mockResolveNativeHookRelayDeferredToolApproval = vi.mocked( const mockReviewExecRequestWithConfiguredModel = vi.mocked(reviewExecRequestWithConfiguredModel); const mockRunBeforeToolCallHook = vi.mocked(runBeforeToolCallHook); -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`Expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-capitalized"); function gatewayCallAt(callIndex = 0) { const call = mockCallGatewayTool.mock.calls[callIndex]; diff --git a/extensions/codex/src/app-server/computer-use.test.ts b/extensions/codex/src/app-server/computer-use.test.ts index 100dabe0c0d5..45185846ed43 100644 --- a/extensions/codex/src/app-server/computer-use.test.ts +++ b/extensions/codex/src/app-server/computer-use.test.ts @@ -1,6 +1,7 @@ -// Codex tests cover computer use plugin behavior. import fs from "node:fs"; import path from "node:path"; +// Codex tests cover computer use plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, describe, expect, it, vi } from "vitest"; import { resolveCodexAppServerRuntimeOptions } from "./config.js"; import { acquireCodexNativeConfigFence } from "./native-config-fence.js"; @@ -61,12 +62,7 @@ async function expectSetupErrorStatus( expectStatusFields(status, fields); } -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null) { - throw new Error(`${label} was not an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "label-not-object"); function requestCalls( request: CodexComputerUseRequest, diff --git a/extensions/codex/src/app-server/config.test.ts b/extensions/codex/src/app-server/config.test.ts index 4943f32379ac..8496377fe306 100644 --- a/extensions/codex/src/app-server/config.test.ts +++ b/extensions/codex/src/app-server/config.test.ts @@ -1,8 +1,9 @@ -// Codex tests cover config plugin behavior. import fs from "node:fs/promises"; import path from "node:path"; import { MAX_TIMER_TIMEOUT_MS } from "openclaw/plugin-sdk/number-runtime"; import { withTempDir } from "openclaw/plugin-sdk/test-env"; +// Codex tests cover config plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { describe, expect, it, vi } from "vitest"; import { canUseCodexModelBackedApprovalsReviewerForModel, @@ -45,12 +46,7 @@ function envRef(id: string) { return { source: "env" as const, provider: "default", id }; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`Expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-capitalized"); function expectFields( value: unknown, diff --git a/extensions/codex/src/app-server/dynamic-tools.test.ts b/extensions/codex/src/app-server/dynamic-tools.test.ts index 4dabfaabb60a..0b69a065abee 100644 --- a/extensions/codex/src/app-server/dynamic-tools.test.ts +++ b/extensions/codex/src/app-server/dynamic-tools.test.ts @@ -1,4 +1,3 @@ -// Codex tests cover dynamic tools plugin behavior. import { createHash } from "node:crypto"; import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; import os from "node:os"; @@ -26,6 +25,8 @@ import { createTestRegistry, setActivePluginRegistry, } from "openclaw/plugin-sdk/plugin-test-runtime"; +// Codex tests cover dynamic tools plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { createOpenClawTestState } from "openclaw/plugin-sdk/test-state"; import { estimateToolResultTextChars } from "openclaw/plugin-sdk/text-utility-runtime"; import { afterEach, describe, expect, it, vi } from "vitest"; @@ -105,12 +106,7 @@ function expectInputText(text: string) { }; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function callArg( mock: { mock: { calls: Array> } }, callIndex: number, diff --git a/extensions/codex/src/app-server/openclaw-owned-tool-runtime-contract.test.ts b/extensions/codex/src/app-server/openclaw-owned-tool-runtime-contract.test.ts index 9df679278df8..4696f209c483 100644 --- a/extensions/codex/src/app-server/openclaw-owned-tool-runtime-contract.test.ts +++ b/extensions/codex/src/app-server/openclaw-owned-tool-runtime-contract.test.ts @@ -1,4 +1,3 @@ -// Codex tests cover openclaw owned tool runtime contract plugin behavior. import type { AnyAgentTool } from "openclaw/plugin-sdk/agent-harness"; import { wrapToolWithBeforeToolCallHook } from "openclaw/plugin-sdk/agent-harness-runtime"; import { @@ -8,6 +7,8 @@ import { resetOpenClawOwnedToolHooks, textToolResult, } from "openclaw/plugin-sdk/agent-runtime-test-contracts"; +// Codex tests cover openclaw owned tool runtime contract plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, describe, expect, it, vi } from "vitest"; import { createCodexDynamicToolBridge } from "./dynamic-tools.js"; @@ -21,12 +22,7 @@ function createContractTool(overrides: Partial): AnyAgentTool { } as unknown as AnyAgentTool; } -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null) { - throw new Error(`${label} was not an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "label-not-object"); function expectRecordFields(record: Record, fields: Record) { for (const [key, value] of Object.entries(fields)) { diff --git a/extensions/codex/src/app-server/run-attempt.context-engine.test.ts b/extensions/codex/src/app-server/run-attempt.context-engine.test.ts index 0d04cae007f5..604425632c71 100644 --- a/extensions/codex/src/app-server/run-attempt.context-engine.test.ts +++ b/extensions/codex/src/app-server/run-attempt.context-engine.test.ts @@ -1,4 +1,3 @@ -// Codex tests cover run attempt.context engine plugin behavior. import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; @@ -20,6 +19,8 @@ import { createMockPluginRegistry } from "openclaw/plugin-sdk/plugin-test-runtim import { registerSandboxBackend } from "openclaw/plugin-sdk/sandbox"; import { upsertSessionEntry } from "openclaw/plugin-sdk/session-store-runtime"; import { formatSqliteSessionFileMarker } from "openclaw/plugin-sdk/sqlite-runtime-testing"; +// Codex tests cover run attempt.context engine plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { readAttemptTerminal } from "./attempt-terminal.test-helper.js"; import { shouldEnableCodexAppServerNativeToolSurface } from "./dynamic-tool-build.js"; @@ -382,12 +383,7 @@ function createContextEngine(overrides: Partial = {}): ContextEng type MockCallReader = { mock: { calls: unknown[][] } }; -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); function optionalString(value: unknown): string { return typeof value === "string" ? value : ""; diff --git a/extensions/codex/src/commands.test.ts b/extensions/codex/src/commands.test.ts index dec1ce70fd44..5af9ace52108 100644 --- a/extensions/codex/src/commands.test.ts +++ b/extensions/codex/src/commands.test.ts @@ -1,4 +1,3 @@ -// Codex tests cover commands plugin behavior. import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; @@ -11,6 +10,8 @@ import { import { MODEL_SELECTION_LOCKED_MESSAGE } from "openclaw/plugin-sdk/model-session-runtime"; import type { PluginCommandContext, PluginCommandResult } from "openclaw/plugin-sdk/plugin-entry"; import { upsertSessionEntry } from "openclaw/plugin-sdk/session-store-runtime"; +// Codex tests cover commands plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { CODEX_CONTROL_METHODS } from "./app-server/capabilities.js"; import type { CodexComputerUseStatus } from "./app-server/computer-use.js"; @@ -302,12 +303,7 @@ function codexRateLimitPayload(params: { }; } -function requireRecord(value: unknown, message: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(message); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "message"); function mockCall(mockFn: ReturnType, callIndex = 0): ReadonlyArray { const call = mockFn.mock.calls[callIndex]; diff --git a/extensions/deepinfra/speech-provider.test.ts b/extensions/deepinfra/speech-provider.test.ts index b26b32168e1c..cf6efb16e3e6 100644 --- a/extensions/deepinfra/speech-provider.test.ts +++ b/extensions/deepinfra/speech-provider.test.ts @@ -1,4 +1,5 @@ // Deepinfra tests cover speech provider plugin behavior. +import { requireFirstPostJsonRequest } from "openclaw/plugin-sdk/test-fixtures"; import { afterAll, afterEach, describe, expect, it, vi } from "vitest"; import { buildDeepInfraSpeechProvider } from "./speech-provider.js"; @@ -33,14 +34,6 @@ afterAll(() => { vi.resetModules(); }); -function requireFirstPostJsonRequest(): unknown { - const [call] = postJsonRequestMock.mock.calls; - if (!call) { - throw new Error("expected DeepInfra speech request"); - } - return call[0]; -} - describe("deepinfra speech provider", () => { afterEach(() => { assertOkOrThrowHttpErrorMock.mockClear(); @@ -126,7 +119,10 @@ describe("deepinfra speech provider", () => { ], ]); expect(postJsonRequestMock).toHaveBeenCalledOnce(); - const postRequest = requireFirstPostJsonRequest(); + const postRequest = requireFirstPostJsonRequest( + postJsonRequestMock, + "DeepInfra speech request", + ); const postRequestHeaders = Reflect.get(postRequest ?? {}, "headers"); expect(postRequestHeaders).toBeInstanceOf(Headers); expect(Object.fromEntries((postRequestHeaders as Headers).entries())).toEqual({ diff --git a/extensions/deepinfra/video-generation-provider.test.ts b/extensions/deepinfra/video-generation-provider.test.ts index 1e76562976bc..8bfb4dd0081a 100644 --- a/extensions/deepinfra/video-generation-provider.test.ts +++ b/extensions/deepinfra/video-generation-provider.test.ts @@ -3,6 +3,7 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts"; import { getProviderHttpMocks, installProviderHttpMockCleanup, + requireFirstPostJsonRequest, } from "openclaw/plugin-sdk/provider-http-test-mocks"; import { expectExplicitVideoGenerationCapabilities } from "openclaw/plugin-sdk/provider-test-contracts"; import { beforeAll, describe, expect, it, vi } from "vitest"; @@ -30,14 +31,6 @@ function mockSubmit(job: unknown, release = vi.fn(async () => {})): typeof relea return release; } -function requireFirstPostJsonRequest(): unknown { - const [call] = postJsonRequestMock.mock.calls; - if (!call) { - throw new Error("expected DeepInfra video submit request"); - } - return call[0]; -} - describe("deepinfra video generation provider", () => { it("declares explicit mode capabilities", () => { expectExplicitVideoGenerationCapabilities(buildDeepInfraVideoGenerationProvider()); @@ -101,7 +94,10 @@ describe("deepinfra video generation provider", () => { ]); expect(postJsonRequestMock).toHaveBeenCalledOnce(); - const postRequest = requireFirstPostJsonRequest(); + const postRequest = requireFirstPostJsonRequest( + postJsonRequestMock, + "DeepInfra video submit request", + ); const postRequestHeaders = Reflect.get(postRequest ?? {}, "headers"); expect(postRequestHeaders).toBeInstanceOf(Headers); expect(Object.fromEntries((postRequestHeaders as Headers).entries())).toEqual({ @@ -196,9 +192,12 @@ describe("deepinfra video generation provider", () => { } as unknown as OpenClawConfig, }); - expect(Reflect.get(requireFirstPostJsonRequest() ?? {}, "url")).toBe( - "https://video.example.com/v1/openai/videos", - ); + expect( + Reflect.get( + requireFirstPostJsonRequest(postJsonRequestMock, "DeepInfra video submit request") ?? {}, + "url", + ), + ).toBe("https://video.example.com/v1/openai/videos"); expect(result.videos).toEqual([ { url: "https://video.example.com/generated/custom.mp4", @@ -287,7 +286,10 @@ describe("deepinfra video generation provider", () => { }); expect(postJsonRequestMock).toHaveBeenCalledOnce(); - const postRequest = requireFirstPostJsonRequest(); + const postRequest = requireFirstPostJsonRequest( + postJsonRequestMock, + "DeepInfra video submit request", + ); expect(Reflect.get(Reflect.get(postRequest ?? {}, "body") ?? {}, "seed")).toBeUndefined(); }); diff --git a/extensions/diffs/src/config.test.ts b/extensions/diffs/src/config.test.ts index a3b84f4c7c32..4b58ee64e3c6 100644 --- a/extensions/diffs/src/config.test.ts +++ b/extensions/diffs/src/config.test.ts @@ -1,9 +1,10 @@ -// Diffs tests cover config plugin behavior. import fs from "node:fs"; import { validateJsonSchemaValue, type JsonSchemaObject, } from "openclaw/plugin-sdk/json-schema-runtime"; +// Diffs tests cover config plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeAll, describe, expect, it } from "vitest"; import { diffsPluginConfigSchema, @@ -63,12 +64,7 @@ function compileManifestConfigSchema() { }); } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function expectFields(value: unknown, fields: Record) { const record = requireRecord(value, "record"); diff --git a/extensions/discord/src/monitor.test.ts b/extensions/discord/src/monitor.test.ts index 67e60ebee841..6ed670621182 100644 --- a/extensions/discord/src/monitor.test.ts +++ b/extensions/discord/src/monitor.test.ts @@ -1,6 +1,6 @@ // Discord tests cover monitor plugin behavior. import { danger } from "openclaw/plugin-sdk/runtime-env"; -import { typedCases } from "openclaw/plugin-sdk/test-fixtures"; +import { createRequireRecord, typedCases } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { ChannelType, type Guild } from "./internal/discord.js"; import { @@ -949,12 +949,7 @@ function firstMockArg(mock: MockWithCalls, label: string) { return firstMockCall(mock, label)[0]; } -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null) { - throw new Error(`expected ${label} to be an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label-object"); function makeReactionEvent(overrides?: { guildId?: string; diff --git a/extensions/discord/src/monitor/message-media.test.ts b/extensions/discord/src/monitor/message-media.test.ts index fbb16d2b7164..f974f7d75039 100644 --- a/extensions/discord/src/monitor/message-media.test.ts +++ b/extensions/discord/src/monitor/message-media.test.ts @@ -1,10 +1,11 @@ -// Discord tests cover message utils plugin behavior. import { type APIAttachment, type APIStickerItem, MessageReferenceType, StickerFormatType, } from "discord-api-types/v10"; +// Discord tests cover message utils plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { Message } from "../internal/discord.js"; @@ -97,12 +98,7 @@ const DISCORD_CDN_HOSTNAMES = [ "*.discordapp.net", ]; -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function requireArray(value: unknown, label: string): Array { expect(Array.isArray(value), label).toBe(true); diff --git a/extensions/discord/src/monitor/thread-bindings.lifecycle.test.ts b/extensions/discord/src/monitor/thread-bindings.lifecycle.test.ts index 3908c0b167e1..91ea57e15297 100644 --- a/extensions/discord/src/monitor/thread-bindings.lifecycle.test.ts +++ b/extensions/discord/src/monitor/thread-bindings.lifecycle.test.ts @@ -1,4 +1,3 @@ -// Discord tests cover thread bindings.lifecycle plugin behavior. import fs from "node:fs"; import os from "node:os"; import path from "node:path"; @@ -15,6 +14,8 @@ import { setRuntimeConfigSnapshot, type OpenClawConfig, } from "openclaw/plugin-sdk/runtime-config-snapshot"; +// Discord tests cover thread bindings.lifecycle plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { setDiscordRuntime } from "../runtime.js"; import { EMPTY_DISCORD_TEST_CONFIG } from "../test-support/config.js"; @@ -92,12 +93,7 @@ function createTestThreadBindingManager( }); } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`Expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-capitalized"); function expectFields( value: unknown, diff --git a/extensions/discord/src/monitor/threading.auto-thread.test.ts b/extensions/discord/src/monitor/threading.auto-thread.test.ts index a12cad48dfcd..142a84b11cb0 100644 --- a/extensions/discord/src/monitor/threading.auto-thread.test.ts +++ b/extensions/discord/src/monitor/threading.auto-thread.test.ts @@ -1,5 +1,6 @@ -// Discord tests cover threading.auto thread plugin behavior. import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts"; +// Discord tests cover threading.auto thread plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { ChannelType } from "../internal/discord.js"; import { EMPTY_DISCORD_TEST_CONFIG } from "../test-support/config.js"; @@ -57,12 +58,7 @@ async function flushAsyncWork() { await Promise.resolve(); } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function callArg(mock: unknown, callIndex: number, argIndex: number, label: string) { const calls = (mock as { mock?: { calls?: Array> } }).mock?.calls ?? []; diff --git a/extensions/discord/src/send.creates-thread.test.ts b/extensions/discord/src/send.creates-thread.test.ts index cd7ff21454bb..f8bd77ffce35 100644 --- a/extensions/discord/src/send.creates-thread.test.ts +++ b/extensions/discord/src/send.creates-thread.test.ts @@ -1,5 +1,6 @@ -// Discord tests cover send.creates thread plugin behavior. import { ChannelType, MessageFlags, Routes } from "discord-api-types/v10"; +// Discord tests cover send.creates thread plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { loadWebMediaRaw } from "openclaw/plugin-sdk/web-media"; import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { RateLimitError } from "./internal/discord.js"; @@ -45,12 +46,7 @@ type MockCallSource = { }; }; -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function mockArg(source: MockCallSource, callIndex: number, argIndex: number, label: string) { const call = source.mock.calls[callIndex]; diff --git a/extensions/discord/src/send.sends-basic-channel-messages.test.ts b/extensions/discord/src/send.sends-basic-channel-messages.test.ts index 8cb9a017bf19..67716ee29a28 100644 --- a/extensions/discord/src/send.sends-basic-channel-messages.test.ts +++ b/extensions/discord/src/send.sends-basic-channel-messages.test.ts @@ -1,5 +1,6 @@ -// Discord tests cover send.sends basic channel messages plugin behavior. import { ChannelType, MessageFlags, PermissionFlagsBits, Routes } from "discord-api-types/v10"; +// Discord tests cover send.sends basic channel messages plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { Container, TextDisplay } from "./internal/discord.js"; import { discordWebMediaMockFactory, makeDiscordRest } from "./send.test-harness.js"; @@ -113,16 +114,7 @@ beforeEach(() => { clearDiscordDirectoryCacheForTest(); }); -function isRecord(value: unknown): value is Record { - return typeof value === "object" && value !== null && !Array.isArray(value); -} - -function requireRecord(value: unknown, label: string): Record { - if (!isRecord(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); function requireArray(value: unknown, label: string): unknown[] { if (!Array.isArray(value)) { diff --git a/extensions/feishu/src/bot.card-action.test.ts b/extensions/feishu/src/bot.card-action.test.ts index f0756f0b0763..cbb4c56b7f8a 100644 --- a/extensions/feishu/src/bot.card-action.test.ts +++ b/extensions/feishu/src/bot.card-action.test.ts @@ -1,5 +1,6 @@ -// Feishu tests cover bot.card action plugin behavior. import { createRuntimeEnv } from "openclaw/plugin-sdk/plugin-test-runtime"; +// Feishu tests cover bot.card action plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterAll, afterEach, describe, it, expect, vi, beforeEach } from "vitest"; import type { ClawdbotConfig, RuntimeEnv } from "../runtime-api.js"; import { processedCardActions, resolvedCardActionChatTypes } from "./card-action-state.js"; @@ -133,12 +134,7 @@ describe("Feishu Card Action Handler", () => { return call[0]; } - function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`Expected ${label}`); - } - return value as Record; - } + const requireRecord = createRequireRecord("object", "expected-label-capitalized"); function handleMessageEvent(callIndex = 0) { const arg = requireRecord( diff --git a/extensions/feishu/src/channel.test.ts b/extensions/feishu/src/channel.test.ts index e3e3d3a464fa..246c3041c633 100644 --- a/extensions/feishu/src/channel.test.ts +++ b/extensions/feishu/src/channel.test.ts @@ -1,4 +1,5 @@ // Feishu tests cover channel plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../runtime-api.js"; import { feishuPlugin } from "./channel.js"; @@ -82,12 +83,7 @@ function getDescribedActions(cfg: OpenClawConfig, accountId?: string): string[] return [...(feishuPlugin.actions?.describeMessageTool?.({ cfg, accountId })?.actions ?? [])]; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`Expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-capitalized"); function requireArray(value: unknown, label: string): unknown[] { if (!Array.isArray(value)) { diff --git a/extensions/feishu/src/docx.test.ts b/extensions/feishu/src/docx.test.ts index d526d6312933..43dcd05a6490 100644 --- a/extensions/feishu/src/docx.test.ts +++ b/extensions/feishu/src/docx.test.ts @@ -1,6 +1,7 @@ -// Feishu tests cover docx plugin behavior. import { mkdirSync, rmSync, writeFileSync } from "node:fs"; import path from "node:path"; +// Feishu tests cover docx plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterAll, beforeEach, describe, expect, it, vi } from "vitest"; import { FEISHU_HTTP_TIMEOUT_MS } from "./client-timeout.js"; import { createToolFactoryHarness, type ToolLike } from "./tool-factory-test-harness.js"; @@ -69,12 +70,7 @@ type ToolResultWithDetails = { const WORKSPACE_ROOT = path.resolve("/workspace"); -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function callArg(mock: unknown, callIndex: number, argIndex: number, label: string) { const calls = (mock as { mock?: { calls?: Array> } }).mock?.calls ?? []; diff --git a/extensions/file-transfer/src/shared/node-invoke-policy.test.ts b/extensions/file-transfer/src/shared/node-invoke-policy.test.ts index d54753dd3e7d..36bbc306e422 100644 --- a/extensions/file-transfer/src/shared/node-invoke-policy.test.ts +++ b/extensions/file-transfer/src/shared/node-invoke-policy.test.ts @@ -1,8 +1,9 @@ -// File Transfer tests cover node invoke policy plugin behavior. import crypto from "node:crypto"; import fs from "node:fs/promises"; import { gzipSync } from "node:zlib"; import type { OpenClawPluginNodeInvokePolicyContext } from "openclaw/plugin-sdk/plugin-entry"; +// File Transfer tests cover node invoke policy plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterAll, afterEach, describe, expect, it, vi } from "vitest"; import { appendFileTransferAudit } from "./audit.js"; import { createFileTransferNodeInvokePolicy } from "./node-invoke-policy.js"; @@ -128,12 +129,7 @@ function createCtx(overrides: { }; } -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null) { - throw new Error(`${label} was not an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "label-not-object"); function expectRecordFields(record: Record, fields: Record) { for (const [key, value] of Object.entries(fields)) { diff --git a/extensions/google-meet/index.create.test.ts b/extensions/google-meet/index.create.test.ts index 48273c30958c..c73e6b6d6ab3 100644 --- a/extensions/google-meet/index.create.test.ts +++ b/extensions/google-meet/index.create.test.ts @@ -1,5 +1,6 @@ -// Google Meet tests cover index.create plugin behavior. import { Command } from "commander"; +// Google Meet tests cover index.create plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import plugin, { testing as googleMeetPluginTesting } from "./index.js"; import { registerGoogleMeetCli } from "./src/cli.js"; @@ -137,12 +138,7 @@ async function runCreateMeetBrowserScript(params: { buttonText: string }) { return { button, result: scriptResult }; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); type BrowserProxyBody = { fn?: string; diff --git a/extensions/google-meet/index.test.ts b/extensions/google-meet/index.test.ts index 12f409c546b8..46286909e291 100644 --- a/extensions/google-meet/index.test.ts +++ b/extensions/google-meet/index.test.ts @@ -1,4 +1,3 @@ -// Google Meet tests cover index plugin behavior. import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import path from "node:path"; @@ -22,6 +21,8 @@ import type { RealtimeVoiceBridge, RealtimeVoiceProviderPlugin, } from "openclaw/plugin-sdk/realtime-voice"; +// Google Meet tests cover index plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import plugin, { testing as googleMeetPluginTesting } from "./index.js"; import { findGoogleMeetCalendarEvent, listGoogleMeetCalendarEvents } from "./src/calendar.js"; @@ -612,12 +613,7 @@ function createMockSessionRuntime(sessionStore: Record) { }; } -function requireRecord(value: unknown, label: string): Record { - if (!isRecord(value)) { - throw new Error(`Expected ${label} to be an object`); - } - return value; -} +const requireRecord = createRequireRecord("record", "expected-label-object-capitalized"); function mockCall(mock: { mock: { calls: unknown[][] } }, callIndex = 0): unknown[] { const call = mock.mock.calls.at(callIndex); diff --git a/extensions/google/video-generation-provider.test.ts b/extensions/google/video-generation-provider.test.ts index 94a49f8c46a5..49f9ae4d4aed 100644 --- a/extensions/google/video-generation-provider.test.ts +++ b/extensions/google/video-generation-provider.test.ts @@ -1,5 +1,6 @@ // Google tests cover video generation provider plugin behavior. import { mockPinnedHostnameResolution } from "openclaw/plugin-sdk/test-env"; +import { oversizedJsonResponse } from "openclaw/plugin-sdk/test-fixtures"; import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from "vitest"; const { createGoogleGenAIMock, downloadMock, generateVideosMock, getVideosOperationMock } = @@ -94,39 +95,6 @@ function fetchInputUrl(fetchMock: ReturnType, index: number): stri return input.url; } -function oversizedJsonResponse(params: { chunkCount: number; chunkSize: number }): { - response: Response; - getReadCount: () => number; - wasCanceled: () => boolean; -} { - const chunk = new Uint8Array(params.chunkSize); - let readCount = 0; - let canceled = false; - return { - response: new Response( - new ReadableStream({ - pull(controller) { - if (readCount >= params.chunkCount) { - controller.close(); - return; - } - readCount += 1; - controller.enqueue(chunk); - }, - cancel() { - canceled = true; - }, - }), - { - status: 200, - headers: { "Content-Type": "application/json" }, - }, - ), - getReadCount: () => readCount, - wasCanceled: () => canceled, - }; -} - let ssrfMock: { mockRestore: () => void } | undefined; describe("google video generation provider", () => { diff --git a/extensions/kilocode/provider-models.test.ts b/extensions/kilocode/provider-models.test.ts index 82a88d321a44..780ff198f47a 100644 --- a/extensions/kilocode/provider-models.test.ts +++ b/extensions/kilocode/provider-models.test.ts @@ -1,4 +1,5 @@ // Kilocode tests cover provider models plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterAll, describe, expect, it, vi } from "vitest"; const { fetchWithSsrFGuardMock } = vi.hoisted(() => ({ @@ -41,12 +42,7 @@ function requireModelById( return model; } -function requireRecord(value: unknown, label: string): Record { - if (value === null || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be a record`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-record"); function requireFirstMockCall(mock: { mock: { calls: unknown[][] } }, label: string): unknown[] { const [call] = mock.mock.calls; diff --git a/extensions/lmstudio/src/setup.test.ts b/extensions/lmstudio/src/setup.test.ts index aba7c3af25ad..58ceea52a617 100644 --- a/extensions/lmstudio/src/setup.test.ts +++ b/extensions/lmstudio/src/setup.test.ts @@ -1,4 +1,3 @@ -// Lmstudio tests cover setup plugin behavior. import { createDeferred } from "openclaw/plugin-sdk/extension-shared"; import { createNonExitingRuntimeEnv, @@ -17,7 +16,8 @@ import { type ProviderCatalogContext, } from "openclaw/plugin-sdk/provider-setup"; import type { WizardPrompter } from "openclaw/plugin-sdk/setup"; -import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime"; +// Lmstudio tests cover setup plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { LMSTUDIO_DEFAULT_API_KEY_ENV_VAR, @@ -265,12 +265,7 @@ function createMethodBoundWizardPrompterHarness(values: WizardPromptValues = {}) return { prompter: new MethodBoundWizardPrompter(), note, text }; } -function requireRecord(value: unknown, label: string): Record { - if (!isRecord(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); function expectRecordFields( value: unknown, diff --git a/extensions/lmstudio/src/stream.test.ts b/extensions/lmstudio/src/stream.test.ts index 3248c58047f5..b97e69029742 100644 --- a/extensions/lmstudio/src/stream.test.ts +++ b/extensions/lmstudio/src/stream.test.ts @@ -1,6 +1,7 @@ -// Lmstudio tests cover stream plugin behavior. import type { StreamFn } from "openclaw/plugin-sdk/agent-core"; import { createAssistantMessageEventStream } from "openclaw/plugin-sdk/llm"; +// Lmstudio tests cover stream plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from "vitest"; let wrapLmstudioInferencePreload: typeof import("./stream.js").wrapLmstudioInferencePreload; @@ -38,12 +39,7 @@ afterAll(() => { type StreamEvent = { type: string } & Record; -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be a record`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-record"); function expectRecordFields(record: Record, fields: Record) { for (const [key, value] of Object.entries(fields)) { diff --git a/extensions/lobster/src/lobster-runner.test.ts b/extensions/lobster/src/lobster-runner.test.ts index 1802ea902ebb..c44fe38ef4d4 100644 --- a/extensions/lobster/src/lobster-runner.test.ts +++ b/extensions/lobster/src/lobster-runner.test.ts @@ -1,16 +1,12 @@ -// Lobster tests cover lobster runner plugin behavior. import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; +// Lobster tests cover lobster runner plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, describe, expect, it, vi } from "vitest"; import { createEmbeddedLobsterRunner, resolveLobsterCwd } from "./lobster-runner.js"; -function requireRecord(value: unknown, label: string): Record { - if (value === null || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be a record`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-record"); function requireFirstCallParam(calls: ReadonlyArray, label: string) { const call = calls[0]; diff --git a/extensions/lobster/src/lobster-tool.test.ts b/extensions/lobster/src/lobster-tool.test.ts index d30c710edb9d..5bdae4274933 100644 --- a/extensions/lobster/src/lobster-tool.test.ts +++ b/extensions/lobster/src/lobster-tool.test.ts @@ -1,5 +1,6 @@ -// Lobster tests cover lobster tool plugin behavior. import { createTestPluginApi } from "openclaw/plugin-sdk/plugin-test-api"; +// Lobster tests cover lobster tool plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { describe, expect, it, vi } from "vitest"; import type { OpenClawPluginApi, OpenClawPluginToolContext } from "../runtime-api.js"; import { createLobsterTool } from "./lobster-tool.js"; @@ -30,12 +31,7 @@ function fakeCtx(overrides: Partial = {}): OpenClawPl }; } -function requireRecord(value: unknown, label: string): Record { - if (value === null || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be a record`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-record"); describe("lobster plugin tool", () => { it("returns the Lobster envelope in details", async () => { diff --git a/extensions/matrix/src/matrix/client.test.ts b/extensions/matrix/src/matrix/client.test.ts index b9c2e09a73e8..7525a4ad0c68 100644 --- a/extensions/matrix/src/matrix/client.test.ts +++ b/extensions/matrix/src/matrix/client.test.ts @@ -1,5 +1,6 @@ -// Matrix tests cover client plugin behavior. import { expectDefined } from "@openclaw/normalization-core"; +// Matrix tests cover client plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { installMatrixTestRuntime } from "../test-runtime.js"; import type { CoreConfig } from "../types.js"; @@ -53,12 +54,7 @@ vi.mock("./client/logging.js", () => ({ ensureMatrixSdkLoggingConfigured: authClientMocks.ensureMatrixSdkLoggingConfigured, })); -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null) { - throw new Error(`${label} was not an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "label-not-object"); function expectRecordFields(record: Record, fields: Record) { for (const [key, value] of Object.entries(fields)) { diff --git a/extensions/matrix/src/matrix/monitor/events.test.ts b/extensions/matrix/src/matrix/monitor/events.test.ts index b495d3f32e38..40116160b020 100644 --- a/extensions/matrix/src/matrix/monitor/events.test.ts +++ b/extensions/matrix/src/matrix/monitor/events.test.ts @@ -1,5 +1,6 @@ -// Matrix tests cover events plugin behavior. import { expectDefined } from "@openclaw/normalization-core"; +// Matrix tests cover events plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { describe, expect, it, vi } from "vitest"; import type { CoreConfig } from "../../types.js"; import type { MatrixAuth } from "../client.js"; @@ -35,12 +36,7 @@ function expectBodiesExclude(bodies: string[], text: string) { expect(bodies.join("\n")).not.toContain(text); } -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null) { - throw new Error(`${label} was not an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "label-not-object"); function expectRecordFields(record: Record, fields: Record) { for (const [key, value] of Object.entries(fields)) { diff --git a/extensions/matrix/src/matrix/monitor/handler.test.ts b/extensions/matrix/src/matrix/monitor/handler.test.ts index 12d0dae09055..289194fa7a58 100644 --- a/extensions/matrix/src/matrix/monitor/handler.test.ts +++ b/extensions/matrix/src/matrix/monitor/handler.test.ts @@ -1,4 +1,3 @@ -// Matrix tests cover handler plugin behavior. import fs from "node:fs"; import os from "node:os"; import path from "node:path"; @@ -14,6 +13,8 @@ import { sessionDeliveryOrigin, upsertSessionEntry, } from "openclaw/plugin-sdk/session-store-runtime"; +// Matrix tests cover handler plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { installMatrixMonitorTestRuntime } from "../../test-runtime.js"; import { MATRIX_OPENCLAW_FINALIZED_PREVIEW_KEY } from "../send/types.js"; @@ -172,12 +173,7 @@ function createReactionHarness(params?: { }); } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function requireArray(value: unknown, label: string): Array { expect(Array.isArray(value), label).toBe(true); diff --git a/extensions/matrix/src/matrix/monitor/handler.thread-root-media.test.ts b/extensions/matrix/src/matrix/monitor/handler.thread-root-media.test.ts index 0bb893045604..7782be07523e 100644 --- a/extensions/matrix/src/matrix/monitor/handler.thread-root-media.test.ts +++ b/extensions/matrix/src/matrix/monitor/handler.thread-root-media.test.ts @@ -1,4 +1,5 @@ // Matrix tests cover handler.thread root media plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { describe, expect, it, vi } from "vitest"; import { installMatrixMonitorTestRuntime } from "../../test-runtime.js"; import { @@ -7,12 +8,7 @@ import { createMatrixTextMessageEvent, } from "./handler.test-helpers.js"; -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function readFirstMockArg(fn: unknown): unknown { return (fn as { mock: { calls: unknown[][] } }).mock.calls.at(0)?.[0]; diff --git a/extensions/matrix/src/matrix/sdk.test.ts b/extensions/matrix/src/matrix/sdk.test.ts index 12711e121f9a..dafcb97fbc60 100644 --- a/extensions/matrix/src/matrix/sdk.test.ts +++ b/extensions/matrix/src/matrix/sdk.test.ts @@ -1,6 +1,5 @@ -// Matrix tests cover sdk plugin behavior. -import "fake-indexeddb/auto"; import { EventEmitter } from "node:events"; +import "fake-indexeddb/auto"; import fs from "node:fs"; import { createRequire } from "node:module"; import os from "node:os"; @@ -12,6 +11,8 @@ import { type MatrixEvent, MsgType } from "matrix-js-sdk/lib/matrix.js"; import { EventStatus } from "matrix-js-sdk/lib/models/event-status.js"; import { SyncApi, SyncState } from "matrix-js-sdk/lib/sync.js"; import { resetPluginStateStoreForTests } from "openclaw/plugin-sdk/plugin-state-test-runtime"; +// Matrix tests cover sdk plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { installMatrixTestRuntime } from "../test-runtime.js"; import { readMatrixRecoveryKeyStateForPath } from "./crypto-state-store.js"; @@ -32,12 +33,7 @@ function requestUrl(input: RequestInfo | URL | undefined): string { return input.url; } -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null) { - throw new Error(`${label} was not an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "label-not-object"); function expectRecordFields(record: Record, fields: Record) { for (const [key, value] of Object.entries(fields)) { diff --git a/extensions/matrix/src/matrix/send.test.ts b/extensions/matrix/src/matrix/send.test.ts index dc034251111e..2f6977ff0015 100644 --- a/extensions/matrix/src/matrix/send.test.ts +++ b/extensions/matrix/src/matrix/send.test.ts @@ -1,4 +1,3 @@ -// Matrix tests cover send plugin behavior. import fs from "node:fs"; import os from "node:os"; import path from "node:path"; @@ -6,6 +5,8 @@ import { resetPluginBlobStoreForTests, resetPluginStateStoreForTests, } from "openclaw/plugin-sdk/plugin-state-test-runtime"; +// Matrix tests cover send plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { PluginRuntime } from "../../runtime-api.js"; import { setMatrixRuntime } from "../runtime.js"; @@ -165,12 +166,7 @@ function makeEncryptedMediaClient() { return result; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function requireArray(value: unknown, label: string): Array { expect(Array.isArray(value), label).toBe(true); diff --git a/extensions/matrix/src/matrix/subagent-hooks.test.ts b/extensions/matrix/src/matrix/subagent-hooks.test.ts index 94c262961c72..08a0eb6e8ea6 100644 --- a/extensions/matrix/src/matrix/subagent-hooks.test.ts +++ b/extensions/matrix/src/matrix/subagent-hooks.test.ts @@ -1,9 +1,10 @@ -// Matrix tests cover subagent hooks plugin behavior. import type { OpenClawPluginApi as MatrixEntryPluginApi } from "openclaw/plugin-sdk/channel-entry-contract"; import { getRequiredHookHandler, registerHookHandlersForTest, } from "openclaw/plugin-sdk/channel-test-helpers"; +// Matrix tests cover subagent hooks plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { registerMatrixSubagentHooks } from "../../subagent-hooks-api.js"; @@ -158,12 +159,7 @@ function makeDeliveryResult( }; } -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null) { - throw new Error(`${label} was not an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "label-not-object"); function expectRecordFields(record: Record, fields: Record) { for (const [key, value] of Object.entries(fields)) { diff --git a/extensions/moonshot/media-understanding-provider.test.ts b/extensions/moonshot/media-understanding-provider.test.ts index 6a66987b41c3..c79aad559869 100644 --- a/extensions/moonshot/media-understanding-provider.test.ts +++ b/extensions/moonshot/media-understanding-provider.test.ts @@ -1,3 +1,4 @@ +import { oversizedJsonResponse } from "openclaw/plugin-sdk/test-fixtures"; // Moonshot tests cover media understanding provider plugin behavior. import { createRequestCaptureJsonFetch, @@ -18,39 +19,6 @@ async function describeVideo( return await handler(params); } -function oversizedJsonResponse(params: { chunkCount: number; chunkSize: number }): { - response: Response; - getReadCount: () => number; - wasCanceled: () => boolean; -} { - const chunk = new Uint8Array(params.chunkSize); - let readCount = 0; - let canceled = false; - return { - response: new Response( - new ReadableStream({ - pull(controller) { - if (readCount >= params.chunkCount) { - controller.close(); - return; - } - readCount += 1; - controller.enqueue(chunk); - }, - cancel() { - canceled = true; - }, - }), - { - status: 200, - headers: { "Content-Type": "application/json" }, - }, - ), - getReadCount: () => readCount, - wasCanceled: () => canceled, - }; -} - describe("describeMoonshotVideo", () => { it("builds an OpenAI-compatible video request", async () => { const { fetchFn, getRequest } = createRequestCaptureJsonFetch({ diff --git a/extensions/ollama/index.test.ts b/extensions/ollama/index.test.ts index 80737710623f..456a7baae2ae 100644 --- a/extensions/ollama/index.test.ts +++ b/extensions/ollama/index.test.ts @@ -1,8 +1,9 @@ -// Ollama tests cover index plugin behavior. import { expectDefined } from "@openclaw/normalization-core"; import type { ProviderAuthMethod } from "openclaw/plugin-sdk/plugin-entry"; import { createTestPluginApi } from "openclaw/plugin-sdk/plugin-test-api"; import { clearLiveCatalogCacheForTests } from "openclaw/plugin-sdk/provider-catalog-shared"; +// Ollama tests cover index plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import plugin from "./index.js"; import { OLLAMA_DEFAULT_API_KEY } from "./src/discovery-shared.js"; @@ -177,12 +178,7 @@ function createOllamaResetValidationContext( }; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function requireConfiguredStreamParams(): Record { return requireRecord(createConfiguredOllamaStreamFnMock.mock.calls[0]?.[0], "stream params"); diff --git a/extensions/ollama/src/stream-runtime.test.ts b/extensions/ollama/src/stream-runtime.test.ts index 56ffab843ffe..750dd1b3e353 100644 --- a/extensions/ollama/src/stream-runtime.test.ts +++ b/extensions/ollama/src/stream-runtime.test.ts @@ -1,5 +1,6 @@ -// Ollama tests cover stream runtime plugin behavior. import { expectDefined } from "@openclaw/normalization-core"; +// Ollama tests cover stream runtime plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, describe, expect, it, vi } from "vitest"; const { fetchWithSsrFGuardMock, ollamaStreamWarnMock } = vi.hoisted(() => ({ @@ -44,12 +45,7 @@ function requireEntry(entries: readonly T[], index: number, context: string): return expectDefined(entries[index], context); } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function requireOptionalRecord(value: unknown): Record | undefined { return value === undefined ? undefined : requireRecord(value, "request options"); diff --git a/extensions/opencode-go/index.test.ts b/extensions/opencode-go/index.test.ts index 29b65fd78ef1..e28e5bff5b48 100644 --- a/extensions/opencode-go/index.test.ts +++ b/extensions/opencode-go/index.test.ts @@ -1,4 +1,3 @@ -// Opencode Go tests cover index plugin behavior. import { clampThinkingLevel } from "openclaw/plugin-sdk/llm"; import type { ProviderRuntimeModel } from "openclaw/plugin-sdk/plugin-entry"; import { @@ -8,6 +7,8 @@ import { import { NON_ENV_SECRETREF_MARKER } from "openclaw/plugin-sdk/provider-auth-runtime"; import { clearLiveCatalogCacheForTests } from "openclaw/plugin-sdk/provider-catalog-live-runtime"; import { expectPassthroughReplayPolicy } from "openclaw/plugin-sdk/provider-test-contracts"; +// Opencode Go tests cover index plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import plugin from "./index.js"; import manifest from "./openclaw.plugin.json" with { type: "json" }; @@ -17,12 +18,7 @@ import { } from "./provider-catalog.js"; import opencodeGoProviderDiscovery from "./provider-discovery.js"; -function requireRecord(value: unknown, label: string): Record { - if (value === null || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be a record`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-record"); function requireMapEntry(map: Map, id: string): T { const entry = map.get(id); diff --git a/extensions/opencode/index.test.ts b/extensions/opencode/index.test.ts index a184ae3d0a24..d8bb73f09ef2 100644 --- a/extensions/opencode/index.test.ts +++ b/extensions/opencode/index.test.ts @@ -1,4 +1,3 @@ -// Opencode tests cover index plugin behavior. import { readFileSync } from "node:fs"; import type { ProviderRuntimeModel } from "openclaw/plugin-sdk/plugin-entry"; import { @@ -9,17 +8,14 @@ import { import { NON_ENV_SECRETREF_MARKER } from "openclaw/plugin-sdk/provider-auth-runtime"; import { clearLiveCatalogCacheForTests } from "openclaw/plugin-sdk/provider-catalog-live-runtime"; import { expectPassthroughReplayPolicy } from "openclaw/plugin-sdk/provider-test-contracts"; +// Opencode tests cover index plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import plugin from "./index.js"; import manifest from "./openclaw.plugin.json" with { type: "json" }; import { buildOpencodeZenLiveProviderConfig } from "./provider-catalog.js"; -function requireRecord(value: unknown, label: string): Record { - if (value === null || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be a record`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-record"); function requireMapEntry(map: Map, id: string): T { const entry = map.get(id); diff --git a/extensions/openrouter/video-generation-provider.test.ts b/extensions/openrouter/video-generation-provider.test.ts index 9768ca527068..50cd3c0ec282 100644 --- a/extensions/openrouter/video-generation-provider.test.ts +++ b/extensions/openrouter/video-generation-provider.test.ts @@ -1,9 +1,10 @@ -// Openrouter tests cover video generation provider plugin behavior. import { clearLiveCatalogCacheForTests } from "openclaw/plugin-sdk/provider-catalog-shared"; import { expectExplicitVideoGenerationCapabilities, expectUnifiedModelCatalogEntries, } from "openclaw/plugin-sdk/provider-test-contracts"; +// Openrouter tests cover video generation provider plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { buildOpenRouterVideoGenerationProvider, @@ -142,12 +143,7 @@ function requireFetchCallHeaders(index: number): Headers { return new Headers(init.headers); } -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null) { - throw new Error(`${label} was not an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "label-not-object"); function expectRecordFields(record: Record, fields: Record) { for (const [key, value] of Object.entries(fields)) { diff --git a/extensions/pixverse/video-generation-provider.test.ts b/extensions/pixverse/video-generation-provider.test.ts index a150559b49de..241bdeb767d1 100644 --- a/extensions/pixverse/video-generation-provider.test.ts +++ b/extensions/pixverse/video-generation-provider.test.ts @@ -2,6 +2,8 @@ import { getProviderHttpMocks, installProviderHttpMockCleanup, + oversizedJsonResponse, + streamedJsonResponse, } from "openclaw/plugin-sdk/provider-http-test-mocks"; import { expectExplicitVideoGenerationCapabilities } from "openclaw/plugin-sdk/provider-test-contracts"; import { beforeAll, describe, expect, it, vi } from "vitest"; @@ -56,18 +58,6 @@ function pollFetchHeaders(callIndex: number): Headers | undefined { return (init as { headers?: Headers } | undefined)?.headers; } -function streamedJsonResponse(payload: unknown): Response { - return new Response( - new ReadableStream({ - start(controller) { - controller.enqueue(new TextEncoder().encode(JSON.stringify(payload))); - controller.close(); - }, - }), - { headers: { "content-type": "application/json" } }, - ); -} - function mockPixVerseVideoSubmit(videoId = 123) { postJsonRequestMock.mockResolvedValue({ response: streamedJsonResponse({ @@ -107,37 +97,6 @@ function mockPixVerseVideoTask( }); } -// Drives an unbounded JSON body (>16 MiB, no Content-Length) so the bounded -// reader has to cancel the stream instead of buffering it all. A hard ceiling -// guards the test from hanging if the reader ever fails to cancel. -function oversizedJsonResponse(): { - response: Response; - state: { canceled: boolean; enqueuedBytes: number }; -} { - const state = { canceled: false, enqueuedBytes: 0 }; - const chunk = 1024 * 1024; - const maxChunks = 64; // 64 MiB ceiling, 4x the 16 MiB cap. - let emitted = 0; - const response = new Response( - new ReadableStream({ - pull(controller) { - if (emitted >= maxChunks) { - controller.close(); - return; - } - emitted += 1; - state.enqueuedBytes += chunk; - controller.enqueue(new Uint8Array(chunk)); - }, - cancel() { - state.canceled = true; - }, - }), - { headers: { "content-type": "application/json" } }, - ); - return { response, state }; -} - describe("pixverse video generation provider", () => { it("declares explicit mode capabilities", () => { expectExplicitVideoGenerationCapabilities(buildPixVerseVideoGenerationProvider()); diff --git a/extensions/qa-lab/src/providers/mock-openai/server.test.ts b/extensions/qa-lab/src/providers/mock-openai/server.test.ts index 9425f6293a0e..a0396391d1b8 100644 --- a/extensions/qa-lab/src/providers/mock-openai/server.test.ts +++ b/extensions/qa-lab/src/providers/mock-openai/server.test.ts @@ -1,5 +1,6 @@ -// Qa Lab tests cover server plugin behavior. import { once } from "node:events"; +// Qa Lab tests cover server plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, describe, expect, it } from "vitest"; import { WebSocket } from "ws"; import { readQaMockRequestCursor } from "../shared/debug-request-cursor.js"; @@ -216,12 +217,7 @@ function expectOpenAiStreamingResponsesText(server: MockServer, body: Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`Expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-capitalized"); function requireArray(value: unknown, label: string): unknown[] { if (!Array.isArray(value)) { diff --git a/extensions/qwen/media-understanding-provider.test.ts b/extensions/qwen/media-understanding-provider.test.ts index 1e9da36869fe..585d064c9680 100644 --- a/extensions/qwen/media-understanding-provider.test.ts +++ b/extensions/qwen/media-understanding-provider.test.ts @@ -1,3 +1,4 @@ +import { oversizedJsonResponse } from "openclaw/plugin-sdk/test-fixtures"; // Qwen tests cover media understanding provider plugin behavior. import { createRequestCaptureJsonFetch, @@ -23,39 +24,6 @@ describe("qwen media understanding provider", () => { }); }); -function oversizedJsonResponse(params: { chunkCount: number; chunkSize: number }): { - response: Response; - getReadCount: () => number; - wasCanceled: () => boolean; -} { - const chunk = new Uint8Array(params.chunkSize); - let readCount = 0; - let canceled = false; - return { - response: new Response( - new ReadableStream({ - pull(controller) { - if (readCount >= params.chunkCount) { - controller.close(); - return; - } - readCount += 1; - controller.enqueue(chunk); - }, - cancel() { - canceled = true; - }, - }), - { - status: 200, - headers: { "Content-Type": "application/json" }, - }, - ), - getReadCount: () => readCount, - wasCanceled: () => canceled, - }; -} - describe("describeQwenVideo", () => { it("builds the expected OpenAI-compatible video payload", async () => { const { fetchFn, getRequest } = createRequestCaptureJsonFetch({ diff --git a/extensions/slack/src/action-runtime.test.ts b/extensions/slack/src/action-runtime.test.ts index 78b2ebf1fdbb..e35d9efa8fdf 100644 --- a/extensions/slack/src/action-runtime.test.ts +++ b/extensions/slack/src/action-runtime.test.ts @@ -1,5 +1,6 @@ -// Slack tests cover action runtime plugin behavior. import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts"; +// Slack tests cover action runtime plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import type { SlackActionContext } from "./action-runtime.js"; import { handleSlackAction, slackActionRuntime } from "./action-runtime.js"; @@ -90,12 +91,7 @@ describe("handleSlackAction", () => { return { cfg, context, hasRepliedRef }; } - function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null) { - throw new Error(`${label} was not an object`); - } - return value as Record; - } + const requireRecord = createRequireRecord("object", "label-not-object"); function requireArray(value: unknown, label: string): unknown[] { expect(Array.isArray(value)).toBe(true); diff --git a/extensions/slack/src/channel.test.ts b/extensions/slack/src/channel.test.ts index 0f6608e05b3f..90fb12976d62 100644 --- a/extensions/slack/src/channel.test.ts +++ b/extensions/slack/src/channel.test.ts @@ -1,5 +1,6 @@ -// Slack tests cover channel plugin behavior. import { createRuntimeEnv } from "openclaw/plugin-sdk/plugin-test-runtime"; +// Slack tests cover channel plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { slackPlugin } from "./channel.js"; import { slackOutbound } from "./outbound-adapter.js"; @@ -147,12 +148,7 @@ function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } -function requireRecord(value: unknown, label: string): Record { - if (!isRecord(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); function requireArray(value: unknown, label: string): unknown[] { if (!Array.isArray(value)) { diff --git a/extensions/slack/src/monitor/events/interactions.test.ts b/extensions/slack/src/monitor/events/interactions.test.ts index d05fb99fe726..9b2e3626621c 100644 --- a/extensions/slack/src/monitor/events/interactions.test.ts +++ b/extensions/slack/src/monitor/events/interactions.test.ts @@ -1,5 +1,6 @@ -// Slack tests cover interactions plugin behavior. import type { SlackShortcutMiddlewareArgs } from "@slack/bolt"; +// Slack tests cover interactions plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const enqueueSystemEventMock = vi.hoisted(() => vi.fn()); @@ -347,12 +348,7 @@ function mockCallArg(mock: unknown, index: number, label: string, argIndex = 0): return call[argIndex]; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`Expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label-capitalized"); function hasLoneSurrogate(value: string): boolean { return Array.from(value).some((char) => { diff --git a/extensions/slack/src/monitor/media.test.ts b/extensions/slack/src/monitor/media.test.ts index 6228f67d4e96..ca0c396a49d0 100644 --- a/extensions/slack/src/monitor/media.test.ts +++ b/extensions/slack/src/monitor/media.test.ts @@ -7,6 +7,7 @@ import { type LookupFn, type SsrFPolicy, } from "openclaw/plugin-sdk/ssrf-runtime"; +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { resolveSlackAttachmentContent, @@ -186,12 +187,7 @@ function requireMockCall(mock: unknown, index: number, label: string): unknown[] return call; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); function expectFetchCalledWithUrl(mock: unknown, expectedUrl: string): void { expect(requireMockCall(mock, 0, "fetch")[0]).toBe(expectedUrl); diff --git a/extensions/slack/src/monitor/message-handler/dispatch.preview-fallback.test.ts b/extensions/slack/src/monitor/message-handler/dispatch.preview-fallback.test.ts index fbe4c63d95ca..14376664f143 100644 --- a/extensions/slack/src/monitor/message-handler/dispatch.preview-fallback.test.ts +++ b/extensions/slack/src/monitor/message-handler/dispatch.preview-fallback.test.ts @@ -1,5 +1,6 @@ -// Slack tests cover dispatch.preview fallback plugin behavior. import type { GetReplyOptions, ReplyPayload } from "openclaw/plugin-sdk/reply-runtime"; +// Slack tests cover dispatch.preview fallback plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const FINAL_REPLY_TEXT = "final answer"; @@ -176,12 +177,7 @@ function requireCapturedItemEventHandler() { return handler; } -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null) { - throw new Error(`${label} was not an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "label-not-object"); function expectRecordFields(record: Record, fields: Record) { for (const [key, value] of Object.entries(fields)) { diff --git a/extensions/synology-chat/src/channel.integration.test.ts b/extensions/synology-chat/src/channel.integration.test.ts index 9186838575a3..78e85e8abdab 100644 --- a/extensions/synology-chat/src/channel.integration.test.ts +++ b/extensions/synology-chat/src/channel.integration.test.ts @@ -1,4 +1,5 @@ // Synology Chat tests cover channel.integration plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { buildChannelInboundEventContextMock, @@ -25,12 +26,7 @@ function makeStartContext(cfg: T, accountId: string, abortSignal: AbortSignal }; } -function requireRecord(value: unknown, label: string): Record { - if (value === null || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be a record`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-record"); function requireMockCall( mock: { mock: { calls: TArgs[] } }, diff --git a/extensions/telegram/src/action-runtime.test.ts b/extensions/telegram/src/action-runtime.test.ts index ef1938740ecf..e039bd06802f 100644 --- a/extensions/telegram/src/action-runtime.test.ts +++ b/extensions/telegram/src/action-runtime.test.ts @@ -1,9 +1,10 @@ -// Telegram tests cover action runtime plugin behavior. import os from "node:os"; import path from "node:path"; import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts"; import { resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime"; import { captureEnv } from "openclaw/plugin-sdk/test-env"; +// Telegram tests cover action runtime plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { createOpenClawTestState, type OpenClawTestState } from "openclaw/plugin-sdk/test-state"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { @@ -256,12 +257,7 @@ type MockCallSource = { }; }; -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function mockCall(source: MockCallSource, callIndex: number, label: string) { const call = source.mock.calls[callIndex]; diff --git a/extensions/telegram/src/bot-native-commands.session-meta.test.ts b/extensions/telegram/src/bot-native-commands.session-meta.test.ts index 29a7cba6c95b..0e441dace180 100644 --- a/extensions/telegram/src/bot-native-commands.session-meta.test.ts +++ b/extensions/telegram/src/bot-native-commands.session-meta.test.ts @@ -1,4 +1,3 @@ -// Telegram tests cover bot native commands.session meta plugin behavior. import { createChannelPartialDeliveryError } from "openclaw/plugin-sdk/channel-inbound"; import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts"; import { getAgentScopedMediaLocalRoots } from "openclaw/plugin-sdk/media-runtime"; @@ -6,6 +5,8 @@ import { resolveChunkMode } from "openclaw/plugin-sdk/reply-dispatch-runtime"; import { resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing"; import type { ResolvedAgentRoute } from "openclaw/plugin-sdk/routing"; import type { SessionEntry } from "openclaw/plugin-sdk/session-store-runtime"; +// Telegram tests cover bot native commands.session meta plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { TelegramNativeCommandDeps } from "./bot-native-command-deps.runtime.js"; import { @@ -575,12 +576,7 @@ function requireValue(value: T | null | undefined, label: string): T { return value; } -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null || Array.isArray(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); function firstMockArg(mockFn: ReturnType, label: string, callIndex = 0): unknown { const call = mockFn.mock.calls.at(callIndex); diff --git a/extensions/telegram/src/bot.create-telegram-bot.test.ts b/extensions/telegram/src/bot.create-telegram-bot.test.ts index e684f6f93312..3845d65b5e07 100644 --- a/extensions/telegram/src/bot.create-telegram-bot.test.ts +++ b/extensions/telegram/src/bot.create-telegram-bot.test.ts @@ -19,7 +19,7 @@ import type { } from "openclaw/plugin-sdk/plugin-state-runtime"; import type { GetReplyOptions, MsgContext } from "openclaw/plugin-sdk/reply-runtime"; import { withEnvAsync } from "openclaw/plugin-sdk/test-env"; -import { sanitizeTerminalText } from "openclaw/plugin-sdk/test-fixtures"; +import { createRequireRecord, sanitizeTerminalText } from "openclaw/plugin-sdk/test-fixtures"; import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { createTelegramNativeCommandTestDeps, @@ -386,12 +386,7 @@ function createDeferred() { return { promise, resolve, reject }; } -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null || Array.isArray(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); function expectRecordFields( value: unknown, diff --git a/extensions/telegram/src/bot.media.downloads-media-file-path-no-file-download.e2e.test.ts b/extensions/telegram/src/bot.media.downloads-media-file-path-no-file-download.e2e.test.ts index 97a9c59fd92a..d7d9941b1f8c 100644 --- a/extensions/telegram/src/bot.media.downloads-media-file-path-no-file-download.e2e.test.ts +++ b/extensions/telegram/src/bot.media.downloads-media-file-path-no-file-download.e2e.test.ts @@ -1,4 +1,5 @@ // Telegram tests cover bot.mediaownloads media file path no file download plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, describe, expect, it, vi } from "vitest"; import { readRemoteMediaBufferSpy, @@ -39,12 +40,7 @@ function replyPayload(replySpy: ReturnType, index = 0): ReplyPaylo return payload as ReplyPayload; } -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null || Array.isArray(value)) { - throw new Error(`expected ${label} record`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-record-short"); function requireArray(value: unknown, label: string): unknown[] { if (!Array.isArray(value)) { diff --git a/extensions/telegram/src/bot.test.ts b/extensions/telegram/src/bot.test.ts index e6b8e2a4e590..b2fa13b72967 100644 --- a/extensions/telegram/src/bot.test.ts +++ b/extensions/telegram/src/bot.test.ts @@ -19,6 +19,7 @@ import { } from "openclaw/plugin-sdk/session-store-runtime"; import { appendSessionTranscriptMessageByIdentity } from "openclaw/plugin-sdk/session-transcript-runtime"; import { mockPinnedHostnameResolution } from "openclaw/plugin-sdk/test-env"; +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { createOpenClawTestState, type OpenClawTestState } from "openclaw/plugin-sdk/test-state"; import { registerSessionBindingAdapter, @@ -344,12 +345,7 @@ type MockCallSource = { }; }; -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function requireArray(value: unknown, label: string): unknown[] { expect(Array.isArray(value), label).toBe(true); diff --git a/extensions/telegram/src/bot/delivery.resolve-media-local.test.ts b/extensions/telegram/src/bot/delivery.resolve-media-local.test.ts index 9ca61cd85790..3a6931db6a9f 100644 --- a/extensions/telegram/src/bot/delivery.resolve-media-local.test.ts +++ b/extensions/telegram/src/bot/delivery.resolve-media-local.test.ts @@ -1,7 +1,8 @@ -// Telegram tests cover delivery.resolve media retry plugin behavior. import { GrammyError } from "grammy"; import type { Message } from "grammy/types"; import { sleepWithAbort } from "openclaw/plugin-sdk/runtime-env"; +// Telegram tests cover delivery.resolve media retry plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { resolveMedia } from "./delivery.resolve-media.js"; import type { TelegramContext } from "./types.js"; @@ -243,12 +244,7 @@ function requireResolvedMedia( return result; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be a record`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-record"); function expectRecordFields(record: Record, fields: Record) { for (const [key, value] of Object.entries(fields)) { diff --git a/extensions/telegram/src/bot/delivery.resolve-media-retry.test.ts b/extensions/telegram/src/bot/delivery.resolve-media-retry.test.ts index 66477ed5e43d..1b7e87d1b55c 100644 --- a/extensions/telegram/src/bot/delivery.resolve-media-retry.test.ts +++ b/extensions/telegram/src/bot/delivery.resolve-media-retry.test.ts @@ -1,6 +1,7 @@ -// Telegram tests cover delivery.resolve media retry plugin behavior. import type { Message } from "grammy/types"; import { sleepWithAbort } from "openclaw/plugin-sdk/runtime-env"; +// Telegram tests cover delivery.resolve media retry plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { resolveMedia } from "./delivery.resolve-media.js"; import type { TelegramContext } from "./types.js"; @@ -195,12 +196,7 @@ function requireResolvedMedia( return result; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be a record`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-record"); function expectRecordFields(record: Record, fields: Record) { for (const [key, value] of Object.entries(fields)) { diff --git a/extensions/telegram/src/send.test.ts b/extensions/telegram/src/send.test.ts index fb61baa2735b..d6e15de5d8fe 100644 --- a/extensions/telegram/src/send.test.ts +++ b/extensions/telegram/src/send.test.ts @@ -8,7 +8,7 @@ import { createPluginStateSyncKeyedStoreForTests, resetPluginStateStoreForTests, } from "openclaw/plugin-sdk/plugin-state-test-runtime"; -import { importFreshModule } from "openclaw/plugin-sdk/test-fixtures"; +import { createRequireRecord, importFreshModule } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { markdownToTelegramHtml, telegramHtmlToPlainTextFallback } from "./format.js"; import { @@ -365,12 +365,7 @@ function requireString(value: unknown, label: string): string { return value; } -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null) { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function expectMediaSendCall( call: unknown[] | undefined, diff --git a/extensions/telegram/src/webhook.test.ts b/extensions/telegram/src/webhook.test.ts index fcc0674451cf..f58ccf5cf19f 100644 --- a/extensions/telegram/src/webhook.test.ts +++ b/extensions/telegram/src/webhook.test.ts @@ -1,4 +1,3 @@ -// Telegram tests cover webhook plugin behavior. import { createHash } from "node:crypto"; import { once } from "node:events"; import fs from "node:fs/promises"; @@ -10,6 +9,8 @@ import { closeOpenClawStateDatabaseForTest, createChannelIngressQueueForTests as createChannelIngressQueue, } from "openclaw/plugin-sdk/plugin-state-test-runtime"; +// Telegram tests cover webhook plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { WEBHOOK_RATE_LIMIT_DEFAULTS } from "openclaw/plugin-sdk/webhook-ingress"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { buildTelegramApprovalCallbackData } from "./approval-callback-data.js"; @@ -221,12 +222,7 @@ function resetTelegramWebhookMocks(): void { type MockCallReader = { mock: { calls: unknown[][] } }; -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); function requireMockCall(mock: unknown, index: number, label: string): unknown[] { const call = (mock as MockCallReader).mock.calls.at(index); diff --git a/extensions/together/video-generation-provider.test.ts b/extensions/together/video-generation-provider.test.ts index ba2ee7609b7f..0c469b0f372e 100644 --- a/extensions/together/video-generation-provider.test.ts +++ b/extensions/together/video-generation-provider.test.ts @@ -1,9 +1,13 @@ -// Together tests cover video generation provider plugin behavior. import { getProviderHttpMocks, installProviderHttpMockCleanup, + oversizedJsonResponse, + requireFirstPostJsonRecordRequest as requireFirstPostJsonRequest, + streamedJsonResponse, } from "openclaw/plugin-sdk/provider-http-test-mocks"; import { expectExplicitVideoGenerationCapabilities } from "openclaw/plugin-sdk/provider-test-contracts"; +// Together tests cover video generation provider plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeAll, describe, expect, it, vi } from "vitest"; const { postJsonRequestMock, fetchWithTimeoutMock } = getProviderHttpMocks(); @@ -16,20 +20,7 @@ beforeAll(async () => { installProviderHttpMockCleanup(); -function requireRecord(value: unknown, label: string): Record { - if (value === null || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be a record`); - } - return value as Record; -} - -function requireFirstPostJsonRequest(label: string): Record { - const [call] = postJsonRequestMock.mock.calls; - if (!call) { - throw new Error(`expected ${label}`); - } - return requireRecord(call[0], label); -} +const requireRecord = createRequireRecord("record", "expected-label-record"); function streamingResponse(params: { body: string; @@ -48,49 +39,6 @@ function streamingResponse(params: { return new Response(stream, { headers: params.headers }); } -function streamedJsonResponse(payload: unknown): Response { - return new Response( - new ReadableStream({ - start(controller) { - controller.enqueue(new TextEncoder().encode(JSON.stringify(payload))); - controller.close(); - }, - }), - { headers: { "content-type": "application/json" } }, - ); -} - -// Drives an unbounded JSON body (>16 MiB, no Content-Length) so the bounded -// reader has to cancel the stream instead of buffering it all. A hard ceiling -// guards the test from hanging if the reader ever fails to cancel. -function oversizedJsonResponse(): { - response: Response; - state: { canceled: boolean; enqueuedBytes: number }; -} { - const state = { canceled: false, enqueuedBytes: 0 }; - const chunk = 1024 * 1024; - const maxChunks = 64; // 64 MiB ceiling, 4x the 16 MiB cap. - let emitted = 0; - const response = new Response( - new ReadableStream({ - pull(controller) { - if (emitted >= maxChunks) { - controller.close(); - return; - } - emitted += 1; - state.enqueuedBytes += chunk; - controller.enqueue(new Uint8Array(chunk)); - }, - cancel() { - state.canceled = true; - }, - }), - { headers: { "content-type": "application/json" } }, - ); - return { response, state }; -} - describe("together video generation provider", () => { it("declares explicit mode capabilities", () => { expectExplicitVideoGenerationCapabilities(buildTogetherVideoGenerationProvider()); @@ -135,7 +83,7 @@ describe("together video generation provider", () => { }); expect(postJsonRequestMock).toHaveBeenCalledOnce(); - const request = requireFirstPostJsonRequest("Together request"); + const request = requireFirstPostJsonRequest(postJsonRequestMock, "Together request"); expect(request.url).toBe("https://api.together.xyz/v2/videos"); const body = requireRecord(request.body, "Together request body"); expect(body.model).toBe("Wan-AI/Wan2.2-T2V-A14B"); @@ -377,7 +325,7 @@ describe("together video generation provider", () => { }, }); - const request = requireFirstPostJsonRequest("Together request"); + const request = requireFirstPostJsonRequest(postJsonRequestMock, "Together request"); expect(request.url).toBe("https://api.together.xyz/v2/videos"); }); @@ -410,7 +358,7 @@ describe("together video generation provider", () => { cfg: {}, }); - const request = requireFirstPostJsonRequest("Together request"); + const request = requireFirstPostJsonRequest(postJsonRequestMock, "Together request"); const body = requireRecord(request.body, "Together request body"); expect(body).not.toHaveProperty("seconds"); }); @@ -471,7 +419,7 @@ describe("together video generation provider", () => { ], }); - const request = requireFirstPostJsonRequest("Together request"); + const request = requireFirstPostJsonRequest(postJsonRequestMock, "Together request"); const body = requireRecord(request.body, "Together request body"); const media = requireRecord(body.media, "Together video media payload"); expect(body.model).toBe("Wan-AI/Wan2.2-I2V-A14B"); diff --git a/extensions/voice-call/src/manager.notify.test.ts b/extensions/voice-call/src/manager.notify.test.ts index 5b9a419fa198..1495d67cbe21 100644 --- a/extensions/voice-call/src/manager.notify.test.ts +++ b/extensions/voice-call/src/manager.notify.test.ts @@ -1,4 +1,5 @@ // Voice Call tests cover manager.notify plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { describe, expect, it, vi } from "vitest"; import { createManagerHarness, FakeProvider } from "./manager.test-harness.js"; @@ -77,12 +78,7 @@ function requireFirstPlayTtsCall(provider: FakeProvider) { return call; } -function requireRecord(value: unknown, label: string): Record { - if (value === null || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be a record`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-record"); function requireSingleStartListeningCall(provider: FakeProvider) { expect(provider.startListeningCalls).toHaveLength(1); diff --git a/extensions/voice-call/src/manager.restore.test.ts b/extensions/voice-call/src/manager.restore.test.ts index df48b4b898a3..9716ecd60634 100644 --- a/extensions/voice-call/src/manager.restore.test.ts +++ b/extensions/voice-call/src/manager.restore.test.ts @@ -1,9 +1,10 @@ -// Voice Call tests cover manager.restore plugin behavior. import type { OpenKeyedStoreOptions } from "openclaw/plugin-sdk/plugin-state-runtime"; import { createPluginStateSyncKeyedStoreForTests, resetPluginStateStoreForTests, } from "openclaw/plugin-sdk/plugin-state-test-runtime"; +// Voice Call tests cover manager.restore plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { VoiceCallConfigSchema } from "./config.js"; import { CallManager } from "./manager.js"; @@ -46,12 +47,7 @@ function requireSingleActiveCall(manager: CallManager) { return activeCall; } -function requireRecord(value: unknown, label: string): Record { - if (value === null || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be a record`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-record"); function requireSingleHangupCall(provider: FakeProvider) { expect(provider.hangupCalls).toHaveLength(1); diff --git a/extensions/voice-call/src/runtime.test.ts b/extensions/voice-call/src/runtime.test.ts index f3c09fb42a95..86f418ae5112 100644 --- a/extensions/voice-call/src/runtime.test.ts +++ b/extensions/voice-call/src/runtime.test.ts @@ -1,5 +1,6 @@ -// Voice Call tests cover runtime plugin behavior. import type { OpenClawConfig } from "openclaw/plugin-sdk/core"; +// Voice Call tests cover runtime plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import type { VoiceCallConfig } from "./config.js"; import type { CoreConfig } from "./core-bridge.js"; @@ -207,12 +208,7 @@ function createMockSessionRuntime(sessionStore: Record) { }; } -function requireRecord(value: unknown, label: string): Record { - if (value === null || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be a record`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-record"); function requireRealtimeConsultToolHandler(): RealtimeConsultToolHandler { const registeredToolHandler = firstMockCall( diff --git a/extensions/vydra/image-generation-provider.test.ts b/extensions/vydra/image-generation-provider.test.ts index 0f66febad1e7..3f99ef939c14 100644 --- a/extensions/vydra/image-generation-provider.test.ts +++ b/extensions/vydra/image-generation-provider.test.ts @@ -1,3 +1,4 @@ +import { bufferedOversizedJsonResponse as oversizedJsonResponse } from "openclaw/plugin-sdk/test-fixtures"; // Vydra tests cover image generation provider plugin behavior. import { installPinnedHostnameTestHooks } from "openclaw/plugin-sdk/test-media-understanding"; import { afterEach, describe, expect, it, vi } from "vitest"; @@ -17,13 +18,6 @@ function fetchCall(fetchMock: ReturnType, index = 0): [string, Req return call as [string, RequestInit]; } -function oversizedJsonResponse(): Response { - return new Response(Buffer.alloc(16 * 1024 * 1024 + 1, 0x20), { - status: 200, - headers: { "Content-Type": "application/json" }, - }); -} - describe("vydra image-generation provider", () => { installPinnedHostnameTestHooks(); diff --git a/extensions/vydra/speech-provider.test.ts b/extensions/vydra/speech-provider.test.ts index 8ae30b4b48a8..03f61032b84a 100644 --- a/extensions/vydra/speech-provider.test.ts +++ b/extensions/vydra/speech-provider.test.ts @@ -1,3 +1,4 @@ +import { bufferedOversizedJsonResponse as oversizedJsonResponse } from "openclaw/plugin-sdk/test-fixtures"; // Vydra tests cover speech provider plugin behavior. import { installPinnedHostnameTestHooks } from "openclaw/plugin-sdk/test-media-understanding"; import { afterEach, describe, expect, it, vi } from "vitest"; @@ -9,12 +10,6 @@ describe("vydra speech provider", () => { const provider = buildVydraSpeechProvider(); const originalVydraApiKey = process.env.VYDRA_API_KEY; - const oversizedJsonResponse = () => - new Response(Buffer.alloc(16 * 1024 * 1024 + 1, 0x20), { - status: 200, - headers: { "Content-Type": "application/json" }, - }); - afterEach(() => { if (originalVydraApiKey === undefined) { delete process.env.VYDRA_API_KEY; diff --git a/extensions/vydra/video-generation-provider.test.ts b/extensions/vydra/video-generation-provider.test.ts index 3b014c45c6c1..d4455bf49192 100644 --- a/extensions/vydra/video-generation-provider.test.ts +++ b/extensions/vydra/video-generation-provider.test.ts @@ -1,6 +1,7 @@ // Vydra tests cover video generation provider plugin behavior. import * as providerHttp from "openclaw/plugin-sdk/provider-http"; import { expectExplicitVideoGenerationCapabilities } from "openclaw/plugin-sdk/provider-test-contracts"; +import { bufferedOversizedJsonResponse as oversizedJsonResponse } from "openclaw/plugin-sdk/test-fixtures"; import { installPinnedHostnameTestHooks } from "openclaw/plugin-sdk/test-media-understanding"; import { afterEach, describe, expect, it, vi } from "vitest"; import { @@ -19,13 +20,6 @@ function fetchCall(fetchMock: ReturnType, index: number) { return call; } -function oversizedJsonResponse(): Response { - return new Response(Buffer.alloc(16 * 1024 * 1024 + 1, 0x20), { - status: 200, - headers: { "Content-Type": "application/json" }, - }); -} - describe("vydra video-generation provider", () => { installPinnedHostnameTestHooks(); diff --git a/extensions/whatsapp/src/auto-reply/deliver-reply.test.ts b/extensions/whatsapp/src/auto-reply/deliver-reply.test.ts index 62c18836d04b..9055d00d2caf 100644 --- a/extensions/whatsapp/src/auto-reply/deliver-reply.test.ts +++ b/extensions/whatsapp/src/auto-reply/deliver-reply.test.ts @@ -1,4 +1,3 @@ -// Whatsapp tests cover deliver reply plugin behavior. import type { WAMessage } from "baileys"; import { createChannelPartialDeliveryError, @@ -8,6 +7,8 @@ import { listMessageReceiptPlatformIds } from "openclaw/plugin-sdk/channel-outbo import { PlatformMessageNotDispatchedError } from "openclaw/plugin-sdk/error-runtime"; import { MEDIA_FFMPEG_MAX_AUDIO_DURATION_SECS } from "openclaw/plugin-sdk/media-runtime"; import { logVerbose } from "openclaw/plugin-sdk/runtime-env"; +// Whatsapp tests cover deliver reply plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeAll, describe, expect, it, vi } from "vitest"; import { createWebSendApi } from "../inbound/send-api.js"; import { normalizeWhatsAppSendResult } from "../inbound/send-result.js"; @@ -110,12 +111,7 @@ function expectFirstSendMediaPayload(msg: AdmittedWebInboundMessage) { return requireRecord(mockCallArg(msg.platform.sendMedia, 0, 0, "sendMedia"), "sendMedia payload"); } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label"); function mockCallArg(mock: unknown, callIndex: number, argIndex: number, label: string) { const call = (mock as MockWithCalls).mock.calls.at(callIndex); diff --git a/extensions/whatsapp/src/auto-reply/monitor/inbound-dispatch.test.ts b/extensions/whatsapp/src/auto-reply/monitor/inbound-dispatch.test.ts index e95b486dce5a..f9637a569a8a 100644 --- a/extensions/whatsapp/src/auto-reply/monitor/inbound-dispatch.test.ts +++ b/extensions/whatsapp/src/auto-reply/monitor/inbound-dispatch.test.ts @@ -1,4 +1,5 @@ // Whatsapp tests cover inbound dispatch plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { describe, expect, it, vi, beforeEach } from "vitest"; import { createTestWebInboundMessage } from "../../inbound/test-message.test-helper.js"; @@ -423,12 +424,7 @@ function getCapturedReplyOptions() { return (capturedDispatchParams as CapturedDispatchParams)?.replyOptions; } -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null) { - throw new Error(`${label} was not an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "label-not-object"); function expectRecordFields(record: Record, fields: Record) { for (const [key, value] of Object.entries(fields)) { diff --git a/extensions/whatsapp/src/inbound/send-api.test.ts b/extensions/whatsapp/src/inbound/send-api.test.ts index 32fd47335911..87ad469df26d 100644 --- a/extensions/whatsapp/src/inbound/send-api.test.ts +++ b/extensions/whatsapp/src/inbound/send-api.test.ts @@ -1,4 +1,3 @@ -// Whatsapp tests cover send api plugin behavior. import fs from "node:fs"; import os from "node:os"; import path from "node:path"; @@ -9,6 +8,8 @@ import { } from "openclaw/plugin-sdk/channel-inbound"; import { listMessageReceiptPlatformIds } from "openclaw/plugin-sdk/channel-outbound"; import { PlatformMessageNotDispatchedError } from "openclaw/plugin-sdk/error-runtime"; +// Whatsapp tests cover send api plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { prepareWhatsAppOutboundMedia } from "../outbound-media-contract.js"; import { resolveWhatsAppOutboundMentions } from "./outbound-mentions.js"; @@ -42,12 +43,7 @@ vi.mock("openclaw/plugin-sdk/media-runtime", async () => { }; }); -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null) { - throw new Error(`${label} was not an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "label-not-object"); type MockCallSource = { mock: { diff --git a/extensions/xai/video-generation-provider.test.ts b/extensions/xai/video-generation-provider.test.ts index 31a4313312ed..6899b600d331 100644 --- a/extensions/xai/video-generation-provider.test.ts +++ b/extensions/xai/video-generation-provider.test.ts @@ -2,6 +2,8 @@ import { getProviderHttpMocks, installProviderHttpMockCleanup, + oversizedJsonResponse, + streamedJsonResponse, } from "openclaw/plugin-sdk/provider-http-test-mocks"; import { expectExplicitVideoGenerationCapabilities } from "openclaw/plugin-sdk/provider-test-contracts"; import type { VideoGenerationRequest } from "openclaw/plugin-sdk/video-generation"; @@ -150,52 +152,6 @@ function streamedVideoResponse(bytes: string, contentType = "video/mp4"): Respon ); } -function streamedJsonResponse(payload: unknown): Response { - return new Response( - new ReadableStream({ - start(controller) { - controller.enqueue(new TextEncoder().encode(JSON.stringify(payload))); - controller.close(); - }, - }), - { headers: { "content-type": "application/json" } }, - ); -} - -// Drives an unbounded JSON body (>16 MiB, no Content-Length) so the bounded -// reader has to cancel the stream instead of buffering it all. The 1 MiB -// chunks are emitted lazily on `pull`, and a hard ceiling guards the test from -// hanging if the reader ever fails to cancel. -function oversizedJsonResponse(): { - response: Response; - state: { canceled: boolean; enqueuedBytes: number }; -} { - const state = { canceled: false, enqueuedBytes: 0 }; - const chunk = 1024 * 1024; - // 64 MiB ceiling: 4x the 16 MiB cap, so the bounded reader must cancel long - // before we run out of chunks. - const maxChunks = 64; - let emitted = 0; - const response = new Response( - new ReadableStream({ - pull(controller) { - if (emitted >= maxChunks) { - controller.close(); - return; - } - emitted += 1; - state.enqueuedBytes += chunk; - controller.enqueue(new Uint8Array(chunk)); - }, - cancel() { - state.canceled = true; - }, - }), - { headers: { "content-type": "application/json" } }, - ); - return { response, state }; -} - describe("xai video generation provider", () => { it("declares explicit mode capabilities", () => { const provider = buildXaiVideoGenerationProvider(); diff --git a/extensions/zalouser/src/channel.sendpayload.test.ts b/extensions/zalouser/src/channel.sendpayload.test.ts index 674bcf0672c5..a4c99e6071a0 100644 --- a/extensions/zalouser/src/channel.sendpayload.test.ts +++ b/extensions/zalouser/src/channel.sendpayload.test.ts @@ -1,4 +1,3 @@ -// Zalouser tests cover channel.sendpayload plugin behavior. import { installChannelOutboundPayloadContractSuite, primeChannelOutboundSendMock, @@ -8,6 +7,8 @@ import { createMessageReceiptFromOutboundResults, verifyChannelMessageAdapterCapabilityProofs, } from "openclaw/plugin-sdk/channel-outbound"; +// Zalouser tests cover channel.sendpayload plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import "./accounts.test-mocks.js"; import "./zalo-js.test-mocks.js"; @@ -71,12 +72,7 @@ function requireZalouserMediaSender( return media; } -function requireRecord(value: unknown, label: string): Record { - if (value === null || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be a record`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-record"); function requireSendOptions( mockedSend: ReturnType>, diff --git a/extensions/zalouser/src/monitor.account-scope.test.ts b/extensions/zalouser/src/monitor.account-scope.test.ts index d35c5181ab68..fc3fd3a2edd5 100644 --- a/extensions/zalouser/src/monitor.account-scope.test.ts +++ b/extensions/zalouser/src/monitor.account-scope.test.ts @@ -1,4 +1,5 @@ // Zalouser tests cover monitor.account scope plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { describe, expect, it, vi } from "vitest"; import type { OpenClawConfig, PluginRuntime } from "../runtime-api.js"; import "./monitor.send.test-mocks.js"; @@ -18,12 +19,7 @@ import { startZaloListenerMock } from "./zalo-js.test-mocks.js"; type ZaloJsModule = typeof import("./zalo-js.js"); type ListenerParams = Parameters[0]; -function requireRecord(value: unknown, label: string): Record { - if (value === null || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be a record`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-record"); describe("zalouser monitor pairing account scoping", () => { it("scopes DM pairing-store reads and pairing requests to accountId", async () => { diff --git a/extensions/zalouser/src/send.test.ts b/extensions/zalouser/src/send.test.ts index 511ce6349e61..e9a9fd00f15f 100644 --- a/extensions/zalouser/src/send.test.ts +++ b/extensions/zalouser/src/send.test.ts @@ -1,4 +1,5 @@ // Zalouser tests cover send plugin behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { createZalouserSendReceipt } from "./send-receipt.js"; import { @@ -60,12 +61,7 @@ function sendFailure(error: string, threadId = "thread") { }; } -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null) { - throw new Error(`${label} was not an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "label-not-object"); function expectRecordFields(record: Record, fields: Record) { for (const [key, value] of Object.entries(fields)) { diff --git a/src/agents/agent-command.live-model-switch.test.ts b/src/agents/agent-command.live-model-switch.test.ts index 332c9faeb2ba..78a18ec2e6cf 100644 --- a/src/agents/agent-command.live-model-switch.test.ts +++ b/src/agents/agent-command.live-model-switch.test.ts @@ -1,6 +1,7 @@ /** Tests live model switching behavior in active agent command sessions. */ import { expectDefined } from "@openclaw/normalization-core"; +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { SessionEntry } from "../config/sessions.js"; import { createUserTurnTranscriptRecorder } from "../sessions/user-turn-transcript.js"; @@ -757,12 +758,7 @@ function setupAcpSession(): void { }); } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label} to be an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label-object"); function requireArray(value: unknown, label: string): unknown[] { if (!Array.isArray(value)) { diff --git a/src/agents/agent-hooks/compaction-safeguard.test.ts b/src/agents/agent-hooks/compaction-safeguard.test.ts index ee6594786713..9b3c23a9055c 100644 --- a/src/agents/agent-hooks/compaction-safeguard.test.ts +++ b/src/agents/agent-hooks/compaction-safeguard.test.ts @@ -1,10 +1,11 @@ -/** Tests compaction safeguard summaries, quality audit, providers, and runtime settings. */ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import type { AgentMessage, StreamFn } from "openclaw/plugin-sdk/agent-core"; import type { ExtensionAPI, ExtensionContext } from "openclaw/plugin-sdk/agent-sessions"; import { createAssistantMessageEventStream, type Model } from "openclaw/plugin-sdk/llm"; +/** Tests compaction safeguard summaries, quality audit, providers, and runtime settings. */ +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../../config/config.js"; import { @@ -230,12 +231,7 @@ function latestMockCallArg( return mockCallArg(mock, mock.mock.calls.length - 1, argIndex); } -function requireRecord(value: unknown): Record { - if (!value || typeof value !== "object") { - throw new Error("expected record"); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-record"); function requireArray(value: unknown): unknown[] { if (!Array.isArray(value)) { diff --git a/src/agents/agent-tools.before-tool-call.e2e.test.ts b/src/agents/agent-tools.before-tool-call.e2e.test.ts index ec18985e2a12..504239a9303d 100644 --- a/src/agents/agent-tools.before-tool-call.e2e.test.ts +++ b/src/agents/agent-tools.before-tool-call.e2e.test.ts @@ -6,6 +6,7 @@ import fs from "node:fs/promises"; */ import os from "node:os"; import path from "node:path"; +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { GatewayClientRequestError } from "../gateway/client.js"; @@ -344,12 +345,7 @@ describe("before_tool_call loop detection behavior", () => { return result; } - function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null) { - throw new Error(`${label} was not an object`); - } - return value as Record; - } + const requireRecord = createRequireRecord("object", "label-not-object"); function requireArray(value: unknown, label: string): unknown[] { expect(Array.isArray(value)).toBe(true); @@ -2018,12 +2014,7 @@ describe("before_tool_call requireApproval handling", () => { let hookRunner: TestHookRunner; const mockCallGateway = vi.mocked(callGatewayTool); - function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null) { - throw new Error(`${label} was not an object`); - } - return value as Record; - } + const requireRecord = createRequireRecord("object", "label-not-object"); function requireHookCall( index: number, diff --git a/src/agents/agent-tools.before-tool-call.embedded-mode.test.ts b/src/agents/agent-tools.before-tool-call.embedded-mode.test.ts index 0aec19557073..219ec9603eff 100644 --- a/src/agents/agent-tools.before-tool-call.embedded-mode.test.ts +++ b/src/agents/agent-tools.before-tool-call.embedded-mode.test.ts @@ -5,6 +5,7 @@ */ import { expectDefined } from "@openclaw/normalization-core"; +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { clearRuntimeConfigSnapshot, setRuntimeConfigSnapshot } from "../config/config.js"; import { setEmbeddedMode } from "../infra/embedded-mode.js"; @@ -50,12 +51,7 @@ vi.mock("../logging/subsystem.js", async (importOriginal) => { const mockGetGlobalHookRunner = vi.mocked(getGlobalHookRunner); const mockCallGatewayTool = vi.mocked(callGatewayTool); -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label"); function requireApprovalRequestCall(label: string): { timeoutParams: Record; diff --git a/src/agents/auth-profiles/external-oauth.test.ts b/src/agents/auth-profiles/external-oauth.test.ts index 46a99f9cd08a..570dea07a641 100644 --- a/src/agents/auth-profiles/external-oauth.test.ts +++ b/src/agents/auth-profiles/external-oauth.test.ts @@ -3,6 +3,7 @@ * Covers provider plugin profiles, external CLI scoped discovery, persistence * rules, and external CLI bootstrap policy. */ +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { ProviderExternalAuthProfile } from "../../plugins/types.js"; import { resolveAgentCredentialMapFromStore } from "../agent-auth-credentials.js"; @@ -46,12 +47,7 @@ function createUsableOAuthExpiry(): number { return Date.now() + 30 * 60 * 1000; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function requireProfile(store: AuthProfileStore, profileId: string): Record { return requireRecord(store.profiles[profileId], profileId); diff --git a/src/agents/bash-tools.exec-approval-followup.test.ts b/src/agents/bash-tools.exec-approval-followup.test.ts index 0ebf93a0d2ff..9805e1e558d6 100644 --- a/src/agents/bash-tools.exec-approval-followup.test.ts +++ b/src/agents/bash-tools.exec-approval-followup.test.ts @@ -3,6 +3,7 @@ * Covers denied prompts, agent-session resume, wait handling, direct fallback, * and elevated runtime handoff routing. */ +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, describe, expect, it, vi } from "vitest"; vi.mock("./tools/gateway.js", () => ({ @@ -66,12 +67,7 @@ afterEach(() => { } }); -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label"); function requireFirstMockCall(mock: unknown, label: string): unknown[] { const call = (mock as { mock?: { calls?: unknown[][] } }).mock?.calls?.[0]; diff --git a/src/agents/bash-tools.exec.approval-id.test.ts b/src/agents/bash-tools.exec.approval-id.test.ts index 09fd0a40a673..821ef1b508db 100644 --- a/src/agents/bash-tools.exec.approval-id.test.ts +++ b/src/agents/bash-tools.exec.approval-id.test.ts @@ -1,12 +1,13 @@ +import crypto from "node:crypto"; +import fs from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; /** * Exec approval id routing tests. * Covers approval registration ids, follow-up idempotency, and approved * node/gateway invocation behavior. */ -import crypto from "node:crypto"; -import fs from "node:fs/promises"; -import os from "node:os"; -import path from "node:path"; +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { loadExecApprovals, @@ -378,12 +379,7 @@ function mockNoApprovalRouteRegistration() { }); } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label"); function expectRecordFields( record: Record | undefined, diff --git a/src/agents/bash-tools.process.supervisor.test.ts b/src/agents/bash-tools.process.supervisor.test.ts index c44e95f2b9c0..99d49531f438 100644 --- a/src/agents/bash-tools.process.supervisor.test.ts +++ b/src/agents/bash-tools.process.supervisor.test.ts @@ -2,6 +2,7 @@ * Regression coverage for process-tool supervisor cancellation. * Verifies managed session cancellation, process-tree fallback, and registry state. */ +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const { supervisorMock } = vi.hoisted(() => ({ @@ -43,12 +44,7 @@ function createBackgroundSession(id: string, pid?: number) { }); } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function expectSessionState(sessionId: string, expected: { exited?: boolean }) { const session = requireRecord(getSession(sessionId), sessionId); diff --git a/src/agents/cli-runner.reliability.test.ts b/src/agents/cli-runner.reliability.test.ts index b3439dc9f746..29c2c420f7f1 100644 --- a/src/agents/cli-runner.reliability.test.ts +++ b/src/agents/cli-runner.reliability.test.ts @@ -1,8 +1,9 @@ -/** Tests CLI runner reliability paths for hooks, transcripts, failover, and reply ops. */ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { expectDefined } from "@openclaw/normalization-core"; +/** Tests CLI runner reliability paths for hooks, transcripts, failover, and reply ops. */ +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { createSolidPngBuffer } from "../../test/helpers/image-fixtures.js"; import { useAutoCleanupTempDirTracker } from "../../test/helpers/temp-dir.js"; @@ -246,12 +247,7 @@ function buildPreparedContext(params?: { }; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function claudeInputStartedJson(data: string): string { const event = createClaudeInputStartedEvent(data); diff --git a/src/agents/cli-runner/session-history.test.ts b/src/agents/cli-runner/session-history.test.ts index 3bf3dc0f9d53..48e14b6b2f65 100644 --- a/src/agents/cli-runner/session-history.test.ts +++ b/src/agents/cli-runner/session-history.test.ts @@ -1,9 +1,10 @@ -// Covers CLI session transcript loading and reseeding boundaries. import fs from "node:fs"; import fsp from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { CURRENT_SESSION_VERSION } from "openclaw/plugin-sdk/agent-sessions"; +// Covers CLI session transcript loading and reseeding boundaries. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, describe, expect, it, vi } from "vitest"; import { useAutoCleanupTempDirTracker } from "../../../test/helpers/temp-dir.js"; import { withEnvAsync } from "../../test-utils/env.js"; @@ -82,12 +83,7 @@ function createOversizedSessionTranscript(rootDir: string, sessionId: string): s }); } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function expectMessageFields(value: unknown, expected: { role: string; content?: unknown }) { const message = requireRecord(value, "message"); diff --git a/src/agents/command/attempt-execution.cli.test.ts b/src/agents/command/attempt-execution.cli.test.ts index 779626ec45c2..44c37253382c 100644 --- a/src/agents/command/attempt-execution.cli.test.ts +++ b/src/agents/command/attempt-execution.cli.test.ts @@ -1,7 +1,8 @@ -// Covers CLI-backed attempt execution and session-binding persistence. import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; +// Covers CLI-backed attempt execution and session-binding persistence. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { SessionEntry } from "../../config/sessions.js"; import { @@ -471,12 +472,7 @@ async function readTranscriptEntries { - if (typeof value !== "object" || value === null) { - throw new Error(`${label} was not an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "label-not-object"); function expectRecordFields(record: Record, fields: Record) { for (const [key, value] of Object.entries(fields)) { diff --git a/src/agents/embedded-agent-runner.run-embedded-agent.auth-profile-rotation.e2e.test.ts b/src/agents/embedded-agent-runner.run-embedded-agent.auth-profile-rotation.e2e.test.ts index 45e68cafcb18..ef576a14c6ab 100644 --- a/src/agents/embedded-agent-runner.run-embedded-agent.auth-profile-rotation.e2e.test.ts +++ b/src/agents/embedded-agent-runner.run-embedded-agent.auth-profile-rotation.e2e.test.ts @@ -1,8 +1,9 @@ -// End-to-end auth-profile rotation coverage for embedded runner retries. import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import type { AssistantMessage } from "openclaw/plugin-sdk/llm"; +// End-to-end auth-profile rotation coverage for embedded runner retries. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../config/config.js"; import { redactIdentifier } from "../logging/redact-identifier.js"; @@ -522,12 +523,7 @@ async function withAgentWorkspace( } } -function requireRecord(value: unknown, label: string): Record { - if (value === null || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be a record`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-record"); function requireLogRecord( records: ReadonlyArray, diff --git a/src/agents/embedded-agent-runner/context-engine-maintenance.test.ts b/src/agents/embedded-agent-runner/context-engine-maintenance.test.ts index 88f412b0a84d..369da755784c 100644 --- a/src/agents/embedded-agent-runner/context-engine-maintenance.test.ts +++ b/src/agents/embedded-agent-runner/context-engine-maintenance.test.ts @@ -1,6 +1,7 @@ // Coverage for deferred context-engine maintenance and transcript rewrite hooks. import { expectDefined } from "@openclaw/normalization-core"; +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import type { ContextEngineRuntimeContext } from "../../context-engine/types.js"; import { peekSystemEvents, resetSystemEventsForTest } from "../../infra/system-events.js"; @@ -80,12 +81,7 @@ async function waitForAssertion( } } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label"); function firstMaintainParams(maintain: { mock: { calls: unknown[][] } }): Record { return requireRecord(maintain.mock.calls[0]?.[0], "maintain params"); diff --git a/src/agents/embedded-agent-runner/run/attempt.model-diagnostic-events.test.ts b/src/agents/embedded-agent-runner/run/attempt.model-diagnostic-events.test.ts index b2d4b64db580..3414369c5d10 100644 --- a/src/agents/embedded-agent-runner/run/attempt.model-diagnostic-events.test.ts +++ b/src/agents/embedded-agent-runner/run/attempt.model-diagnostic-events.test.ts @@ -1,7 +1,8 @@ -// Coverage for model-call diagnostic events around attempt stream functions. import { readFileSync } from "node:fs"; import { join } from "node:path"; import type { StreamFn } from "openclaw/plugin-sdk/agent-core"; +// Coverage for model-call diagnostic events around attempt stream functions. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { useAutoCleanupTempDirTracker } from "../../../../test/helpers/temp-dir.js"; import { @@ -88,12 +89,7 @@ function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } -function requireRecord(value: unknown, label: string): Record { - if (!isRecord(value)) { - throw new Error(`Expected ${label} to be an object`); - } - return value; -} +const requireRecord = createRequireRecord("record", "expected-label-object-capitalized"); function readRecordField(record: Record, key: string, label: string) { const value = record[key]; diff --git a/src/agents/embedded-agent-runner/run/attempt.spawn-workspace.context-engine.test.ts b/src/agents/embedded-agent-runner/run/attempt.spawn-workspace.context-engine.test.ts index 1f4fd8acb01b..a947b5561554 100644 --- a/src/agents/embedded-agent-runner/run/attempt.spawn-workspace.context-engine.test.ts +++ b/src/agents/embedded-agent-runner/run/attempt.spawn-workspace.context-engine.test.ts @@ -1,8 +1,9 @@ -// Coverage for context-engine bootstrap, assembly, and turn finalization. import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import type { AgentMessage } from "openclaw/plugin-sdk/agent-core"; +// Coverage for context-engine bootstrap, assembly, and turn finalization. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { HEARTBEAT_TRANSCRIPT_PROMPT } from "../../../auto-reply/heartbeat.js"; import { @@ -80,12 +81,7 @@ async function readTrajectoryEvents(tempPaths: string[]): Promise event.workspaceDir === workspaceDir); } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function requireRecords(value: unknown, label: string): Array> { expect(value, label).toBeInstanceOf(Array); diff --git a/src/agents/embedded-agent-runner/run/attempt.tool-call-normalization.test.ts b/src/agents/embedded-agent-runner/run/attempt.tool-call-normalization.test.ts index 53ff876395f6..d0300df9b910 100644 --- a/src/agents/embedded-agent-runner/run/attempt.tool-call-normalization.test.ts +++ b/src/agents/embedded-agent-runner/run/attempt.tool-call-normalization.test.ts @@ -2,6 +2,7 @@ import { expectDefined } from "@openclaw/normalization-core"; import type { AgentMessage } from "openclaw/plugin-sdk/agent-core"; +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { describe, expect, it, vi } from "vitest"; import { sanitizeOpenAIResponsesReplayForStream, @@ -45,12 +46,7 @@ async function collectStreamEvents(stream: AsyncIterable): Promise { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function requireAssistantMessage(message: AgentMessage | undefined): AssistantMessage { if (!message || message.role !== "assistant") { diff --git a/src/agents/embedded-agent-runner/stream-resolution.test.ts b/src/agents/embedded-agent-runner/stream-resolution.test.ts index 1f549f2334e5..d8248dc0a795 100644 --- a/src/agents/embedded-agent-runner/stream-resolution.test.ts +++ b/src/agents/embedded-agent-runner/stream-resolution.test.ts @@ -5,6 +5,7 @@ import * as providerTransportStream from "@openclaw/ai/transports"; // Stream resolution tests cover how embedded runs choose provider, boundary, // native Codex, or custom stream functions and pass auth/cache/signal options. import type { StreamFn } from "openclaw/plugin-sdk/agent-core"; +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, describe, expect, it, vi } from "vitest"; import { bindStreamLlmRuntime } from "../../llm/model-runtime-binding.js"; import { streamSimple } from "../../llm/stream.js"; @@ -77,14 +78,7 @@ function useNativeStreamFn(streamFn: StreamFn): StreamFn { return streamSimple as StreamFn; } -function requireRecord(value: unknown, label: string): Record { - // Test streams return their options/context as plain records; fail early if a - // route returns an unexpected shape. - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); async function expectStreamResultRecord( result: ReturnType, diff --git a/src/agents/embedded-agent-subscribe.handlers.tools.test.ts b/src/agents/embedded-agent-subscribe.handlers.tools.test.ts index 2ce907f98066..439499d8cbc6 100644 --- a/src/agents/embedded-agent-subscribe.handlers.tools.test.ts +++ b/src/agents/embedded-agent-subscribe.handlers.tools.test.ts @@ -1,6 +1,7 @@ +import type { AgentEvent } from "openclaw/plugin-sdk/agent-core"; // Tool handler tests cover tool lifecycle events, read-path diagnostics, // messaging tool capture, approvals, and emitted summaries. -import type { AgentEvent } from "openclaw/plugin-sdk/agent-core"; +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, describe, expect, it, vi } from "vitest"; import { onAgentEvent as registerAgentEventListener, @@ -247,10 +248,6 @@ function requireString(value: unknown, label: string): string { return value; } -function isRecord(value: unknown): value is Record { - return typeof value === "object" && value !== null && !Array.isArray(value); -} - describe("update_plan progress events", () => { it("emits the typed full plan snapshot after a successful result", async () => { const { ctx, onAgentEvent } = createTestContext(); @@ -296,12 +293,7 @@ describe("update_plan progress events", () => { }); }); -function requireRecord(value: unknown, label: string): Record { - if (!isRecord(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); function expectRecordFields(value: unknown, label: string, expected: Record) { const record = requireRecord(value, label); diff --git a/src/agents/harness/native-hook-relay.test.ts b/src/agents/harness/native-hook-relay.test.ts index acec82f5fde7..707e79dd20f9 100644 --- a/src/agents/harness/native-hook-relay.test.ts +++ b/src/agents/harness/native-hook-relay.test.ts @@ -1,9 +1,10 @@ -// Covers native hook relay registration, bridge invocation, and approval state. import { randomUUID } from "node:crypto"; import fs from "node:fs/promises"; import { createServer, request as httpRequest } from "node:http"; import { tmpdir } from "node:os"; import path from "node:path"; +// Covers native hook relay registration, bridge invocation, and approval state. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, describe, expect, it, vi } from "vitest"; import type { SessionEntry } from "../../config/sessions.js"; import { replaceSessionEntry } from "../../config/sessions/session-accessor.js"; @@ -46,14 +47,7 @@ function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } -function requireRecord(value: unknown, label: string): Record { - // Relay bridge payloads cross a process boundary. Tests narrow unknown JSON - // before making assertions so malformed bridge responses fail clearly. - if (!isRecord(value)) { - throw new Error(`Expected ${label} to be an object`); - } - return value; -} +const requireRecord = createRequireRecord("record", "expected-label-object-capitalized"); function readRecordField(record: Record, key: string, label: string) { const value = record[key]; diff --git a/src/agents/model-fallback.probe.test.ts b/src/agents/model-fallback.probe.test.ts index 9e58b06223fb..a46084ac6b25 100644 --- a/src/agents/model-fallback.probe.test.ts +++ b/src/agents/model-fallback.probe.test.ts @@ -1,7 +1,8 @@ -// Verifies fallback cooldown probe decisions and diagnostic records. import { randomUUID } from "node:crypto"; import os from "node:os"; import path from "node:path"; +// Verifies fallback cooldown probe decisions and diagnostic records. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../config/config.js"; import { createDiagnosticLogRecordCapture } from "../logging/test-helpers/diagnostic-log-capture.js"; @@ -202,12 +203,7 @@ function expectPrimaryProbeSuccess( }); } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function expectRecordWithFields( records: Array>, diff --git a/src/agents/model-fallback.test.ts b/src/agents/model-fallback.test.ts index 6f48416f2280..283eee59bdfa 100644 --- a/src/agents/model-fallback.test.ts +++ b/src/agents/model-fallback.test.ts @@ -1,7 +1,8 @@ -// Covers model fallback ordering, error classification, and auth cooldown behavior. import crypto from "node:crypto"; import path from "node:path"; import { expectDefined } from "@openclaw/normalization-core"; +// Covers model fallback ordering, error classification, and auth cooldown behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { TranscriptNotContinuableError } from "../../packages/agent-core/src/errors.js"; import type { OpenClawConfig } from "../config/config.js"; @@ -369,12 +370,7 @@ function setAuthRuntimeStore(agentDir: string | undefined, store: AuthProfileSto authRuntimeMock.setStore(agentDir, store); } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label"); function requireMockCall( mock: { mock: { calls: unknown[][] } }, diff --git a/src/agents/openclaw-tools.camera.test.ts b/src/agents/openclaw-tools.camera.test.ts index 8e686fab490f..8e45e7454a3c 100644 --- a/src/agents/openclaw-tools.camera.test.ts +++ b/src/agents/openclaw-tools.camera.test.ts @@ -1,6 +1,7 @@ -// Verifies node camera/photo tool payloads, media URLs, and vision gating. import crypto from "node:crypto"; import fs from "node:fs/promises"; +// Verifies node camera/photo tool payloads, media URLs, and vision gating. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { cameraTempPath } from "../cli/nodes-camera.js"; import { @@ -77,12 +78,7 @@ async function executeNodes( type NodesToolResult = Awaited>; type GatewayMockResult = Record | null | undefined; -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label"); function expectInvokeParams( invokeParams: unknown, diff --git a/src/agents/sandbox/ssh-backend.test.ts b/src/agents/sandbox/ssh-backend.test.ts index 41e78dd4279c..9fd233101a5f 100644 --- a/src/agents/sandbox/ssh-backend.test.ts +++ b/src/agents/sandbox/ssh-backend.test.ts @@ -4,6 +4,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { + createRequireRecord, createSandboxBrowserConfig, createSandboxPruneConfig, createSandboxSshConfig, @@ -74,12 +75,7 @@ function createSession() { }; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function requireMockRecordArg(mock: ReturnType, callIndex: number, label: string) { return requireRecord(mock.mock.calls[callIndex]?.[0], label); diff --git a/src/agents/sessions-spawn-hooks.test.ts b/src/agents/sessions-spawn-hooks.test.ts index 666c1bff64fd..4a2debf73582 100644 --- a/src/agents/sessions-spawn-hooks.test.ts +++ b/src/agents/sessions-spawn-hooks.test.ts @@ -1,4 +1,5 @@ // Verifies sessions_spawn lifecycle hooks, binding cleanup, and gateway calls. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { createSubagentSpawnTestConfig, @@ -71,12 +72,7 @@ function findGatewayRequest(method: string): GatewayRequest | undefined { return getGatewayRequests().find((request) => request.method === method); } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function expectFields(value: unknown, expected: Record, label = "object"): void { const record = requireRecord(value, label); diff --git a/src/agents/subagent-registry.steer-restart.test.ts b/src/agents/subagent-registry.steer-restart.test.ts index 39d7ec03226d..f08d13d298b2 100644 --- a/src/agents/subagent-registry.steer-restart.test.ts +++ b/src/agents/subagent-registry.steer-restart.test.ts @@ -2,6 +2,7 @@ // commands while preserving lifecycle hooks and completion delivery. import { expectDefined } from "@openclaw/normalization-core"; +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { ContextEngine } from "../context-engine/types.js"; import { getDetachedTaskLifecycleRuntime } from "../tasks/detached-task-runtime.js"; @@ -106,12 +107,7 @@ function countMatching(items: readonly T[], predicate: (item: T) => boolean) return count; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label"); function requireSubagentEndedHookCall(runId: string): { event: Record; diff --git a/src/agents/subagent-spawn.test.ts b/src/agents/subagent-spawn.test.ts index 851a5c2df6d6..5c984eb7449c 100644 --- a/src/agents/subagent-spawn.test.ts +++ b/src/agents/subagent-spawn.test.ts @@ -1,6 +1,7 @@ +import os from "node:os"; // Subagent spawn tests cover target policy, session patching, runtime model // persistence, registry registration, and lifecycle event emission. -import os from "node:os"; +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { resolveIncognitoOpenClawAgentSqlitePath } from "../state/openclaw-agent-db.paths.js"; @@ -52,12 +53,7 @@ function createConfigOverride(overrides?: Record) { }); } -function requireRecord(value: unknown): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error("Expected a non-array record"); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-non-array-record"); function gatewayRequestRecords(): Record[] { // Gateway calls are the seam proof for spawn orchestration; assertions inspect diff --git a/src/agents/tools/image-generate-tool.test.ts b/src/agents/tools/image-generate-tool.test.ts index 57a465e9af6b..13f8c494b40b 100644 --- a/src/agents/tools/image-generate-tool.test.ts +++ b/src/agents/tools/image-generate-tool.test.ts @@ -1,5 +1,6 @@ // image_generate tool tests cover provider/model selection, edit inputs, // background task handling, media saving, and duplicate-generation guards. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; @@ -192,12 +193,7 @@ function mockCallArg( return call[argIndex] as Record; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`Expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label-capitalized"); type ImageGenerateTool = NonNullable>; type ToolResult = Awaited>; diff --git a/src/agents/tools/sessions-spawn-tool.test.ts b/src/agents/tools/sessions-spawn-tool.test.ts index 407f832f78d2..0e2eea32b816 100644 --- a/src/agents/tools/sessions-spawn-tool.test.ts +++ b/src/agents/tools/sessions-spawn-tool.test.ts @@ -1,6 +1,7 @@ +import path from "node:path"; // sessions_spawn tool tests cover model-visible schema gating, ACP/subagent // dispatch, and result details for spawned child sessions. -import path from "node:path"; +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { upsertSessionEntry } from "../../config/sessions/session-accessor.js"; import { withTempDir } from "../../test-helpers/temp-dir.js"; @@ -110,12 +111,7 @@ describe("sessions_spawn tool", () => { return property; } - function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label}`); - } - return value as Record; - } + const requireRecord = createRequireRecord("record", "expected-label"); function expectDetailFields(details: unknown, expected: Record) { const record = requireRecord(details, "result details"); diff --git a/src/agents/tools/sessions.test.ts b/src/agents/tools/sessions.test.ts index 9289390b8893..c89e0b6c720c 100644 --- a/src/agents/tools/sessions.test.ts +++ b/src/agents/tools/sessions.test.ts @@ -1,7 +1,8 @@ -// Sessions tool tests cover list/send helpers, announce-target resolution, -// and assistant-visible text sanitization. import { expectDefined } from "@openclaw/normalization-core"; import { MAX_TIMER_TIMEOUT_MS } from "@openclaw/normalization-core/number-coercion"; +// Sessions tool tests cover list/send helpers, announce-target resolution, +// and assistant-visible text sanitization. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { ChannelMessagingAdapter } from "../../channels/plugins/types.public.js"; import { clearRuntimeConfigSnapshot, setRuntimeConfigSnapshot } from "../../config/io.js"; @@ -109,12 +110,7 @@ const resolveSessionTargetStub: NonNullable (threadId ? `${kind}:${id}:thread:${threadId}` : `${kind}:${id}`); -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label"); function requireDetails(result: { details?: unknown }, label = "result details") { return requireRecord(result.details, label); diff --git a/src/agents/tools/tts-tool.test.ts b/src/agents/tools/tts-tool.test.ts index 3f7fee8244fd..b273a83d5639 100644 --- a/src/agents/tools/tts-tool.test.ts +++ b/src/agents/tools/tts-tool.test.ts @@ -2,18 +2,14 @@ // timeout validation, and reply-directive defusing. import { expectDefined } from "@openclaw/normalization-core"; +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import * as ttsRuntime from "../../tts/tts.js"; import { createTtsTool } from "./tts-tool.js"; let textToSpeechSpy: ReturnType; -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function latestTextToSpeechArgs(): Record { // Speech runtime args are the public handoff between the model-facing tool diff --git a/src/auto-reply/reply/agent-runner.misc.runreplyagent.test.ts b/src/auto-reply/reply/agent-runner.misc.runreplyagent.test.ts index 6ff93914391b..d04d4e30a28b 100644 --- a/src/auto-reply/reply/agent-runner.misc.runreplyagent.test.ts +++ b/src/auto-reply/reply/agent-runner.misc.runreplyagent.test.ts @@ -1,7 +1,8 @@ -// Tests miscellaneous run-reply-agent behaviors and artifact output. import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; +// Tests miscellaneous run-reply-agent behaviors and artifact output. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { testing as cliBackendsTesting } from "../../agents/cli-backends.test-support.js"; import { @@ -242,12 +243,7 @@ type RunWithModelFallbackParams = { run: (provider: string, model: string) => Promise; }; -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null || Array.isArray(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); function expectRecordFields( value: unknown, diff --git a/src/auto-reply/reply/agent-runner.runreplyagent.e2e.test.ts b/src/auto-reply/reply/agent-runner.runreplyagent.e2e.test.ts index fcb7deafa8c9..3e1f15fb0a11 100644 --- a/src/auto-reply/reply/agent-runner.runreplyagent.e2e.test.ts +++ b/src/auto-reply/reply/agent-runner.runreplyagent.e2e.test.ts @@ -1,7 +1,8 @@ -// E2E tests for run-reply-agent execution and generated session artifacts. import { mkdtemp, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; +// E2E tests for run-reply-agent execution and generated session artifacts. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { useAutoCleanupTempDirTracker } from "../../../test/helpers/temp-dir.js"; import { SessionWriteLockStaleError } from "../../agents/session-write-lock-error.js"; @@ -89,12 +90,7 @@ function countMatching(items: readonly T[], predicate: (item: T) => boolean): return count; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); function mockCallArgs(mock: ReturnType, label: string, callIndex = 0): unknown[] { const call = mock.mock.calls[callIndex] as unknown[] | undefined; diff --git a/src/auto-reply/reply/commands-approve.test.ts b/src/auto-reply/reply/commands-approve.test.ts index ea0a64498a60..d0f3a67169d2 100644 --- a/src/auto-reply/reply/commands-approve.test.ts +++ b/src/auto-reply/reply/commands-approve.test.ts @@ -1,4 +1,5 @@ // Tests approval command behavior for pending tool and execution requests. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import type { ChannelApprovalCapability, @@ -24,12 +25,7 @@ vi.mock("../../globals.js", () => ({ logVerbose: vi.fn(), })); -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); function approvalResolverRequest(callIndex = 0) { const call = resolveApprovalOverGatewayMock.mock.calls[callIndex] as unknown[] | undefined; diff --git a/src/auto-reply/reply/commands-plugins.test.ts b/src/auto-reply/reply/commands-plugins.test.ts index 783252885227..70c85ca6ab35 100644 --- a/src/auto-reply/reply/commands-plugins.test.ts +++ b/src/auto-reply/reply/commands-plugins.test.ts @@ -1,4 +1,5 @@ // Tests plugin command install, listing, and config behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../../config/config.js"; import { handlePluginsCommand } from "./commands-plugins.js"; @@ -154,12 +155,7 @@ type MockCalls = { mock: { calls: unknown[][] }; }; -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function getNestedRecord(record: Record, key: string, label: string) { return requireRecord(record[key], label); diff --git a/src/auto-reply/reply/commands-reset-hooks.test.ts b/src/auto-reply/reply/commands-reset-hooks.test.ts index 0192fe173216..3015190b48ef 100644 --- a/src/auto-reply/reply/commands-reset-hooks.test.ts +++ b/src/auto-reply/reply/commands-reset-hooks.test.ts @@ -1,4 +1,5 @@ // Tests reset hook emission and cleanup around reset commands. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import * as bootstrapCache from "../../agents/bootstrap-cache.js"; import type { OpenClawConfig } from "../../config/config.js"; @@ -122,12 +123,7 @@ function mockCall(mock: unknown, index = 0): Array { return call; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function expectObjectFields( value: unknown, diff --git a/src/auto-reply/reply/dispatch-acp.test.ts b/src/auto-reply/reply/dispatch-acp.test.ts index 9f1c9e064f7c..75a3fff3c2b4 100644 --- a/src/auto-reply/reply/dispatch-acp.test.ts +++ b/src/auto-reply/reply/dispatch-acp.test.ts @@ -1,8 +1,9 @@ -// Tests ACP dispatch wiring, command bypass, and runtime event handling. import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { detectMime } from "@openclaw/media-core/mime"; +// Tests ACP dispatch wiring, command bypass, and runtime event handling. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import type { MediaUnderstandingSkipError } from "../../../packages/media-understanding-common/src/errors.js"; import { AcpRuntimeError } from "../../acp/runtime/errors.js"; @@ -268,12 +269,7 @@ const originalFetch = globalThis.fetch; type MockTtsReply = Awaited>; type MockCallSource = { mock: { calls: Array> } }; -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function mockArg(source: MockCallSource, callIndex: number, argIndex: number, _label: string) { return source.mock.calls[callIndex]?.[argIndex]; diff --git a/src/auto-reply/reply/get-reply-inline-actions.skip-when-config-empty.test.ts b/src/auto-reply/reply/get-reply-inline-actions.skip-when-config-empty.test.ts index d27b91d7904f..aafce452e2b3 100644 --- a/src/auto-reply/reply/get-reply-inline-actions.skip-when-config-empty.test.ts +++ b/src/auto-reply/reply/get-reply-inline-actions.skip-when-config-empty.test.ts @@ -1,7 +1,8 @@ -// Tests inline action skipping when channel config does not define actions. import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; +// Tests inline action skipping when channel config does not define actions. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import type { SessionEntry } from "../../config/sessions.js"; import { replaceSessionEntry } from "../../config/sessions/session-accessor.js"; @@ -171,12 +172,7 @@ async function runInlineStatusAction(storePath?: string) { return { result, typing }; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); function mockObjectArg(mock: ReturnType, label: string, callIndex = 0, argIndex = 0) { const call = mock.mock.calls[callIndex]; diff --git a/src/channels/plugins/setup-helpers.test.ts b/src/channels/plugins/setup-helpers.test.ts index 840c749e865a..a32f4c3aa6f2 100644 --- a/src/channels/plugins/setup-helpers.test.ts +++ b/src/channels/plugins/setup-helpers.test.ts @@ -1,5 +1,6 @@ -// Setup helper tests cover channel setup helper outputs and lifecycle cleanup. import { expectDefined } from "@openclaw/normalization-core"; +// Setup helper tests cover channel setup helper outputs and lifecycle cleanup. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterAll, beforeEach, describe, expect, it } from "vitest"; import type { OpenClawConfig } from "../../config/config.js"; import { resetPluginRuntimeStateForTest, setActivePluginRegistry } from "../../plugins/runtime.js"; @@ -22,12 +23,7 @@ function asConfig(value: unknown): OpenClawConfig { return value as OpenClawConfig; } -function requireRecord(value: unknown): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error("Expected a non-array record"); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-non-array-record"); function channelRecord(cfg: OpenClawConfig, channelKey: string): Record { return requireRecord(cfg.channels?.[channelKey]); diff --git a/src/cli/config-cli.test.ts b/src/cli/config-cli.test.ts index a8a156c69018..42816b41f804 100644 --- a/src/cli/config-cli.test.ts +++ b/src/cli/config-cli.test.ts @@ -1,8 +1,9 @@ -// Config CLI tests cover config command registration, reads, writes, and output modes. import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { Command } from "commander"; +// Config CLI tests cover config command registration, reads, writes, and output modes. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { ConfigFileSnapshot, OpenClawConfig } from "../config/types.js"; import type { PluginManifestRecord, PluginManifestRegistry } from "../plugins/manifest-registry.js"; @@ -471,12 +472,7 @@ function expectErrorIncludes(text: string) { expect(mockError.mock.calls.map((call) => String(call[0])).join("\n")).toContain(text); } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); function requireResolveSecretRefCall(index: number): [unknown, unknown] { const call = mockResolveSecretRefValue.mock.calls[index]; diff --git a/src/cli/devices-cli.test.ts b/src/cli/devices-cli.test.ts index b0564582fe74..138f5c07fe7a 100644 --- a/src/cli/devices-cli.test.ts +++ b/src/cli/devices-cli.test.ts @@ -1,5 +1,6 @@ -// Devices CLI tests cover device command registration and output behavior. import { Command } from "commander"; +// Devices CLI tests cover device command registration and output behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { stripAnsi } from "../../packages/terminal-core/src/ansi.js"; import { registerDevicesCli } from "./devices-cli.js"; @@ -168,12 +169,7 @@ function mockApprovedReplacement() { }); } -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null) { - throw new Error(`${label} was not an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "label-not-object"); function expectRecordFields(record: Record, fields: Record) { for (const [key, value] of Object.entries(fields)) { diff --git a/src/cli/exec-approvals-cli.pending-resolve.test.ts b/src/cli/exec-approvals-cli.pending-resolve.test.ts index fcdcd080031a..144ce2a80267 100644 --- a/src/cli/exec-approvals-cli.pending-resolve.test.ts +++ b/src/cli/exec-approvals-cli.pending-resolve.test.ts @@ -1,5 +1,6 @@ -// Pending and resolve CLI tests stay separate from policy-management coverage. import { Command } from "commander"; +// Pending and resolve CLI tests stay separate from policy-management coverage. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { registerExecApprovalsCli } from "./exec-approvals-cli.js"; @@ -30,12 +31,7 @@ const mocks = vi.hoisted(() => { const { callGatewayFromCli, defaultRuntime, runtimeErrors } = mocks; -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`Expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-capitalized"); function firstMockArg(mock: { mock: { calls: ReadonlyArray> } }): unknown { const call = mock.mock.calls[0]; diff --git a/src/cli/exec-approvals-cli.test.ts b/src/cli/exec-approvals-cli.test.ts index a6783a40c44e..0bd1eaf707a9 100644 --- a/src/cli/exec-approvals-cli.test.ts +++ b/src/cli/exec-approvals-cli.test.ts @@ -1,8 +1,9 @@ -// Exec approvals CLI tests cover approval command registration and output handling. import fs from "node:fs"; import path from "node:path"; import { Readable } from "node:stream"; import { Command } from "commander"; +// Exec approvals CLI tests cover approval command registration and output handling. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { useAutoCleanupTempDirTracker } from "../../test/helpers/temp-dir.js"; import { SESSION_EXEC_OVERRIDES_NOTE } from "../infra/exec-approvals-effective.js"; @@ -96,12 +97,7 @@ const localSnapshot = { file: { version: 1, agents: {} } as ExecApprovalsFile, }; -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`Expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-capitalized"); function requireArray(value: unknown, label: string): unknown[] { if (!Array.isArray(value)) { diff --git a/src/cli/program/message/helpers.test.ts b/src/cli/program/message/helpers.test.ts index f171eabb317a..586e45c50e7f 100644 --- a/src/cli/program/message/helpers.test.ts +++ b/src/cli/program/message/helpers.test.ts @@ -1,4 +1,5 @@ // Message program helper tests cover message command helper behavior and mocks. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; const messageCommandMock = vi.fn(async () => {}); @@ -102,12 +103,7 @@ function expectNoAccountFieldInPassedOptions() { expect(passedOpts).not.toHaveProperty("account"); } -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null || Array.isArray(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); function expectMessageCommandOptions(expected: Record, callIndex = 0): void { const call = (messageCommandMock.mock.calls as unknown[][])[callIndex]; diff --git a/src/cli/program/register.status-health-sessions.test.ts b/src/cli/program/register.status-health-sessions.test.ts index d4d5c3df3834..2ceaa553abbe 100644 --- a/src/cli/program/register.status-health-sessions.test.ts +++ b/src/cli/program/register.status-health-sessions.test.ts @@ -1,5 +1,6 @@ -// Register status/health/session tests cover status-related command registration. import { Command } from "commander"; +// Register status/health/session tests cover status-related command registration. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { registerStatusHealthSessionsCommands } from "./register.status-health-sessions.js"; @@ -59,12 +60,7 @@ type MockCalls = { mock: { calls: unknown[][] }; }; -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function expectCommandOptions(command: MockCalls, expected: Record) { expect(command.mock.calls).toHaveLength(1); diff --git a/src/commands/agent-via-gateway.test.ts b/src/commands/agent-via-gateway.test.ts index 0a966df84661..738ad6c480b7 100644 --- a/src/commands/agent-via-gateway.test.ts +++ b/src/commands/agent-via-gateway.test.ts @@ -1,9 +1,10 @@ -// Agent via gateway tests cover gateway-backed agent command dispatch and session loading. import { execFileSync } from "node:child_process"; import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { MAX_TIMER_TIMEOUT_MS } from "@openclaw/normalization-core/number-coercion"; +// Agent via gateway tests cover gateway-backed agent command dispatch and session loading. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { configureExecutionIdentityAdmissionSink, @@ -143,12 +144,7 @@ function requireFirstCallOrder( return order; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-object-short"); function createSignalProcess() { type SignalName = "SIGINT" | "SIGTERM"; diff --git a/src/commands/channel-setup/plugin-install.test.ts b/src/commands/channel-setup/plugin-install.test.ts index 4d7cd6c9ef46..a59ac7519f92 100644 --- a/src/commands/channel-setup/plugin-install.test.ts +++ b/src/commands/channel-setup/plugin-install.test.ts @@ -1,6 +1,10 @@ // Channel setup plugin install tests cover install decisions, registry reloads, scoped snapshots, and trust boundaries. import path from "node:path"; -import { bundledPluginRoot, bundledPluginRootAt } from "openclaw/plugin-sdk/test-fixtures"; +import { + createRequireRecord, + bundledPluginRoot, + bundledPluginRootAt, +} from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; vi.mock("node:fs", async () => { @@ -314,12 +318,7 @@ function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } -function requireRecord(value: unknown, label: string): Record { - if (!isRecord(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); function requireArray(value: unknown, label: string): unknown[] { if (!Array.isArray(value)) { diff --git a/src/commands/channels.add.test.ts b/src/commands/channels.add.test.ts index 01493481d306..2025d646c77d 100644 --- a/src/commands/channels.add.test.ts +++ b/src/commands/channels.add.test.ts @@ -1,4 +1,5 @@ // Channels add tests cover guided setup, plugin install paths, and channel account config writes. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { getBundledChannelSetupPlugin } from "../channels/plugins/bundled.js"; import type { ChannelPluginCatalogEntry } from "../channels/plugins/catalog.js"; @@ -154,12 +155,7 @@ function listConfiguredAccountIds( return []; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function mockArg(source: MockCallSource, callIndex: number, argIndex: number, label: string) { const call = source.mock.calls[callIndex]; diff --git a/src/commands/channels.resolve.test.ts b/src/commands/channels.resolve.test.ts index 5a11b32fd994..696ed7b1948c 100644 --- a/src/commands/channels.resolve.test.ts +++ b/src/commands/channels.resolve.test.ts @@ -1,4 +1,5 @@ // Channels resolve tests cover channel/account selection and command output for message routing. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { channelsResolveCommand } from "./channels/resolve.js"; @@ -54,12 +55,7 @@ vi.mock("../channels/plugins/index.js", () => ({ getChannelPlugin: mocks.getChannelPlugin, })); -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label"); function requireFirstMockArg( mock: { mock: { calls: unknown[][] } }, diff --git a/src/commands/channels/capabilities.test.ts b/src/commands/channels/capabilities.test.ts index c034d1963a1f..498d0865b011 100644 --- a/src/commands/channels/capabilities.test.ts +++ b/src/commands/channels/capabilities.test.ts @@ -1,4 +1,5 @@ // Channels capabilities tests cover capability reporting, account selection, probes, and installable plugins. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { getChannelPlugin, listChannelPlugins } from "../../channels/plugins/index.js"; import type { ChannelPlugin } from "../../channels/plugins/types.public.js"; @@ -71,12 +72,7 @@ function resetOutput() { errors.length = 0; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label"); function requireFirstMockArg( mock: { mock: { calls: unknown[][] } }, diff --git a/src/commands/configure.wizard.test.ts b/src/commands/configure.wizard.test.ts index 3b83aa877f0d..c02a956ef867 100644 --- a/src/commands/configure.wizard.test.ts +++ b/src/commands/configure.wizard.test.ts @@ -1,4 +1,5 @@ // Configure wizard tests cover guided setup routing across gateway, auth, channels, skills, and search. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../config/config.js"; import type { RuntimeEnv } from "../runtime.js"; @@ -261,12 +262,7 @@ function setupBaseWizardState(config: OpenClawConfig = {}) { }); } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function mockCallArg( mock: { mock: { calls: ReadonlyArray> } }, diff --git a/src/commands/doctor-device-pairing.test.ts b/src/commands/doctor-device-pairing.test.ts index 75071b46d646..b0f9df3013e0 100644 --- a/src/commands/doctor-device-pairing.test.ts +++ b/src/commands/doctor-device-pairing.test.ts @@ -1,6 +1,7 @@ -// Doctor device pairing tests cover device-pairing checks, repair prompts, and diagnostics. import fs from "node:fs/promises"; import path from "node:path"; +// Doctor device pairing tests cover device-pairing checks, repair prompts, and diagnostics. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { storeDeviceAuthToken } from "../infra/device-auth-store.js"; import { @@ -52,12 +53,7 @@ function requireNoteTitle(callIndex = 0): unknown { return title; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label} record`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label-record-short"); describe("noteDevicePairingHealth", () => { let collectDevicePairingHealthFindings: typeof import("./doctor-device-pairing.js").collectDevicePairingHealthFindings; diff --git a/src/commands/doctor-gateway-services.test.ts b/src/commands/doctor-gateway-services.test.ts index 1031e8963db2..a63eb12d7a51 100644 --- a/src/commands/doctor-gateway-services.test.ts +++ b/src/commands/doctor-gateway-services.test.ts @@ -1,7 +1,8 @@ -// Doctor gateway service tests cover service audit diagnostics and duplicate gateway service reporting. import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; +// Doctor gateway service tests cover service audit diagnostics and duplicate gateway service reporting. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../config/config.js"; import { withEnvAsync } from "../test-utils/env.js"; @@ -308,12 +309,7 @@ function createGatewayCommand(entrypoint: string) { }; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function callArg(mock: { mock: { calls: Array> } }, index: number, label: string) { const call = mock.mock.calls[index]; diff --git a/src/commands/doctor/cron/index.test.ts b/src/commands/doctor/cron/index.test.ts index a76c59061f51..d220dafaf2a0 100644 --- a/src/commands/doctor/cron/index.test.ts +++ b/src/commands/doctor/cron/index.test.ts @@ -1,8 +1,9 @@ -// Doctor cron index tests cover cron doctor checks and repair entrypoints. import fsSync from "node:fs"; import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; +// Doctor cron index tests cover cron doctor checks and repair entrypoints. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../../../config/config.js"; import { @@ -183,12 +184,7 @@ function requirePersistedJob(jobs: Array>, index: number return job; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label"); function expectNoteContaining(message: string, title: string): void { expect( diff --git a/src/commands/doctor/shared/plugin-registry-migration.test.ts b/src/commands/doctor/shared/plugin-registry-migration.test.ts index 23bc0bee2cac..784cb3db7233 100644 --- a/src/commands/doctor/shared/plugin-registry-migration.test.ts +++ b/src/commands/doctor/shared/plugin-registry-migration.test.ts @@ -1,6 +1,7 @@ -// Plugin registry migration tests cover doctor repair of persisted plugin registry state. import fs from "node:fs"; import path from "node:path"; +// Plugin registry migration tests cover doctor repair of persisted plugin registry state. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, describe, expect, it, vi } from "vitest"; import type { PluginCandidate } from "../../../plugins/discovery.js"; import { @@ -79,16 +80,7 @@ function createCurrentIndex(): InstalledPluginIndex { }; } -function isRecord(value: unknown): value is Record { - return typeof value === "object" && value !== null && !Array.isArray(value); -} - -function requireRecord(value: unknown, label: string): Record { - if (!isRecord(value)) { - throw new Error(`Expected ${label} to be an object`); - } - return value; -} +const requireRecord = createRequireRecord("record", "expected-label-object-capitalized"); function expectRecordFields(record: Record, fields: Record) { for (const [key, value] of Object.entries(fields)) { expect(record[key]).toEqual(value); diff --git a/src/commands/gateway-status.test.ts b/src/commands/gateway-status.test.ts index 256bd9b18346..d8efef4b559c 100644 --- a/src/commands/gateway-status.test.ts +++ b/src/commands/gateway-status.test.ts @@ -1,5 +1,6 @@ -// Gateway status command tests cover probe targets, JSON/text output, SSH tunnels, and warnings. import { expectDefined } from "@openclaw/normalization-core"; +// Gateway status command tests cover probe targets, JSON/text output, SSH tunnels, and warnings. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import type { GatewayProbeResult } from "../gateway/probe.js"; import type { GatewayBonjourBeacon } from "../infra/bonjour-discovery.js"; @@ -325,12 +326,7 @@ async function runGatewayStatus( await gatewayStatusCommand(opts, asRuntimeEnv(runtime)); } -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null) { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function requireRecordArray(value: unknown, label: string): Array> { if ( diff --git a/src/commands/migrate/selection.test.ts b/src/commands/migrate/selection.test.ts index b673fbabf3a9..9d4d122d3a6a 100644 --- a/src/commands/migrate/selection.test.ts +++ b/src/commands/migrate/selection.test.ts @@ -1,6 +1,7 @@ // Migration selection tests cover skill/plugin filtering, defaults, shortcuts, and skipped-item reasons. import { expectDefined } from "@openclaw/normalization-core"; +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { describe, expect, it } from "vitest"; import type { MigrationItem, MigrationPlan } from "../../plugins/types.js"; import { applyMigrationItemSelection } from "./item-selection.js"; @@ -154,12 +155,7 @@ function expectItemStatus( } } -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null) { - throw new Error(`${label} was not an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "label-not-object"); function requireCodexPluginConfigPlugins(item: MigrationItem): Record { const details = requireRecord(item.details, "config details"); diff --git a/src/commands/model-picker.test.ts b/src/commands/model-picker.test.ts index f9bcd01611a8..251d35f0de65 100644 --- a/src/commands/model-picker.test.ts +++ b/src/commands/model-picker.test.ts @@ -1,6 +1,7 @@ -// Model picker tests cover catalog rows, provider metadata, backend defaults, and prompt choices. import path from "node:path"; import type { NormalizedModelCatalogRow } from "@openclaw/model-catalog-core/model-catalog-types"; +// Model picker tests cover catalog rows, provider metadata, backend defaults, and prompt choices. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { testing as cliBackendsTesting } from "../agents/cli-backends.test-support.js"; import type { ModelCatalogEntry } from "../agents/model-catalog.js"; @@ -302,12 +303,7 @@ type PickerOption = Record & { value: string; }; -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function mockArg(source: MockCallSource, callIndex: number, argIndex: number, label: string) { const call = source.mock.calls[callIndex]; diff --git a/src/commands/models/list.status.test.ts b/src/commands/models/list.status.test.ts index 954b4e2b7534..5dba576987b2 100644 --- a/src/commands/models/list.status.test.ts +++ b/src/commands/models/list.status.test.ts @@ -1,4 +1,5 @@ // Model list status tests cover status column construction and auth/probe summaries. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { describe, expect, it, type Mock, vi } from "vitest"; import { getCurrentPluginMetadataSnapshot, @@ -349,12 +350,7 @@ function parseFirstJsonLog(runtimeLike: { log: Mock }) { return JSON.parse(String(runtimeLike.log.mock.calls[0]?.[0])); } -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null) { - throw new Error(`${label} was not an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "label-not-object"); function expectRecordFields(record: Record, fields: Record) { for (const [key, value] of Object.entries(fields)) { diff --git a/src/commands/onboard-auth.test.ts b/src/commands/onboard-auth.test.ts index 936792072fa9..97ccebdc6304 100644 --- a/src/commands/onboard-auth.test.ts +++ b/src/commands/onboard-auth.test.ts @@ -1,7 +1,8 @@ -// Onboard auth tests cover provider auth setup, credential persistence, and auth-profile state. import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; +// Onboard auth tests cover provider auth setup, credential persistence, and auth-profile state. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, describe, expect, it, vi } from "vitest"; import type { OAuthCredentials } from "../llm/utils/oauth/types.js"; import { @@ -97,12 +98,7 @@ vi.mock("../secrets/provider-env-vars.js", () => ({ }), })); -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function expectFields(value: unknown, expected: Record, label = "record") { const record = requireRecord(value, label); diff --git a/src/cron/isolated-agent/delivery-dispatch.double-announce.test.ts b/src/cron/isolated-agent/delivery-dispatch.double-announce.test.ts index 92c681464198..7b259b1a918c 100644 --- a/src/cron/isolated-agent/delivery-dispatch.double-announce.test.ts +++ b/src/cron/isolated-agent/delivery-dispatch.double-announce.test.ts @@ -10,6 +10,7 @@ * returning so the timer correctly skips the system-event fallback. */ +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { SILENT_REPLY_TOKEN } from "../../auto-reply/tokens.js"; import * as deliveryQueueSqlite from "../../infra/delivery-queue-sqlite.js"; @@ -267,12 +268,7 @@ function makeBaseParams(overrides: { }; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function outboundDeliveryCall(callIndex = 0) { const call = vi.mocked(deliverOutboundPayloads).mock.calls[callIndex]; diff --git a/src/cron/isolated-agent/run.cron-model-override-forwarding.test.ts b/src/cron/isolated-agent/run.cron-model-override-forwarding.test.ts index d88429832e79..9fe04ae892eb 100644 --- a/src/cron/isolated-agent/run.cron-model-override-forwarding.test.ts +++ b/src/cron/isolated-agent/run.cron-model-override-forwarding.test.ts @@ -1,4 +1,5 @@ // Cron model override forwarding tests cover passing overrides into agent runs. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { clearCliSessionMock, @@ -111,12 +112,7 @@ function createDeferred() { return { promise, resolve, reject }; } -function requireRecord(value: unknown): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error("Expected a non-array record"); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-non-array-record"); function firstMockArg(mock: { mock: { calls: unknown[][] } }): Record { return requireRecord(mock.mock.calls[0]?.[0]); diff --git a/src/cron/isolated-agent/run.message-tool-policy.test.ts b/src/cron/isolated-agent/run.message-tool-policy.test.ts index 54b268d1efc8..dc6d688b77db 100644 --- a/src/cron/isolated-agent/run.message-tool-policy.test.ts +++ b/src/cron/isolated-agent/run.message-tool-policy.test.ts @@ -1,4 +1,5 @@ // Message tool policy tests cover message tool availability during cron runs. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { createSourceDeliveryPlan } from "../../infra/outbound/source-delivery-plan.js"; import type { SkillSnapshot } from "../../skills/types.js"; @@ -119,12 +120,7 @@ function mockPendingMessagePresentationWarningOutcome() { }); } -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null || Array.isArray(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); function expectRecordFields( value: unknown, diff --git a/src/cron/isolated-agent/run.skill-filter.test.ts b/src/cron/isolated-agent/run.skill-filter.test.ts index d24f4b46f00c..cff28e765b1b 100644 --- a/src/cron/isolated-agent/run.skill-filter.test.ts +++ b/src/cron/isolated-agent/run.skill-filter.test.ts @@ -1,4 +1,5 @@ // Skill filter tests cover active skill selection for isolated cron runs. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { describe, expect, it } from "vitest"; import { makeIsolatedAgentJobFixture, makeIsolatedAgentParamsFixture } from "./job-fixtures.js"; import { setupRunCronIsolatedAgentTurnSuite } from "./run.suite-helpers.js"; @@ -22,12 +23,7 @@ import { const runCronIsolatedAgentTurn = await loadRunCronIsolatedAgentTurn(); -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null || Array.isArray(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); function getMockCallArg( mock: { mock: { calls: readonly unknown[][] } }, diff --git a/src/cron/service/timer.timeout-watchdog.test.ts b/src/cron/service/timer.timeout-watchdog.test.ts index cc2c0665797d..448b03090a07 100644 --- a/src/cron/service/timer.timeout-watchdog.test.ts +++ b/src/cron/service/timer.timeout-watchdog.test.ts @@ -1,4 +1,5 @@ // Integration regressions for cron execution timeouts and setup watchdogs. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { describe, expect, it, vi } from "vitest"; import { createDeferred, @@ -30,12 +31,7 @@ function requireJob(state: { store?: { jobs?: CronJob[] } | null }, id: string): return job; } -function requireRecord(value: unknown): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error("Expected a non-array record"); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-non-array-record"); function firstMockArg(mock: unknown): unknown { const calls = (mock as { mock: { calls: readonly (readonly unknown[])[] } }).mock.calls; diff --git a/src/cron/store.test.ts b/src/cron/store.test.ts index f2789e7205e9..eb0677f427c0 100644 --- a/src/cron/store.test.ts +++ b/src/cron/store.test.ts @@ -1,8 +1,9 @@ -// Cron store tests cover persisted scheduled job state and run metadata. import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { expectDefined } from "@openclaw/normalization-core"; +// Cron store tests cover persisted scheduled job state and run metadata. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { loadLegacyCronQuarantineForMigration } from "../commands/doctor/cron/legacy-quarantine-migration.js"; import { @@ -79,12 +80,7 @@ async function expectPathMissing(targetPath: string): Promise { throw new Error(`expected path to be missing: ${targetPath}`); } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label"); describe("resolveCronStorePath", () => { const envSnapshot = captureEnv(["OPENCLAW_HOME", "HOME"]); diff --git a/src/gateway/client.test.ts b/src/gateway/client.test.ts index e3b6c3062d99..a474f098aa54 100644 --- a/src/gateway/client.test.ts +++ b/src/gateway/client.test.ts @@ -1,7 +1,8 @@ -// Gateway client tests cover WebSocket protocol negotiation, auth persistence, -// proxy bypass setup, command dispatch, reconnect, and error handling. import { Buffer } from "node:buffer"; import { generateKeyPairSync } from "node:crypto"; +// Gateway client tests cover WebSocket protocol negotiation, auth persistence, +// proxy bypass setup, command dispatch, reconnect, and error handling. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { MIN_CLIENT_PROTOCOL_VERSION, @@ -222,12 +223,7 @@ function getLatestWs(): MockWebSocket { return ws; } -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null || Array.isArray(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); function expectRecordFields( value: unknown, diff --git a/src/gateway/server-chat.agent-events.test.ts b/src/gateway/server-chat.agent-events.test.ts index 591d5ab150b9..37511b5e0f69 100644 --- a/src/gateway/server-chat.agent-events.test.ts +++ b/src/gateway/server-chat.agent-events.test.ts @@ -2,6 +2,7 @@ // session lifecycle persistence, and subscriber registry behavior. import { expectDefined } from "@openclaw/normalization-core"; +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { INTERNAL_RUNTIME_CONTEXT_BEGIN, @@ -364,12 +365,7 @@ describe("agent event handler", () => { return call; } - function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null) { - throw new Error(`${label} was not an object`); - } - return value as Record; - } + const requireRecord = createRequireRecord("object", "label-not-object"); function expectRecordFields(record: Record, fields: Record) { for (const [key, value] of Object.entries(fields)) { diff --git a/src/gateway/server-cron-notifications.test.ts b/src/gateway/server-cron-notifications.test.ts index d32596aa71aa..f2bc7cad29f8 100644 --- a/src/gateway/server-cron-notifications.test.ts +++ b/src/gateway/server-cron-notifications.test.ts @@ -1,5 +1,6 @@ // Cron notification tests protect completion-delivery warning behavior, // including URL redaction for invalid webhook destinations. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { CliDeps } from "../cli/deps.types.js"; import { makeCronJob } from "../cron/delivery.test-helpers.js"; @@ -44,12 +45,7 @@ function waitForFast(assertion: () => void | Promise) { return vi.waitFor(assertion, { interval: 1 }); } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function webhookRequestBody() { const call = (mocks.fetchWithSsrFGuard.mock.calls as unknown[][])[0]; diff --git a/src/gateway/server-cron.test.ts b/src/gateway/server-cron.test.ts index 5ec19ec7d151..49e8ab8a7ea0 100644 --- a/src/gateway/server-cron.test.ts +++ b/src/gateway/server-cron.test.ts @@ -1,8 +1,9 @@ -// Gateway cron tests cover isolated agent turns, heartbeat wakeups, completion -// delivery, lifecycle cleanup, hook emission, and SSRF-guarded webhooks. import { createServer } from "node:http"; import os from "node:os"; import path from "node:path"; +// Gateway cron tests cover isolated agent turns, heartbeat wakeups, completion +// delivery, lifecycle cleanup, hook emission, and SSRF-guarded webhooks. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { AgentDeletionCommitUncertainError } from "../agents/agent-lifecycle-registry.js"; import type { CliDeps } from "../cli/deps.js"; @@ -255,12 +256,7 @@ function createCronConfig(name: string): OpenClawConfig { } as OpenClawConfig; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function callArg( mock: { mock: { calls: Array> } }, diff --git a/src/gateway/server-methods/cron.validation.test.ts b/src/gateway/server-methods/cron.validation.test.ts index c7e7d9677e07..a7029b021eb3 100644 --- a/src/gateway/server-methods/cron.validation.test.ts +++ b/src/gateway/server-methods/cron.validation.test.ts @@ -2,6 +2,7 @@ // prefixes/aliases and runtime config for cron delivery destinations. import { expectDefined } from "@openclaw/normalization-core"; +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { ChannelPlugin } from "../../channels/plugins/types.public.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; @@ -441,12 +442,7 @@ function expectCronReadSuccess(respond: ReturnType, job: CronJob): ); } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); function requireCronAddPayload( context: ReturnType, diff --git a/src/gateway/server-methods/models-auth-status.test.ts b/src/gateway/server-methods/models-auth-status.test.ts index 439423398f6a..c232abe979ea 100644 --- a/src/gateway/server-methods/models-auth-status.test.ts +++ b/src/gateway/server-methods/models-auth-status.test.ts @@ -2,6 +2,7 @@ // credential cleanup, secret refresh, and provider run abort side effects. import { expectDefined } from "@openclaw/normalization-core"; +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { AuthHealthSummary } from "../../agents/auth-health.js"; import type { AuthProfileStore } from "../../agents/auth-profiles.js"; @@ -193,12 +194,7 @@ function createLogoutOptions( } as unknown as GatewayRequestHandlerOptions & { respond: ReturnType }; } -function requireRecord(value: unknown): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error("Expected a non-array record"); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-non-array-record"); function firstRespondCall( opts: GatewayRequestHandlerOptions & { respond: ReturnType }, diff --git a/src/gateway/server-methods/plugin-approval.test.ts b/src/gateway/server-methods/plugin-approval.test.ts index aaca115e4c23..dcb3a42b75d1 100644 --- a/src/gateway/server-methods/plugin-approval.test.ts +++ b/src/gateway/server-methods/plugin-approval.test.ts @@ -2,6 +2,7 @@ // requester visibility, broadcast behavior, and approval manager integration. import { expectDefined } from "@openclaw/normalization-core"; +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { PluginApprovalRequestPayload } from "../../infra/plugin-approvals.js"; import { ExecApprovalManager } from "../exec-approval-manager.js"; @@ -84,12 +85,7 @@ type MockCallSource = { }; }; -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function requireArray(value: unknown, label: string): unknown[] { expect(Array.isArray(value), label).toBe(true); diff --git a/src/gateway/server-plugins.test.ts b/src/gateway/server-plugins.test.ts index 3fb5c8371b95..2f1be2237700 100644 --- a/src/gateway/server-plugins.test.ts +++ b/src/gateway/server-plugins.test.ts @@ -1,5 +1,6 @@ // Gateway plugin tests cover plugin loading, auto-enable, runtime registry setup, // request-scope injection, diagnostics, and handler dispatch integration. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest"; import { getGlobalPluginRegistry, @@ -206,12 +207,7 @@ function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } -function requireRecord(value: unknown, label: string): Record { - if (!isRecord(value)) { - throw new Error(`Expected ${label} to be an object`); - } - return value; -} +const requireRecord = createRequireRecord("record", "expected-label-object-capitalized"); function getLastMockFirstArg( mock: { mock: { calls: ReadonlyArray> } }, diff --git a/src/gateway/session-message-events.test.ts b/src/gateway/session-message-events.test.ts index 3631eaf688ee..aa56f2e5a5fb 100644 --- a/src/gateway/session-message-events.test.ts +++ b/src/gateway/session-message-events.test.ts @@ -1,9 +1,10 @@ -/** - * Session message event indexing and broadcast tests. - */ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; +/** + * Session message event indexing and broadcast tests. + */ +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterAll, afterEach, beforeAll, describe, expect, test, vi } from "vitest"; import type { RawData } from "ws"; import { @@ -164,12 +165,7 @@ async function expectNoMessageWithin(params: { await expect(received).resolves.toBe(false); } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label} to be an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label-object"); function expectRecordFields(value: unknown, expected: Record): void { const record = requireRecord(value, "record"); diff --git a/src/gateway/session-utils.fs.test.ts b/src/gateway/session-utils.fs.test.ts index e7f1707dc23a..cda0acbeab4d 100644 --- a/src/gateway/session-utils.fs.test.ts +++ b/src/gateway/session-utils.fs.test.ts @@ -1,9 +1,10 @@ -// Session filesystem utility tests cover transcript reading, usage extraction, -// preview rows, message counts, title fields, and archive candidate resolution. import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { SessionManager } from "openclaw/plugin-sdk/agent-sessions"; +// Session filesystem utility tests cover transcript reading, usage extraction, +// preview rows, message counts, title fields, and archive candidate resolution. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterAll, beforeAll, describe, expect, test, vi } from "vitest"; import { createNoisyPngBuffer } from "../../test/helpers/image-fixtures.js"; import { @@ -221,12 +222,7 @@ function appendBlockedUserMessage( return messageId; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function expectMessageContents(messages: unknown[], expected: unknown[]) { expect(messages.map((message) => requireRecord(message, "message").content)).toEqual(expected); diff --git a/src/gateway/talk-handoff.test.ts b/src/gateway/talk-handoff.test.ts index 42262cf98750..1df4f3582eb0 100644 --- a/src/gateway/talk-handoff.test.ts +++ b/src/gateway/talk-handoff.test.ts @@ -1,6 +1,7 @@ /** * Tests talk handoff coordination between gateway sessions and realtime state. */ +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { describe, expect, it, vi } from "vitest"; import { cancelTalkHandoffTurn, @@ -12,12 +13,7 @@ import { startTalkHandoffTurn, } from "./talk-handoff.js"; -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`Expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-capitalized"); function requireArray(value: unknown, label: string): unknown[] { if (!Array.isArray(value)) { diff --git a/src/infra/approval-native-runtime.test.ts b/src/infra/approval-native-runtime.test.ts index 442bac22439e..3d7bf123ff2f 100644 --- a/src/infra/approval-native-runtime.test.ts +++ b/src/infra/approval-native-runtime.test.ts @@ -1,4 +1,5 @@ // Covers native approval runtime delivery and resolution. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, describe, expect, it, vi } from "vitest"; import type { ChannelApprovalNativeAdapter } from "../channels/plugins/types.adapters.js"; import { @@ -62,12 +63,7 @@ afterEach(async () => { vi.useRealTimers(); }); -function requireRecord(value: unknown): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error("Expected a non-array record"); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-non-array-record"); function mockCallArg(mock: ReturnType, index = 0): Record { const arg = mock.mock.calls[index]?.[0]; diff --git a/src/infra/device-pairing.test.ts b/src/infra/device-pairing.test.ts index 430250cb9e3d..28fcb557d497 100644 --- a/src/infra/device-pairing.test.ts +++ b/src/infra/device-pairing.test.ts @@ -1,4 +1,5 @@ // Covers device pairing, token, and role lifecycle behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterAll, beforeAll, beforeEach, describe, expect, test } from "vitest"; import { FULL_ACCESS_PAIRING_SETUP_BOOTSTRAP_PROFILE, @@ -117,16 +118,7 @@ function requireValue(value: T | null | undefined, message: string): T { return value; } -function isRecord(value: unknown): value is Record { - return typeof value === "object" && value !== null && !Array.isArray(value); -} - -function requireRecord(value: unknown, message: string): Record { - if (!isRecord(value)) { - throw new Error(message); - } - return value; -} +const requireRecord = createRequireRecord("record", "message"); function expectRecordFields( value: unknown, diff --git a/src/infra/exec-approval-forwarder.test.ts b/src/infra/exec-approval-forwarder.test.ts index 752672d95d31..026ebd71446c 100644 --- a/src/infra/exec-approval-forwarder.test.ts +++ b/src/infra/exec-approval-forwarder.test.ts @@ -1,5 +1,6 @@ -// Covers exec approval forwarding to channel plugins. import { expectDefined } from "@openclaw/normalization-core"; +// Covers exec approval forwarding to channel plugins. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { ReplyPayload } from "../auto-reply/types.js"; import type { ChannelPlugin } from "../channels/plugins/types.public.js"; @@ -215,12 +216,7 @@ function getFirstDeliveryText(deliver: ReturnType): string { return firstCall.payloads?.[0]?.text ?? ""; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label} to be an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label-object"); function requireFirstCallArg( mock: ReturnType, diff --git a/src/infra/gateway-watch-tmux.test.ts b/src/infra/gateway-watch-tmux.test.ts index 6380990af528..9b59d6481a57 100644 --- a/src/infra/gateway-watch-tmux.test.ts +++ b/src/infra/gateway-watch-tmux.test.ts @@ -1,4 +1,5 @@ // Covers gateway watch tmux script helpers. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { describe, expect, it, vi } from "vitest"; import { buildGatewayWatchTmuxCommand, @@ -19,12 +20,7 @@ const createOutput = () => { }; }; -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function spawnCall(mock: unknown, callIndex: number) { const calls = (mock as { mock?: { calls?: Array> } }).mock?.calls ?? []; diff --git a/src/infra/heartbeat-runner.returns-default-unset.test.ts b/src/infra/heartbeat-runner.returns-default-unset.test.ts index 7434857afd7c..f25e753a2974 100644 --- a/src/infra/heartbeat-runner.returns-default-unset.test.ts +++ b/src/infra/heartbeat-runner.returns-default-unset.test.ts @@ -1,7 +1,8 @@ -// Tests heartbeat runner behavior when defaults are unset. import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; +// Tests heartbeat runner behavior when defaults are unset. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { HEARTBEAT_PROMPT } from "../auto-reply/heartbeat.js"; import type { ChannelOutboundAdapter } from "../channels/plugins/types.public.js"; @@ -173,12 +174,7 @@ const createCaseDir = async (prefix: string) => { return dir; }; -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be a record`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-record"); function expectRecordFields(record: Record, fields: Record) { for (const [key, value] of Object.entries(fields)) { diff --git a/src/infra/net/fetch-guard.ssrf.test.ts b/src/infra/net/fetch-guard.ssrf.test.ts index 2a01e59db082..22a57b90ec8f 100644 --- a/src/infra/net/fetch-guard.ssrf.test.ts +++ b/src/infra/net/fetch-guard.ssrf.test.ts @@ -1,5 +1,6 @@ // Guarded fetch SSRF tests cover redirect hardening, pinned dispatcher setup, // trusted proxy modes, and safe header retention. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { fetchConfiguredLocalOriginWithSsrFGuard, @@ -120,12 +121,7 @@ function getSecondRequestHeaders(fetchImpl: ReturnType): Headers { return new Headers(secondInit.headers); } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label"); function getFirstRequestInit(fetchImpl: ReturnType): RequestInit { const [call] = fetchImpl.mock.calls; diff --git a/src/infra/net/ssrf.dispatcher.test.ts b/src/infra/net/ssrf.dispatcher.test.ts index d77f63b7f2f3..e0ea889798d2 100644 --- a/src/infra/net/ssrf.dispatcher.test.ts +++ b/src/infra/net/ssrf.dispatcher.test.ts @@ -1,5 +1,6 @@ // Pinned dispatcher tests cover undici family policy, pinned lookup injection, // timeout propagation, and proxy dispatcher construction. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const TEST_UNDICI_RUNTIME_DEPS_KEY = "__OPENCLAW_TEST_UNDICI_RUNTIME_DEPS__"; @@ -80,12 +81,7 @@ function createDispatcherWithPinnedOverride(lookup: PinnedHostname["lookup"]) { return (call?.[0] as { connect?: { lookup?: PinnedHostname["lookup"] } })?.connect?.lookup; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label"); function requireFirstAgentOptions(): Record { const [call] = agentCtor.mock.calls; diff --git a/src/infra/node-pairing.test.ts b/src/infra/node-pairing.test.ts index 50957776d4d2..d41dc3ba7dc4 100644 --- a/src/infra/node-pairing.test.ts +++ b/src/infra/node-pairing.test.ts @@ -1,4 +1,5 @@ // Tests node capability-surface approvals stored on paired device records. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterAll, beforeAll, describe, expect, test } from "vitest"; import { createSuiteTempRootTracker } from "../test-helpers/temp-dir.js"; import { createDeferred } from "../test-utils/deferred.js"; @@ -61,12 +62,7 @@ async function findPairedNode(nodeId: string, baseDir: string) { return pairing.paired.find((node) => node.nodeId === nodeId) ?? null; } -function requireRecord(value: unknown): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error("Expected a non-array record"); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-non-array-record"); function findRecordByField>( records: T[], diff --git a/src/infra/outbound/delivery-queue.reconnect-drain.test.ts b/src/infra/outbound/delivery-queue.reconnect-drain.test.ts index 88f4159dc659..d8cc84680077 100644 --- a/src/infra/outbound/delivery-queue.reconnect-drain.test.ts +++ b/src/infra/outbound/delivery-queue.reconnect-drain.test.ts @@ -1,5 +1,6 @@ // Covers reconnect-triggered queue drain selection, active claims, backoff // bypass, and concurrent drain suppression. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { controlNextRecoverySleep } from "../../../test/helpers/infra/delivery-recovery.js"; import type { OpenClawConfig } from "../../config/config.js"; @@ -53,12 +54,7 @@ function countMatching(items: readonly T[], predicate: (item: T) => boolean): return count; } -function requireRecord(value: unknown): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error("Expected a non-array record"); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-non-array-record"); function firstMockArg( mock: { mock: { calls: readonly unknown[][] } }, diff --git a/src/infra/outbound/message-action-runner.media.test.ts b/src/infra/outbound/message-action-runner.media.test.ts index 86389ac366e7..e1afec2d52fc 100644 --- a/src/infra/outbound/message-action-runner.media.test.ts +++ b/src/infra/outbound/message-action-runner.media.test.ts @@ -1,8 +1,9 @@ -// Covers message-action media hydration, sandbox path normalization, -// attachments, and channel/plugin media source aliases. import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; +// Covers message-action media hydration, sandbox path normalization, +// attachments, and channel/plugin media source aliases. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { Type } from "typebox"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { jsonResult } from "../../agents/tools/common.js"; @@ -115,12 +116,7 @@ const runDrySend = (params: { sandboxRoot: params.sandboxRoot, }); -function requireRecord(value: unknown): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error("Expected a non-array record"); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-non-array-record"); function requireActionPayload( result: Awaited>, diff --git a/src/infra/outbound/message.test.ts b/src/infra/outbound/message.test.ts index 948b2b06d357..7daba2c53c3f 100644 --- a/src/infra/outbound/message.test.ts +++ b/src/infra/outbound/message.test.ts @@ -1,5 +1,6 @@ // Covers outbound message send/poll orchestration, target resolution, durable // capability checks, gateway fallback, dry runs, and payload planning. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const mocks = vi.hoisted(() => ({ @@ -74,12 +75,7 @@ beforeAll(async () => { ({ sendMessage } = await import("./message.js")); }); -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null || Array.isArray(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); function expectRecordFields( value: unknown, diff --git a/src/infra/outbound/outbound-send-service.test.ts b/src/infra/outbound/outbound-send-service.test.ts index 362159fe1b3e..7e4152941c7b 100644 --- a/src/infra/outbound/outbound-send-service.test.ts +++ b/src/infra/outbound/outbound-send-service.test.ts @@ -1,5 +1,6 @@ // Covers outbound send service plugin/core routing, media access scoping, // transcript mirroring, and poll fallback. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { ReplyPayload } from "../../auto-reply/reply-payload.js"; import type { ChannelPlugin } from "../../channels/plugins/types.public.js"; @@ -120,12 +121,7 @@ type MockCalls = { mock: { calls: unknown[][] }; }; -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function requireArray(value: unknown, label: string): unknown[] { expect(Array.isArray(value), label).toBe(true); diff --git a/src/infra/outbound/session-binding-service.test.ts b/src/infra/outbound/session-binding-service.test.ts index bcdacd0c9641..1f5abb138161 100644 --- a/src/infra/outbound/session-binding-service.test.ts +++ b/src/infra/outbound/session-binding-service.test.ts @@ -1,5 +1,6 @@ // Covers session binding adapter registration, generic current-conversation // fallback, capability errors, deduping, and duplicate graph teardown. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { setActivePluginRegistry } from "../../plugins/runtime.js"; import { closeOpenClawStateDatabaseForTest } from "../../state/openclaw-state-db.js"; @@ -79,12 +80,7 @@ function createRecord(input: SessionBindingBindInput): SessionBindingRecord { }; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be a record`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-record"); function firstMockArg( mock: { mock: { calls: readonly unknown[][] } }, diff --git a/src/infra/push-apns.test.ts b/src/infra/push-apns.test.ts index d8b05d8e409a..f53949721c04 100644 --- a/src/infra/push-apns.test.ts +++ b/src/infra/push-apns.test.ts @@ -1,10 +1,11 @@ -// Tests APNS push signing and request construction. import { generateKeyPairSync } from "node:crypto"; import { EventEmitter } from "node:events"; import { createServer, type Server as HttpServer } from "node:http"; import http2 from "node:http2"; import net from "node:net"; import { MAX_TIMER_TIMEOUT_MS } from "@openclaw/normalization-core/number-coercion"; +// Tests APNS push signing and request construction. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, describe, expect, it, vi } from "vitest"; import { createDeferred } from "../test-utils/deferred.js"; import { startProxy, stopProxy, type ProxyHandle } from "./net/proxy/proxy-lifecycle.js"; @@ -177,12 +178,7 @@ async function closeServer(server: HttpServer | http2.Http2SecureServer): Promis }); } -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null) { - throw new Error(`${label} was not an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "label-not-object"); function expectRecordFields(record: Record, fields: Record) { for (const [key, value] of Object.entries(fields)) { diff --git a/src/infra/watch-node.test.ts b/src/infra/watch-node.test.ts index 23944dbe04d2..3ee47dda3129 100644 --- a/src/infra/watch-node.test.ts +++ b/src/infra/watch-node.test.ts @@ -4,7 +4,7 @@ import { EventEmitter } from "node:events"; import fs from "node:fs"; import path from "node:path"; import { expectDefined } from "@openclaw/normalization-core"; -import { bundledPluginFile } from "openclaw/plugin-sdk/test-fixtures"; +import { createRequireRecord, bundledPluginFile } from "openclaw/plugin-sdk/test-fixtures"; import { describe, expect, it, vi } from "vitest"; import { runNodeWatchedPaths } from "../../scripts/run-node.mjs"; import { runWatchMain } from "../../scripts/watch-node.mjs"; @@ -95,12 +95,7 @@ const startWatchRun = ({ return { watcher, createWatcher, fakeProcess, runPromise }; }; -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label} to be an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label-object"); function requireMockCall(mock: ReturnType, callIndex: number): unknown[] { const call = mock.mock.calls[callIndex] as unknown[] | undefined; diff --git a/src/logging/diagnostic.test.ts b/src/logging/diagnostic.test.ts index ee9c627c7a2c..ebed841b6260 100644 --- a/src/logging/diagnostic.test.ts +++ b/src/logging/diagnostic.test.ts @@ -1,6 +1,6 @@ // Diagnostic logger tests cover event emission, metrics, and support output. import fs from "node:fs"; -import { importFreshModule } from "openclaw/plugin-sdk/test-fixtures"; +import { createRequireRecord, importFreshModule } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { appendTranscriptMessageSync, @@ -94,12 +94,7 @@ function countMatching(items: readonly T[], predicate: (item: T) => boolean) return count; } -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null) { - throw new Error(`${label} was not an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "label-not-object"); function expectRecordFields(record: Record, fields: Record) { for (const [key, value] of Object.entries(fields)) { diff --git a/src/media-understanding/image.test.ts b/src/media-understanding/image.test.ts index 094c853ce855..1f03f6ba9496 100644 --- a/src/media-understanding/image.test.ts +++ b/src/media-understanding/image.test.ts @@ -1,6 +1,7 @@ +import path from "node:path"; // Image runtime tests cover model-backed image routing, auth/profile handling, // provider payload transforms, and MiniMax/Copilot special paths. -import path from "node:path"; +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { attachModelProviderRequestTransport } from "../agents/provider-request-config.js"; import { mintSecretSentinel } from "../secrets/sentinel.js"; @@ -97,12 +98,7 @@ function requireFirstMockCall( return requireMockCallAt(mock, 0, label); } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`Expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-capitalized"); vi.mock("../llm/stream.js", async () => { const actual = await vi.importActual("../llm/stream.js"); diff --git a/src/plugin-sdk/provider-http-test-mocks.ts b/src/plugin-sdk/provider-http-test-mocks.ts index ba1c497bdf5b..2fdfe338352a 100644 --- a/src/plugin-sdk/provider-http-test-mocks.ts +++ b/src/plugin-sdk/provider-http-test-mocks.ts @@ -5,3 +5,10 @@ export { getProviderHttpMocks, installProviderHttpMockCleanup, } from "./test-helpers/provider-http-mocks.js"; +export { + bufferedOversizedJsonResponse, + oversizedJsonResponse, + requireFirstPostJsonRecordRequest, + requireFirstPostJsonRequest, + streamedJsonResponse, +} from "../../test/helpers/provider-http.js"; diff --git a/src/plugin-sdk/provider-stream-shared.test.ts b/src/plugin-sdk/provider-stream-shared.test.ts index 44eb341758cf..69a65c1a41d6 100644 --- a/src/plugin-sdk/provider-stream-shared.test.ts +++ b/src/plugin-sdk/provider-stream-shared.test.ts @@ -1,8 +1,9 @@ +import type { StreamFn } from "openclaw/plugin-sdk/agent-core"; +import type { Model } from "openclaw/plugin-sdk/llm"; /** * Tests provider stream shared helpers and stream hook capture. */ -import type { StreamFn } from "openclaw/plugin-sdk/agent-core"; -import type { Model } from "openclaw/plugin-sdk/llm"; +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { describe, expect, it } from "vitest"; import { createAssistantMessageEventStream } from "../llm/utils/event-stream.js"; import { @@ -77,12 +78,7 @@ const lmstudioBareModel = { reasoning: true, } as unknown as Model<"openai-completions">; -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be a record`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-record"); function messageOf(event: unknown) { return requireRecord(requireRecord(event, "done event").message, "done message"); diff --git a/src/plugin-sdk/provider-stream.test.ts b/src/plugin-sdk/provider-stream.test.ts index a169ff32998f..e43b98b27774 100644 --- a/src/plugin-sdk/provider-stream.test.ts +++ b/src/plugin-sdk/provider-stream.test.ts @@ -1,5 +1,6 @@ -// Provider stream tests cover shared stream-wrapper families and payload compatibility. import type { StreamFn } from "openclaw/plugin-sdk/agent-core"; +// Provider stream tests cover shared stream-wrapper families and payload compatibility. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { describe, expect, it } from "vitest"; import { createAssistantMessageEventStream } from "../llm/utils/event-stream.js"; import { VERSION } from "../version.js"; @@ -42,12 +43,7 @@ function requireStreamFn(streamFn: StreamFn | null | undefined) { return streamFn; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); function requirePayload(payload: Record | undefined): Record { if (!payload) { diff --git a/src/plugin-sdk/test-fixtures.ts b/src/plugin-sdk/test-fixtures.ts index 65f59b7e757f..8832de645ab3 100644 --- a/src/plugin-sdk/test-fixtures.ts +++ b/src/plugin-sdk/test-fixtures.ts @@ -25,6 +25,15 @@ export { sanitizeTerminalText } from "../../packages/terminal-core/src/safe-text export { countLines, hasBalancedFences } from "../test-utils/chunk-test-helpers.js"; export { expectGeneratedTokenPersistedToGatewayAuth } from "../test-utils/auth-token-assertions.js"; export { typedCases } from "../test-utils/typed-cases.js"; +export { createRequireRecord } from "../../test/helpers/record.js"; +export type { RecordRequirementKind, RecordRequirementMessage } from "../../test/helpers/record.js"; +export { + bufferedOversizedJsonResponse, + oversizedJsonResponse, + requireFirstPostJsonRecordRequest, + requireFirstPostJsonRequest, + streamedJsonResponse, +} from "../../test/helpers/provider-http.js"; export { BUNDLED_PLUGIN_PATH_PREFIX, BUNDLED_PLUGIN_ROOT_DIR, diff --git a/src/plugins/contracts/session-entry-projection.contract.test.ts b/src/plugins/contracts/session-entry-projection.contract.test.ts index 5d4f75ca1c71..e439eb0e3490 100644 --- a/src/plugins/contracts/session-entry-projection.contract.test.ts +++ b/src/plugins/contracts/session-entry-projection.contract.test.ts @@ -1,10 +1,11 @@ -// Session entry projection contract tests cover plugin session entry projection behavior. import fs from "node:fs/promises"; import path from "node:path"; import { createPluginRegistryFixture, registerTestPlugin, } from "openclaw/plugin-sdk/plugin-test-contracts"; +// Session entry projection contract tests cover plugin session entry projection behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import type { SessionEntry } from "../../config/sessions.js"; import { listSessionEntries, replaceSessionEntry } from "../../config/sessions/session-accessor.js"; @@ -20,12 +21,7 @@ import { setActivePluginRegistry } from "../runtime.js"; import { createPluginRecord } from "../status.test-fixtures.js"; import { runTrustedToolPolicies } from "../trusted-tool-policy.js"; -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); async function expectOkResult(promise: Promise, label: string) { const result = requireRecord(await promise, label); diff --git a/src/plugins/install.test.ts b/src/plugins/install.test.ts index f4f38dc39b1b..21238bded534 100644 --- a/src/plugins/install.test.ts +++ b/src/plugins/install.test.ts @@ -1,7 +1,8 @@ -// Covers plugin install flows, manifests, and install records. import fs from "node:fs"; import fsPromises from "node:fs/promises"; import path from "node:path"; +// Covers plugin install flows, manifests, and install records. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { @@ -642,12 +643,7 @@ function expectWarningExcludes(warnings: readonly string[], fragment: string) { expect(warnings.join("\n")).not.toContain(fragment); } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); function firstMockCall(mock: { mock: { calls: unknown[][] } }): unknown[] | undefined { return mock.mock.calls[0]; diff --git a/src/plugins/installed-plugin-index.test.ts b/src/plugins/installed-plugin-index.test.ts index c0fa2fdcae56..0ca2e25fa617 100644 --- a/src/plugins/installed-plugin-index.test.ts +++ b/src/plugins/installed-plugin-index.test.ts @@ -1,6 +1,7 @@ -// Covers installed plugin index read, write, and policy behavior. import fs from "node:fs"; import path from "node:path"; +// Covers installed plugin index read, write, and policy behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, describe, expect, it, vi } from "vitest"; import type { PluginCandidate } from "./discovery.js"; import { buildInstalledPluginIndexRecords } from "./installed-plugin-index-record-builder.js"; @@ -51,12 +52,7 @@ function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } -function requireRecord(value: unknown, label: string): Record { - if (!isRecord(value)) { - throw new Error(`Expected ${label} to be an object`); - } - return value; -} +const requireRecord = createRequireRecord("record", "expected-label-object-capitalized"); function readRecordField(record: Record, key: string, label: string) { const value = record[key]; diff --git a/src/plugins/plugin-module-loader-cache.test.ts b/src/plugins/plugin-module-loader-cache.test.ts index 3c2f32131ef5..a626a800bd7a 100644 --- a/src/plugins/plugin-module-loader-cache.test.ts +++ b/src/plugins/plugin-module-loader-cache.test.ts @@ -2,7 +2,7 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; -import { importFreshModule } from "openclaw/plugin-sdk/test-fixtures"; +import { createRequireRecord, importFreshModule } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeAll, describe, expect, it, vi } from "vitest"; import type { PluginModuleLoaderFactory } from "./plugin-module-loader-cache.js"; @@ -38,12 +38,7 @@ function asPluginModuleLoaderFactory(factory: unknown): PluginModuleLoaderFactor return factory as PluginModuleLoaderFactory; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function callArg(mock: unknown, callIndex: number, argIndex: number, label: string) { const calls = (mock as { mock?: { calls?: Array> } }).mock?.calls ?? []; diff --git a/src/plugins/plugin-registry.test.ts b/src/plugins/plugin-registry.test.ts index ae80a4a7e14d..fa70f204fa98 100644 --- a/src/plugins/plugin-registry.test.ts +++ b/src/plugins/plugin-registry.test.ts @@ -1,8 +1,9 @@ -// Covers plugin registry assembly, contribution lookup, and reset behavior. import crypto from "node:crypto"; import fs from "node:fs"; import path from "node:path"; import { expectDefined } from "@openclaw/normalization-core"; +// Covers plugin registry assembly, contribution lookup, and reset behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, describe, expect, it, vi } from "vitest"; import { closeOpenClawStateDatabaseForTest } from "../state/openclaw-state-db.js"; import type { PluginCandidate } from "./discovery.js"; @@ -165,12 +166,7 @@ function createIndex( }; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function requireArray(value: unknown, label: string): Array { expect(Array.isArray(value), label).toBe(true); diff --git a/src/plugins/provider-runtime.test.ts b/src/plugins/provider-runtime.test.ts index ae70f10f02dd..44795f9fe4a1 100644 --- a/src/plugins/provider-runtime.test.ts +++ b/src/plugins/provider-runtime.test.ts @@ -1,5 +1,6 @@ -/** Exercises provider runtime loading, ordering, and manifest-backed discovery paths. */ import type { AgentMessage } from "openclaw/plugin-sdk/agent-core"; +/** Exercises provider runtime loading, ordering, and manifest-backed discovery paths. */ +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { ModelProviderConfig, OpenClawConfig } from "../config/types.js"; import { captureEnv, deleteTestEnvValue, setTestEnvValue } from "../test-utils/env.js"; @@ -164,12 +165,7 @@ function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } -function requireRecord(value: unknown, label: string): Record { - if (!isRecord(value)) { - throw new Error(`Expected ${label} to be an object`); - } - return value; -} +const requireRecord = createRequireRecord("record", "expected-label-object-capitalized"); function firstMockArg(mock: { mock: { calls: unknown[][] } }): unknown { return mock.mock.calls[0]?.[0]; diff --git a/src/plugins/runtime/runtime-llm.runtime.test.ts b/src/plugins/runtime/runtime-llm.runtime.test.ts index e836d9669123..47aee49a10f6 100644 --- a/src/plugins/runtime/runtime-llm.runtime.test.ts +++ b/src/plugins/runtime/runtime-llm.runtime.test.ts @@ -1,4 +1,5 @@ // Runtime LLM tests cover plugin provider hooks inside the model runtime adapter. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { resolveContextEngineCapabilities } from "../../agents/embedded-agent-runner/context-engine-capabilities.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; @@ -65,12 +66,7 @@ type MockCalls = { mock: { calls: unknown[][] }; }; -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function requireArray(value: unknown, label: string): unknown[] { expect(Array.isArray(value), label).toBe(true); diff --git a/src/plugins/runtime/runtime-tasks.test.ts b/src/plugins/runtime/runtime-tasks.test.ts index 6511ae828ea3..27d2236b0f1b 100644 --- a/src/plugins/runtime/runtime-tasks.test.ts +++ b/src/plugins/runtime/runtime-tasks.test.ts @@ -1,4 +1,5 @@ // Runtime task tests cover plugin task runtime registration, invocation, and cleanup. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { getDetachedTaskLifecycleRuntime } from "../../tasks/detached-task-runtime.js"; import { setDetachedTaskLifecycleRuntime } from "../../tasks/task-runtime.test-helpers.js"; @@ -16,12 +17,7 @@ afterEach(() => { resetRuntimeTaskTestState(); }); -function requireRecord(value: unknown): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error("Expected a non-array record"); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-non-array-record"); function requireRecordById(items: readonly unknown[], id: string): Record { for (const item of items) { diff --git a/src/plugins/setup-registry.test.ts b/src/plugins/setup-registry.test.ts index cf710ba200a7..71e3c24fe638 100644 --- a/src/plugins/setup-registry.test.ts +++ b/src/plugins/setup-registry.test.ts @@ -1,7 +1,8 @@ -// Verifies plugin setup registry discovery and lookup behavior. import fs from "node:fs"; import path from "node:path"; import { pathToFileURL } from "node:url"; +// Verifies plugin setup registry discovery and lookup behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { withMockedWindowsPlatform } from "../test-utils/vitest-spies.js"; import { cleanupTrackedTempDirs, makeTrackedTempDir } from "./test-helpers/fs-fixtures.js"; @@ -171,12 +172,7 @@ async function expectNoUnhandledRejection(run: () => void | Promise): Prom expect(unhandledRejections).toStrictEqual([]); } -function requireRecord(value: unknown): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error("Expected a non-array record"); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-non-array-record"); function mockCall( mock: { mock: { calls: ReadonlyArray> } }, diff --git a/src/plugins/status.registry-snapshot.test.ts b/src/plugins/status.registry-snapshot.test.ts index acca22de5e5f..65b6a7697c4b 100644 --- a/src/plugins/status.registry-snapshot.test.ts +++ b/src/plugins/status.registry-snapshot.test.ts @@ -1,6 +1,7 @@ -// Covers plugin status snapshots built from registry state. import fs from "node:fs"; import path from "node:path"; +// Covers plugin status snapshots built from registry state. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, describe, expect, it } from "vitest"; import { readPersistedInstalledPluginIndex, @@ -42,12 +43,7 @@ afterEach(() => { cleanupTrackedTempDirs(tempDirs); }); -function requireRecord(value: unknown): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error("Expected a non-array record"); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-non-array-record"); function requirePlugin( plugins: readonly Record[], diff --git a/src/plugins/web-provider-runtime-shared.test.ts b/src/plugins/web-provider-runtime-shared.test.ts index b098569185e4..ed311ce64d71 100644 --- a/src/plugins/web-provider-runtime-shared.test.ts +++ b/src/plugins/web-provider-runtime-shared.test.ts @@ -1,4 +1,5 @@ // Covers shared web provider runtime helpers. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const mocks = vi.hoisted(() => ({ @@ -48,12 +49,7 @@ vi.mock("./runtime/load-context.js", () => ({ let resolvePluginWebProviders: typeof import("./web-provider-runtime-shared.js").resolvePluginWebProviders; let resolveRuntimeWebProviders: typeof import("./web-provider-runtime-shared.js").resolveRuntimeWebProviders; -function requireRecord(value: unknown): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error("Expected a non-array record"); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-non-array-record"); function mockArg(mock: ReturnType, callIndex = 0): Record { return requireRecord(mock.mock.calls[callIndex]?.[0]); diff --git a/src/plugins/web-search-providers.runtime.test.ts b/src/plugins/web-search-providers.runtime.test.ts index 8d7c16e38d74..1672b06a9f21 100644 --- a/src/plugins/web-search-providers.runtime.test.ts +++ b/src/plugins/web-search-providers.runtime.test.ts @@ -1,4 +1,5 @@ /** Covers runtime loading and sorting for plugin web search providers. */ +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; type RegistryModule = typeof import("./registry.js"); @@ -205,12 +206,7 @@ function expectLoaderCallCount(count: number) { expect(loadOpenClawPluginsMock).toHaveBeenCalledTimes(count); } -function requireRecord(value: unknown): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error("Expected a non-array record"); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-non-array-record"); function requireLastCallFirstArg( mock: { mock: { calls: readonly (readonly unknown[])[] } }, diff --git a/src/secrets/runtime-web-tools.test.ts b/src/secrets/runtime-web-tools.test.ts index b43fb23adab0..349cc9419f08 100644 --- a/src/secrets/runtime-web-tools.test.ts +++ b/src/secrets/runtime-web-tools.test.ts @@ -1,4 +1,5 @@ /** Tests web-tool secret metadata resolution from config and plugins. */ +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../config/config.js"; import type { @@ -340,12 +341,7 @@ function readProviderKey(config: OpenClawConfig, provider: ProviderUnderTest): u return pluginConfig?.webSearch?.apiKey; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function diagnostics(value: unknown) { expect(Array.isArray(value), "diagnostics").toBe(true); diff --git a/src/system-agent/operations.test.ts b/src/system-agent/operations.test.ts index 01a0f020b1db..ca293141a73b 100644 --- a/src/system-agent/operations.test.ts +++ b/src/system-agent/operations.test.ts @@ -1,6 +1,7 @@ -// OpenClaw operation tests cover rescue operation planning and execution. import fs from "node:fs/promises"; import path from "node:path"; +// OpenClaw operation tests cover rescue operation planning and execution. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { useAutoCleanupTempDirTracker } from "../../test/helpers/temp-dir.js"; import { resetPluginStateStoreForTests } from "../plugin-state/plugin-state-store.js"; @@ -18,12 +19,7 @@ import { createSystemAgentTestRuntime } from "./system-agent.runtime.test-suppor type TestConfig = Record; -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null) { - throw new Error(`${label} was not an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "label-not-object"); function expectRecordFields(record: Record, fields: Record) { for (const [key, value] of Object.entries(fields)) { diff --git a/src/web-search/runtime.test.ts b/src/web-search/runtime.test.ts index 7e92e9722818..5ec8ed25ec66 100644 --- a/src/web-search/runtime.test.ts +++ b/src/web-search/runtime.test.ts @@ -1,7 +1,8 @@ -// Web search runtime tests cover provider resolution and search execution. import { mkdtempSync, rmSync } from "node:fs"; import os from "node:os"; import path from "node:path"; +// Web search runtime tests cover provider resolution and search execution. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { getRuntimeAuthProfileStoreCredentialsRevision } from "../agents/auth-profiles/runtime-snapshots.js"; import { @@ -140,12 +141,7 @@ function createOAuthAuthProfileStore(params: { }; } -function requireRecord(value: unknown): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error("Expected a non-array record"); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-non-array-record"); function mockCallParam(mock: ReturnType, index = 0): Record { return requireRecord(mock.mock.calls[index]?.[0]); diff --git a/src/wizard/setup.test.ts b/src/wizard/setup.test.ts index 146d94e847c6..3c9201c90d3d 100644 --- a/src/wizard/setup.test.ts +++ b/src/wizard/setup.test.ts @@ -1,8 +1,9 @@ -// Setup wizard tests cover end-to-end onboarding prompt flows. import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import type { ProviderPlugin } from "openclaw/plugin-sdk/provider-model-shared"; +// Setup wizard tests cover end-to-end onboarding prompt flows. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { createWizardPrompter as buildWizardPrompter } from "../../test/helpers/wizard-prompter.js"; import { @@ -281,12 +282,7 @@ function persistedWizardConfigs(): OpenClawConfig[] { ); } -function requireRecord(value: unknown, label: string): Record { - if (typeof value !== "object" || value === null || Array.isArray(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); function expectRecordFields( value: unknown, diff --git a/test/e2e/qa-lab/runtime/gateway-plugin-approvals.e2e.test.ts b/test/e2e/qa-lab/runtime/gateway-plugin-approvals.e2e.test.ts index 7f8493b447e6..d10ef0570465 100644 --- a/test/e2e/qa-lab/runtime/gateway-plugin-approvals.e2e.test.ts +++ b/test/e2e/qa-lab/runtime/gateway-plugin-approvals.e2e.test.ts @@ -1,6 +1,6 @@ -// Proves the plugin approval lifecycle through authenticated Gateway WebSockets. import path from "node:path"; -import { isRecord } from "@openclaw/normalization-core/record-coerce"; +// Proves the plugin approval lifecycle through authenticated Gateway WebSockets. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, describe, expect, it, vi } from "vitest"; import { GATEWAY_CLIENT_CAPS } from "../../../../packages/gateway-protocol/src/client-info.js"; import { ADMIN_SCOPE, APPROVALS_SCOPE } from "../../../../src/gateway/method-scopes.js"; @@ -39,12 +39,7 @@ type ApprovalDecision = { terminalReason: string | null; }; -function requireRecord(value: unknown, label: string): Record { - if (!isRecord(value)) { - throw new Error(`expected ${label}`); - } - return value; -} +const requireRecord = createRequireRecord("record", "expected-label"); installGatewayTestHooks({ scope: "suite" }); @@ -186,7 +181,9 @@ describe("gateway plugin approvals QA", () => { .finally(() => { waitSettled = true; }); - await new Promise((resolve) => setTimeout(resolve, 25)); + await new Promise((resolve) => { + setTimeout(resolve, 25); + }); expect(waitSettled).toBe(false); markStage("reviewer resolve"); diff --git a/test/helpers/provider-http.test.ts b/test/helpers/provider-http.test.ts new file mode 100644 index 000000000000..a88f88c2eb4c --- /dev/null +++ b/test/helpers/provider-http.test.ts @@ -0,0 +1,31 @@ +import { describe, expect, it, vi } from "vitest"; +import { + oversizedJsonResponse, + requireFirstPostJsonRecordRequest, + streamedJsonResponse, +} from "./provider-http.js"; + +describe("provider HTTP fixtures", () => { + it("builds streamed JSON responses", async () => { + await expect(streamedJsonResponse({ ok: true }).json()).resolves.toEqual({ ok: true }); + }); + + it("tracks bounded oversized response reads and cancellation", async () => { + const fixture = oversizedJsonResponse({ chunkCount: 2, chunkSize: 4 }); + const reader = fixture.response.body?.getReader(); + await reader?.read(); + await reader?.cancel(); + + expect(fixture.getReadCount()).toBe(1); + expect(fixture.wasCanceled()).toBe(true); + }); + + it("requires the first request to be a record", () => { + const mock = vi.fn(); + mock({ url: "https://example.test" }); + + expect(requireFirstPostJsonRecordRequest(mock, "request")).toEqual({ + url: "https://example.test", + }); + }); +}); diff --git a/test/helpers/provider-http.ts b/test/helpers/provider-http.ts new file mode 100644 index 000000000000..ff7f65995b3c --- /dev/null +++ b/test/helpers/provider-http.ts @@ -0,0 +1,117 @@ +type MockCallSource = { + mock: { calls: readonly (readonly unknown[])[] }; +}; + +type TrackedOversizedJsonResponse = { + response: Response; + getReadCount: () => number; + wasCanceled: () => boolean; +}; + +type FixedOversizedJsonResponse = { + response: Response; + state: { canceled: boolean; enqueuedBytes: number }; +}; + +export function oversizedJsonResponse(): FixedOversizedJsonResponse; +export function oversizedJsonResponse(params: { + chunkCount: number; + chunkSize: number; +}): TrackedOversizedJsonResponse; +export function oversizedJsonResponse(params?: { + chunkCount: number; + chunkSize: number; +}): FixedOversizedJsonResponse | TrackedOversizedJsonResponse { + if (params) { + const chunk = new Uint8Array(params.chunkSize); + let readCount = 0; + let canceled = false; + return { + response: new Response( + new ReadableStream({ + pull(controller) { + if (readCount >= params.chunkCount) { + controller.close(); + return; + } + readCount += 1; + controller.enqueue(chunk); + }, + cancel() { + canceled = true; + }, + }), + { + status: 200, + headers: { "Content-Type": "application/json" }, + }, + ), + getReadCount: () => readCount, + wasCanceled: () => canceled, + }; + } + + // Cap the advertised stream at 64 MiB so a broken bounded reader fails + // instead of hanging, while still forcing cancellation past the 16 MiB limit. + const state = { canceled: false, enqueuedBytes: 0 }; + const chunk = 1024 * 1024; + const maxChunks = 64; + let emitted = 0; + const response = new Response( + new ReadableStream({ + pull(controller) { + if (emitted >= maxChunks) { + controller.close(); + return; + } + emitted += 1; + state.enqueuedBytes += chunk; + controller.enqueue(new Uint8Array(chunk)); + }, + cancel() { + state.canceled = true; + }, + }), + { headers: { "content-type": "application/json" } }, + ); + return { response, state }; +} + +export function bufferedOversizedJsonResponse(): Response { + return new Response(Buffer.alloc(16 * 1024 * 1024 + 1, 0x20), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); +} + +export function streamedJsonResponse(payload: unknown): Response { + // Keep a real body stream so bounded-reader tests exercise reads and cancellation. + return new Response( + new ReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode(JSON.stringify(payload))); + controller.close(); + }, + }), + { status: 200, headers: { "content-type": "application/json" } }, + ); +} + +export function requireFirstPostJsonRequest(mock: MockCallSource, label: string): unknown { + const [call] = mock.mock.calls; + if (!call) { + throw new Error(`expected ${label}`); + } + return call[0]; +} + +export function requireFirstPostJsonRecordRequest( + mock: MockCallSource, + label: string, +): Record { + const value = requireFirstPostJsonRequest(mock, label); + if (value === null || typeof value !== "object" || Array.isArray(value)) { + throw new Error(`expected ${label} to be a record`); + } + return value as Record; +} diff --git a/test/helpers/record.test.ts b/test/helpers/record.test.ts new file mode 100644 index 000000000000..74ac1d614d37 --- /dev/null +++ b/test/helpers/record.test.ts @@ -0,0 +1,18 @@ +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; +import { describe, expect, it } from "vitest"; + +describe("createRequireRecord", () => { + it("preserves object assertions that allow arrays", () => { + const requireRecord = createRequireRecord("object", "expected-label"); + + expect(requireRecord([], "payload")).toEqual([]); + expect(() => requireRecord(null, "payload")).toThrow("expected payload"); + }); + + it("preserves strict record assertions and diagnostics", () => { + const requireRecord = createRequireRecord("record", "expected-label-record"); + + expect(requireRecord({ ok: true }, "payload")).toEqual({ ok: true }); + expect(() => requireRecord([], "payload")).toThrow("expected payload to be a record"); + }); +}); diff --git a/test/helpers/record.ts b/test/helpers/record.ts new file mode 100644 index 000000000000..95f1fdb70a1b --- /dev/null +++ b/test/helpers/record.ts @@ -0,0 +1,66 @@ +export type RecordRequirementKind = "object" | "record"; + +export type RecordRequirementMessage = + | "expected-label" + | "expected-label-object" + | "expected-label-object-short" + | "expected-label-record" + | "expected-label-record-short" + | "expected-label-capitalized" + | "expected-label-object-capitalized" + | "expected-non-array-record" + | "expected-object-value" + | "expected-record" + | "label-not-object" + | "message"; + +type FixedRecordRequirementMessage = + | "expected-non-array-record" + | "expected-object-value" + | "expected-record"; +type LabeledRecordRequirementMessage = Exclude< + RecordRequirementMessage, + FixedRecordRequirementMessage +>; + +// Keyed registry keeps the message union exhaustively checked: adding a +// variant without a formatter is a compile error, and the return stays string. +const RECORD_REQUIREMENT_ERRORS = { + "expected-label": (label) => `expected ${label}`, + "expected-label-object": (label) => `expected ${label} to be an object`, + "expected-label-object-short": (label) => `expected ${label} object`, + "expected-label-record": (label) => `expected ${label} to be a record`, + "expected-label-record-short": (label) => `expected ${label} record`, + "expected-label-capitalized": (label) => `Expected ${label}`, + "expected-label-object-capitalized": (label) => `Expected ${label} to be an object`, + "expected-non-array-record": () => "Expected a non-array record", + "expected-object-value": () => "Expected object value", + "expected-record": () => "expected record", + "label-not-object": (label) => `${label} was not an object`, + message: (label) => label ?? "expected record", +} satisfies Record string>; + +function recordRequirementError(message: RecordRequirementMessage, label?: string): string { + return RECORD_REQUIREMENT_ERRORS[message](label); +} + +export function createRequireRecord( + kind: RecordRequirementKind, + message: FixedRecordRequirementMessage, +): (value: unknown) => Record; +export function createRequireRecord( + kind: RecordRequirementKind, + message: LabeledRecordRequirementMessage, +): (value: unknown, label: string) => Record; +export function createRequireRecord( + kind: RecordRequirementKind, + message: RecordRequirementMessage, +): (value: unknown, label?: string) => Record { + return (value: unknown, label?: string): Record => { + const isObject = value !== null && typeof value === "object"; + if (!isObject || (kind === "record" && Array.isArray(value))) { + throw new Error(recordRequirementError(message, label)); + } + return value as Record; + }; +} diff --git a/ui/src/lib/chat/commands.test.ts b/ui/src/lib/chat/commands.test.ts index f4d36aaf111d..2397e29863a7 100644 --- a/ui/src/lib/chat/commands.test.ts +++ b/ui/src/lib/chat/commands.test.ts @@ -1,5 +1,6 @@ +import { expectDefined } from "@openclaw/normalization-core"; // @vitest-environment node -import { expectDefined, isRecord } from "@openclaw/normalization-core"; +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, describe, expect, it } from "vitest"; import { buildFallbackSlashCommands, @@ -15,12 +16,7 @@ afterEach(() => { replaceSlashCommands(buildFallbackSlashCommands()); }); -function requireRecord(value: unknown, label: string): Record { - if (!isRecord(value)) { - throw new Error(`expected ${label} to be an object`); - } - return value; -} +const requireRecord = createRequireRecord("record", "expected-label-object"); function requireArray(value: unknown, label: string): unknown[] { if (!Array.isArray(value)) { diff --git a/ui/src/lib/cron/index.test.ts b/ui/src/lib/cron/index.test.ts index 5ad066cad535..488527c52036 100644 --- a/ui/src/lib/cron/index.test.ts +++ b/ui/src/lib/cron/index.test.ts @@ -1,5 +1,6 @@ // @vitest-environment node // Control UI tests cover cron behavior. +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { describe, expect, it, vi } from "vitest"; import { validateCronAddParams, @@ -131,12 +132,7 @@ function createCronEditHarness(job: CronJob) { return { state, submit }; } -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error(`expected ${label} to be a record`); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-label-record"); function expectRecordFields(record: Record, fields: Record) { for (const [key, value] of Object.entries(fields)) { diff --git a/ui/src/pages/chat/chat-gateway.test.ts b/ui/src/pages/chat/chat-gateway.test.ts index 5e8c9fb8800f..e83a6cc8198b 100644 --- a/ui/src/pages/chat/chat-gateway.test.ts +++ b/ui/src/pages/chat/chat-gateway.test.ts @@ -1,6 +1,7 @@ +import { reduceSessionProjection } from "@openclaw/gateway-client/browser"; // @vitest-environment node // Control UI tests cover chat behavior. -import { reduceSessionProjection } from "@openclaw/gateway-client/browser"; +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { describe, expect, it, vi } from "vitest"; import { GatewayRequestError } from "../../api/gateway.ts"; import { handleChatGatewayEvent, type ChatEventPayload } from "./chat-gateway.ts"; @@ -51,12 +52,7 @@ function createDeferred() { return { promise, resolve, reject }; } -function requireRecord(value: unknown): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error("Expected a non-array record"); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-non-array-record"); function expectTextChatMessage(message: unknown, role: string, text: string): void { const record = requireRecord(message); diff --git a/ui/src/pages/chat/chat-send.test.ts b/ui/src/pages/chat/chat-send.test.ts index dda1d98f93ec..36afa17f070e 100644 --- a/ui/src/pages/chat/chat-send.test.ts +++ b/ui/src/pages/chat/chat-send.test.ts @@ -2,6 +2,7 @@ import { reduceSessionProjection } from "@openclaw/gateway-client/browser"; import { expectDefined } from "@openclaw/normalization-core"; +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { GatewayRequestError } from "../../api/gateway.ts"; import type { GatewaySessionRow, SessionsListResult } from "../../api/types.ts"; @@ -241,12 +242,7 @@ type MockCallSource = { }; }; -function requireRecord(value: unknown, label: string): Record { - if (!value || typeof value !== "object") { - throw new Error(`expected ${label}`); - } - return value as Record; -} +const requireRecord = createRequireRecord("object", "expected-label"); function mockArg(source: MockCallSource, callIndex: number, argIndex: number, label: string) { const call = source.mock.calls[callIndex]; diff --git a/ui/src/pages/chat/chat-thread.test.ts b/ui/src/pages/chat/chat-thread.test.ts index 71646db92eba..ab277d31aec8 100644 --- a/ui/src/pages/chat/chat-thread.test.ts +++ b/ui/src/pages/chat/chat-thread.test.ts @@ -1,6 +1,7 @@ +import { expectDefined } from "@openclaw/normalization-core"; // @vitest-environment node // Control UI tests cover build chat items behavior. -import { expectDefined } from "@openclaw/normalization-core"; +import { createRequireRecord } from "openclaw/plugin-sdk/test-fixtures"; import { describe, expect, it, vi } from "vitest"; import { markInboundContextLabel } from "../../../../src/auto-reply/reply/inbound-context-marker.js"; import type { MessageGroup } from "../../lib/chat/chat-types.ts"; @@ -94,12 +95,7 @@ function firstMessageContent(group: MessageGroup): unknown[] { return Array.isArray(message.content) ? message.content : []; } -function requireRecord(value: unknown): Record { - if (!value || typeof value !== "object" || Array.isArray(value)) { - throw new Error("Expected a non-array record"); - } - return value as Record; -} +const requireRecord = createRequireRecord("record", "expected-non-array-record"); function requireGroup(value: unknown): MessageGroup { const record = requireRecord(value);