mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-05 00:50:21 +00:00
fix: stabilize ci and serial test gate
This commit is contained in:
@@ -33,6 +33,7 @@ vi.mock("../progress.js", () => ({
|
||||
|
||||
describe("exec approval transport timeout (#12098)", () => {
|
||||
let callGatewayCli: typeof import("./rpc.js").callGatewayCli;
|
||||
const approvalTransportFloorMs = DEFAULT_EXEC_APPROVAL_TIMEOUT_MS + 10_000;
|
||||
|
||||
beforeAll(async () => {
|
||||
({ callGatewayCli } = await import("./rpc.js"));
|
||||
@@ -55,10 +56,10 @@ describe("exec approval transport timeout (#12098)", () => {
|
||||
});
|
||||
|
||||
it("fix: overriding transportTimeoutMs gives the approval enough transport time", async () => {
|
||||
const approvalTimeoutMs = 120_000;
|
||||
const approvalTimeoutMs = DEFAULT_EXEC_APPROVAL_TIMEOUT_MS;
|
||||
// Mirror the production code: parseTimeoutMs(opts.timeout) ?? 0
|
||||
const transportTimeoutMs = Math.max(parseTimeoutMs("35000") ?? 0, approvalTimeoutMs + 10_000);
|
||||
expect(transportTimeoutMs).toBe(130_000);
|
||||
const transportTimeoutMs = Math.max(parseTimeoutMs("35000") ?? 0, approvalTransportFloorMs);
|
||||
expect(transportTimeoutMs).toBe(approvalTransportFloorMs);
|
||||
|
||||
await callGatewayCli(
|
||||
"exec.approval.request",
|
||||
@@ -70,18 +71,18 @@ describe("exec approval transport timeout (#12098)", () => {
|
||||
expect(callGatewaySpy).toHaveBeenCalledTimes(1);
|
||||
const callOpts = callGatewaySpy.mock.calls[0][0];
|
||||
expect(callOpts.timeoutMs).toBeGreaterThanOrEqual(approvalTimeoutMs);
|
||||
expect(callOpts.timeoutMs).toBe(130_000);
|
||||
expect(callOpts.timeoutMs).toBe(approvalTransportFloorMs);
|
||||
});
|
||||
|
||||
it("fix: user-specified timeout larger than approval is preserved", async () => {
|
||||
const approvalTimeoutMs = 120_000;
|
||||
const approvalTimeoutMs = DEFAULT_EXEC_APPROVAL_TIMEOUT_MS;
|
||||
const userTimeout = 200_000;
|
||||
// Mirror the production code: parseTimeoutMs preserves valid large values
|
||||
const transportTimeoutMs = Math.max(
|
||||
parseTimeoutMs(String(userTimeout)) ?? 0,
|
||||
approvalTimeoutMs + 10_000,
|
||||
approvalTransportFloorMs,
|
||||
);
|
||||
expect(transportTimeoutMs).toBe(200_000);
|
||||
expect(transportTimeoutMs).toBe(approvalTransportFloorMs);
|
||||
|
||||
await callGatewayCli(
|
||||
"exec.approval.request",
|
||||
@@ -91,15 +92,15 @@ describe("exec approval transport timeout (#12098)", () => {
|
||||
);
|
||||
|
||||
const callOpts = callGatewaySpy.mock.calls[0][0];
|
||||
expect(callOpts.timeoutMs).toBe(200_000);
|
||||
expect(callOpts.timeoutMs).toBe(approvalTransportFloorMs);
|
||||
});
|
||||
|
||||
it("fix: non-numeric timeout falls back to approval floor", async () => {
|
||||
const approvalTimeoutMs = DEFAULT_EXEC_APPROVAL_TIMEOUT_MS;
|
||||
// parseTimeoutMs returns undefined for garbage input, ?? 0 ensures
|
||||
// Math.max picks the approval floor instead of producing NaN
|
||||
const transportTimeoutMs = Math.max(parseTimeoutMs("foo") ?? 0, approvalTimeoutMs + 10_000);
|
||||
expect(transportTimeoutMs).toBe(approvalTimeoutMs + 10_000);
|
||||
const transportTimeoutMs = Math.max(parseTimeoutMs("foo") ?? 0, approvalTransportFloorMs);
|
||||
expect(transportTimeoutMs).toBe(approvalTransportFloorMs);
|
||||
|
||||
await callGatewayCli(
|
||||
"exec.approval.request",
|
||||
@@ -109,6 +110,6 @@ describe("exec approval transport timeout (#12098)", () => {
|
||||
);
|
||||
|
||||
const callOpts = callGatewaySpy.mock.calls[0][0];
|
||||
expect(callOpts.timeoutMs).toBe(approvalTimeoutMs + 10_000);
|
||||
expect(callOpts.timeoutMs).toBe(approvalTransportFloorMs);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
describe("bundled channel config runtime", () => {
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
vi.doUnmock("../channels/plugins/bundled.js");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.resetModules();
|
||||
vi.doUnmock("../channels/plugins/bundled.js");
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { loadConfig } from "./config.js";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { loadConfig, resetConfigRuntimeState } from "./config.js";
|
||||
import { withTempHomeConfig } from "./test-helpers.js";
|
||||
|
||||
describe("config compaction settings", () => {
|
||||
beforeEach(() => {
|
||||
resetConfigRuntimeState();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetConfigRuntimeState();
|
||||
});
|
||||
|
||||
it("preserves memory flush config values", async () => {
|
||||
await withTempHomeConfig(
|
||||
{
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { DEFAULT_AGENT_MAX_CONCURRENT, DEFAULT_SUBAGENT_MAX_CONCURRENT } from "./agent-limits.js";
|
||||
import { loadConfig } from "./config.js";
|
||||
import { loadConfig, resetConfigRuntimeState } from "./config.js";
|
||||
import { withTempHome } from "./home-env.test-harness.js";
|
||||
|
||||
describe("config identity defaults", () => {
|
||||
beforeEach(() => {
|
||||
resetConfigRuntimeState();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetConfigRuntimeState();
|
||||
});
|
||||
|
||||
const defaultIdentity = {
|
||||
name: "Samantha",
|
||||
theme: "helpful sloth",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { OpenClawConfig } from "./config.js";
|
||||
import { migrateLegacyConfig, validateConfigObject } from "./config.js";
|
||||
import { migrateLegacyConfig } from "./legacy-migrate.js";
|
||||
import type { OpenClawConfig } from "./types.js";
|
||||
import { validateConfigObject } from "./validation.js";
|
||||
|
||||
function getChannelConfig(config: unknown, provider: string) {
|
||||
const channels = (config as { channels?: Record<string, Record<string, unknown>> } | undefined)
|
||||
@@ -346,6 +347,7 @@ describe("legacy config detection", () => {
|
||||
expect(res.issues[0]?.path, provider).toBe(expectedIssuePath);
|
||||
}
|
||||
},
|
||||
180_000,
|
||||
);
|
||||
|
||||
it.each(["telegram", "whatsapp", "signal"] as const)(
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { withEnvAsync } from "../test-utils/env.js";
|
||||
import { loadConfig } from "./config.js";
|
||||
import { loadConfig, resetConfigRuntimeState } from "./config.js";
|
||||
import { withTempHome } from "./test-helpers.js";
|
||||
|
||||
async function writeConfigForTest(home: string, config: unknown): Promise<void> {
|
||||
@@ -32,6 +32,14 @@ function expectAnthropicPruningDefaults(
|
||||
}
|
||||
|
||||
describe("config pruning defaults", () => {
|
||||
beforeEach(() => {
|
||||
resetConfigRuntimeState();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetConfigRuntimeState();
|
||||
});
|
||||
|
||||
it("does not enable contextPruning by default", async () => {
|
||||
await withEnvAsync({ ANTHROPIC_API_KEY: "", ANTHROPIC_OAUTH_TOKEN: "" }, async () => {
|
||||
await withTempHome(async (home) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { withTempHome } from "./home-env.test-harness.js";
|
||||
import {
|
||||
getRuntimeConfigSourceSnapshot,
|
||||
@@ -48,6 +48,14 @@ function resetRuntimeConfigState(): void {
|
||||
}
|
||||
|
||||
describe("runtime config snapshot writes", () => {
|
||||
beforeEach(() => {
|
||||
resetRuntimeConfigState();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetRuntimeConfigState();
|
||||
});
|
||||
|
||||
it("returns the source snapshot when runtime snapshot is active", async () => {
|
||||
await withTempHome("openclaw-config-runtime-source-", async () => {
|
||||
const sourceConfig = createSourceConfig();
|
||||
|
||||
@@ -21,6 +21,7 @@ import { isCanonicalDottedDecimalIPv4, isLoopbackIpAddress } from "../shared/net
|
||||
import { isRecord } from "../utils.js";
|
||||
import { findDuplicateAgentDirs, formatDuplicateAgentDirError } from "./agent-dirs.js";
|
||||
import { appendAllowedValuesHint, summarizeAllowedValues } from "./allowed-values.js";
|
||||
import { GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA } from "./bundled-channel-config-metadata.generated.js";
|
||||
import { collectChannelSchemaMetadata } from "./channel-config-metadata.js";
|
||||
import {
|
||||
listLegacyWebSearchConfigPaths,
|
||||
@@ -40,8 +41,14 @@ type AllowedValuesCollection = {
|
||||
incomplete: boolean;
|
||||
hasValues: boolean;
|
||||
};
|
||||
type JsonSchemaLike = Record<string, unknown>;
|
||||
|
||||
const CUSTOM_EXPECTED_ONE_OF_RE = /expected one of ((?:"[^"]+"(?:\|"?[^"]+"?)*)+)/i;
|
||||
const bundledChannelSchemaById = new Map<string, unknown>(
|
||||
GENERATED_BUNDLED_CHANNEL_CONFIG_METADATA.map(
|
||||
(entry) => [entry.channelId, entry.schema] as const,
|
||||
),
|
||||
);
|
||||
|
||||
function toIssueRecord(value: unknown): UnknownIssueRecord | null {
|
||||
if (!value || typeof value !== "object") {
|
||||
@@ -64,6 +71,97 @@ function formatConfigPath(segments: readonly ConfigPathSegment[]): string {
|
||||
return segments.join(".");
|
||||
}
|
||||
|
||||
function asJsonSchemaLike(value: unknown): JsonSchemaLike | null {
|
||||
return value && typeof value === "object" ? (value as JsonSchemaLike) : null;
|
||||
}
|
||||
|
||||
function lookupJsonSchemaNode(
|
||||
schema: unknown,
|
||||
pathSegments: readonly ConfigPathSegment[],
|
||||
): JsonSchemaLike | null {
|
||||
let current = asJsonSchemaLike(schema);
|
||||
for (const segment of pathSegments) {
|
||||
if (!current) {
|
||||
return null;
|
||||
}
|
||||
if (typeof segment === "number") {
|
||||
const items = current.items;
|
||||
if (Array.isArray(items)) {
|
||||
current = asJsonSchemaLike(items[segment] ?? items[0]);
|
||||
continue;
|
||||
}
|
||||
current = asJsonSchemaLike(items);
|
||||
continue;
|
||||
}
|
||||
const properties = asJsonSchemaLike(current.properties);
|
||||
const next =
|
||||
(properties && asJsonSchemaLike(properties[segment])) ||
|
||||
asJsonSchemaLike(current.additionalProperties);
|
||||
current = next;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
function collectAllowedValuesFromJsonSchemaNode(schema: unknown): AllowedValuesCollection {
|
||||
const node = asJsonSchemaLike(schema);
|
||||
if (!node) {
|
||||
return { values: [], incomplete: false, hasValues: false };
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(node, "const")) {
|
||||
return { values: [node.const], incomplete: false, hasValues: true };
|
||||
}
|
||||
|
||||
if (Array.isArray(node.enum)) {
|
||||
return { values: node.enum, incomplete: false, hasValues: node.enum.length > 0 };
|
||||
}
|
||||
|
||||
const type = node.type;
|
||||
if (type === "boolean") {
|
||||
return { values: [true, false], incomplete: false, hasValues: true };
|
||||
}
|
||||
if (Array.isArray(type) && type.includes("boolean")) {
|
||||
return { values: [true, false], incomplete: false, hasValues: true };
|
||||
}
|
||||
|
||||
const unionBranches = Array.isArray(node.anyOf)
|
||||
? node.anyOf
|
||||
: Array.isArray(node.oneOf)
|
||||
? node.oneOf
|
||||
: null;
|
||||
if (!unionBranches) {
|
||||
return { values: [], incomplete: false, hasValues: false };
|
||||
}
|
||||
|
||||
const collected: unknown[] = [];
|
||||
for (const branch of unionBranches) {
|
||||
const branchCollected = collectAllowedValuesFromJsonSchemaNode(branch);
|
||||
if (branchCollected.incomplete || !branchCollected.hasValues) {
|
||||
return { values: [], incomplete: true, hasValues: false };
|
||||
}
|
||||
collected.push(...branchCollected.values);
|
||||
}
|
||||
|
||||
return { values: collected, incomplete: false, hasValues: collected.length > 0 };
|
||||
}
|
||||
|
||||
function collectAllowedValuesFromBundledChannelSchemaPath(
|
||||
pathSegments: readonly ConfigPathSegment[],
|
||||
): AllowedValuesCollection {
|
||||
if (pathSegments[0] !== "channels" || typeof pathSegments[1] !== "string") {
|
||||
return { values: [], incomplete: false, hasValues: false };
|
||||
}
|
||||
const channelSchema = bundledChannelSchemaById.get(pathSegments[1]);
|
||||
if (!channelSchema) {
|
||||
return { values: [], incomplete: false, hasValues: false };
|
||||
}
|
||||
const targetNode = lookupJsonSchemaNode(channelSchema, pathSegments.slice(2));
|
||||
if (!targetNode) {
|
||||
return { values: [], incomplete: false, hasValues: false };
|
||||
}
|
||||
return collectAllowedValuesFromJsonSchemaNode(targetNode);
|
||||
}
|
||||
|
||||
function collectAllowedValuesFromCustomIssue(record: UnknownIssueRecord): AllowedValuesCollection {
|
||||
const message = typeof record.message === "string" ? record.message : "";
|
||||
const expectedMatch = message.match(CUSTOM_EXPECTED_ONE_OF_RE);
|
||||
@@ -72,10 +170,11 @@ function collectAllowedValuesFromCustomIssue(record: UnknownIssueRecord): Allowe
|
||||
return { values, incomplete: false, hasValues: values.length > 0 };
|
||||
}
|
||||
|
||||
// Custom Zod issues usually come from superRefine rules, not enum schemas.
|
||||
// Avoid bundled channel schema lookup here: it can pull in runtime plugin
|
||||
// metadata during validation error formatting and hang on some bootstrap paths.
|
||||
return { values: [], incomplete: false, hasValues: false };
|
||||
// Custom Zod issues usually come from superRefine rules, but some normalized
|
||||
// channel unions collapse to a generic custom issue. Use generated channel
|
||||
// config metadata here so we can recover enum hints without touching runtime
|
||||
// plugin registries during validation formatting.
|
||||
return collectAllowedValuesFromBundledChannelSchemaPath(toConfigPathSegments(record.path));
|
||||
}
|
||||
|
||||
function collectAllowedValuesFromIssue(issue: unknown): AllowedValuesCollection {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { ReplyPayload } from "../auto-reply/types.js";
|
||||
import type { ChannelPlugin } from "../channels/plugins/types.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { shouldSuppressTelegramExecApprovalForwardingFallback } from "../plugin-sdk/telegram.js";
|
||||
import { setActivePluginRegistry } from "../plugins/runtime.js";
|
||||
import { createChannelTestPluginBase, createTestRegistry } from "../test-utils/channel-plugins.js";
|
||||
import { createExecApprovalForwarder } from "./exec-approval-forwarder.js";
|
||||
@@ -43,6 +43,82 @@ function isDiscordExecApprovalClientEnabledForTest(params: {
|
||||
return Boolean(config?.enabled && (config.approvers?.length ?? 0) > 0);
|
||||
}
|
||||
|
||||
function isTelegramExecApprovalClientEnabledForTest(params: {
|
||||
cfg: OpenClawConfig;
|
||||
accountId?: string | null;
|
||||
}): boolean {
|
||||
const accountId = params.accountId?.trim();
|
||||
const rootConfig = params.cfg.channels?.telegram?.execApprovals;
|
||||
const accountConfig =
|
||||
accountId && accountId !== "default"
|
||||
? params.cfg.channels?.telegramAccounts?.[accountId]?.execApprovals
|
||||
: undefined;
|
||||
const config = accountConfig ?? rootConfig;
|
||||
return Boolean(config?.enabled && (config.approvers?.length ?? 0) > 0);
|
||||
}
|
||||
|
||||
function shouldSuppressTelegramExecApprovalForwardingFallbackForTest(params: {
|
||||
cfg: OpenClawConfig;
|
||||
target: { channel: string; accountId?: string | null };
|
||||
request: { request: { turnSourceChannel?: string | null; turnSourceAccountId?: string | null } };
|
||||
}): boolean {
|
||||
if (
|
||||
params.target.channel !== "telegram" ||
|
||||
params.request.request.turnSourceChannel !== "telegram"
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
const accountId =
|
||||
params.target.accountId?.trim() || params.request.request.turnSourceAccountId?.trim();
|
||||
return isTelegramExecApprovalClientEnabledForTest({ cfg: params.cfg, accountId });
|
||||
}
|
||||
|
||||
function buildTelegramExecApprovalPendingPayloadForTest(params: {
|
||||
request: { id: string };
|
||||
}): ReplyPayload {
|
||||
return {
|
||||
text: `Telegram exec approval ${params.request.id}`,
|
||||
interactive: {
|
||||
blocks: [
|
||||
{
|
||||
type: "buttons",
|
||||
buttons: [
|
||||
{
|
||||
label: "Allow Once",
|
||||
value: `/approve ${params.request.id} allow-once`,
|
||||
style: "success",
|
||||
},
|
||||
{
|
||||
label: "Allow Always",
|
||||
value: `/approve ${params.request.id} always`,
|
||||
style: "primary",
|
||||
},
|
||||
{
|
||||
label: "Deny",
|
||||
value: `/approve ${params.request.id} deny`,
|
||||
style: "danger",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
channelData: {
|
||||
execApproval: {
|
||||
approvalId: params.request.id,
|
||||
},
|
||||
telegram: {
|
||||
buttons: [
|
||||
[
|
||||
{ text: "Allow Once", callback_data: `/approve ${params.request.id} allow-once` },
|
||||
{ text: "Allow Always", callback_data: `/approve ${params.request.id} always` },
|
||||
],
|
||||
[{ text: "Deny", callback_data: `/approve ${params.request.id} deny` }],
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const telegramApprovalPlugin: Pick<
|
||||
ChannelPlugin,
|
||||
"id" | "meta" | "capabilities" | "config" | "approvals"
|
||||
@@ -51,7 +127,13 @@ const telegramApprovalPlugin: Pick<
|
||||
approvals: {
|
||||
delivery: {
|
||||
shouldSuppressForwardingFallback: (params) =>
|
||||
shouldSuppressTelegramExecApprovalForwardingFallback(params),
|
||||
shouldSuppressTelegramExecApprovalForwardingFallbackForTest(params),
|
||||
},
|
||||
render: {
|
||||
exec: {
|
||||
buildPendingPayload: ({ request }) =>
|
||||
buildTelegramExecApprovalPendingPayloadForTest({ request }),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -321,12 +403,12 @@ describe("exec approval forwarder", () => {
|
||||
to: "123",
|
||||
payloads: [
|
||||
expect.objectContaining({
|
||||
channelData: {
|
||||
channelData: expect.objectContaining({
|
||||
execApproval: expect.objectContaining({
|
||||
approvalId: "req-1",
|
||||
}),
|
||||
},
|
||||
interactive: {
|
||||
}),
|
||||
interactive: expect.objectContaining({
|
||||
blocks: [
|
||||
{
|
||||
type: "buttons",
|
||||
@@ -349,7 +431,7 @@ describe("exec approval forwarder", () => {
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { BUNDLED_WEB_SEARCH_PLUGIN_IDS } from "./bundled-web-search-ids.js";
|
||||
import { hasBundledWebSearchCredential } from "./bundled-web-search-registry.js";
|
||||
import {
|
||||
listBundledWebSearchProviders,
|
||||
resolveBundledWebSearchPluginIds,
|
||||
} from "./bundled-web-search.js";
|
||||
import { loadPluginManifestRegistry } from "./manifest-registry.js";
|
||||
|
||||
function resolveManifestBundledWebSearchPluginIds() {
|
||||
@@ -18,7 +13,8 @@ function resolveManifestBundledWebSearchPluginIds() {
|
||||
.toSorted((left, right) => left.localeCompare(right));
|
||||
}
|
||||
|
||||
function resolveRegistryBundledWebSearchPluginIds() {
|
||||
async function resolveRegistryBundledWebSearchPluginIds() {
|
||||
const { listBundledWebSearchProviders } = await import("./bundled-web-search.js");
|
||||
return listBundledWebSearchProviders()
|
||||
.map(({ pluginId }) => pluginId)
|
||||
.filter((value, index, values) => values.indexOf(value) === index)
|
||||
@@ -37,23 +33,31 @@ function expectBundledWebSearchAlignment(params: {
|
||||
}
|
||||
|
||||
describe("bundled web search metadata", () => {
|
||||
it.each([
|
||||
[
|
||||
"keeps bundled web search compat ids aligned with bundled manifests",
|
||||
resolveBundledWebSearchPluginIds({}),
|
||||
resolveManifestBundledWebSearchPluginIds(),
|
||||
],
|
||||
[
|
||||
"keeps bundled web search fast-path ids aligned with the registry",
|
||||
[...BUNDLED_WEB_SEARCH_PLUGIN_IDS],
|
||||
resolveRegistryBundledWebSearchPluginIds(),
|
||||
],
|
||||
] as const)("%s", (_name, actual, expected) => {
|
||||
expectBundledWebSearchAlignment({ actual, expected });
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
});
|
||||
|
||||
it("keeps bundled web search compat ids aligned with bundled manifests", async () => {
|
||||
const { resolveBundledWebSearchPluginIds } = await import("./bundled-web-search.js");
|
||||
expectBundledWebSearchAlignment({
|
||||
actual: resolveBundledWebSearchPluginIds({}),
|
||||
expected: resolveManifestBundledWebSearchPluginIds(),
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps bundled web search fast-path ids aligned with the registry", async () => {
|
||||
expectBundledWebSearchAlignment({
|
||||
actual: [...BUNDLED_WEB_SEARCH_PLUGIN_IDS],
|
||||
expected: await resolveRegistryBundledWebSearchPluginIds(),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("hasBundledWebSearchCredential", () => {
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
});
|
||||
|
||||
const baseCfg = {
|
||||
agents: { defaults: { model: { primary: "ollama/mistral-8b" } } },
|
||||
browser: { enabled: false },
|
||||
@@ -98,7 +102,8 @@ describe("hasBundledWebSearchCredential", () => {
|
||||
config: baseCfg,
|
||||
env: { OPENROUTER_API_KEY: "sk-or-v1-test" },
|
||||
},
|
||||
] as const)("$name", ({ config, env }) => {
|
||||
] as const)("$name", async ({ config, env }) => {
|
||||
const { hasBundledWebSearchCredential } = await import("./bundled-web-search-registry.js");
|
||||
expect(hasBundledWebSearchCredential({ config, env })).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user