mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 14:50:45 +00:00
test: narrow hotspot boundaries
This commit is contained in:
@@ -4,9 +4,8 @@ const resolveAgentWorkspaceDir = vi.hoisted(() =>
|
||||
vi.fn((_cfg?: unknown, _agentId?: unknown) => "/tmp/openclaw-workspace"),
|
||||
);
|
||||
const resolveDefaultAgentId = vi.hoisted(() => vi.fn((_cfg?: unknown) => "default"));
|
||||
const listChannelPluginCatalogEntries = vi.hoisted(() => vi.fn((_opts?: unknown): unknown[] => []));
|
||||
const getChannelPluginCatalogEntry = vi.hoisted(() =>
|
||||
vi.fn((_id?: unknown, _opts?: unknown) => undefined),
|
||||
const listTrustedChannelPluginCatalogEntries = vi.hoisted(() =>
|
||||
vi.fn((_params?: unknown): unknown[] => []),
|
||||
);
|
||||
const getChannelSetupPlugin = vi.hoisted(() => vi.fn((_channel?: unknown) => undefined));
|
||||
const listChannelSetupPlugins = vi.hoisted(() => vi.fn((): unknown[] => []));
|
||||
@@ -30,12 +29,6 @@ vi.mock("../agents/agent-scope.js", () => ({
|
||||
resolveDefaultAgentId: (cfg?: unknown) => resolveDefaultAgentId(cfg),
|
||||
}));
|
||||
|
||||
vi.mock("../channels/plugins/catalog.js", () => ({
|
||||
listChannelPluginCatalogEntries: (opts?: unknown) => listChannelPluginCatalogEntries(opts),
|
||||
getChannelPluginCatalogEntry: (id?: unknown, opts?: unknown) =>
|
||||
getChannelPluginCatalogEntry(id, opts),
|
||||
}));
|
||||
|
||||
vi.mock("../channels/plugins/setup-registry.js", () => ({
|
||||
getChannelSetupPlugin: (channel?: unknown) => getChannelSetupPlugin(channel),
|
||||
listChannelSetupPlugins: () => listChannelSetupPlugins(),
|
||||
@@ -63,6 +56,11 @@ vi.mock("../commands/channel-setup/registry.js", () => ({
|
||||
resolveChannelSetupWizardAdapterForPlugin: () => undefined,
|
||||
}));
|
||||
|
||||
vi.mock("../commands/channel-setup/trusted-catalog.js", () => ({
|
||||
listTrustedChannelPluginCatalogEntries: (params?: unknown) =>
|
||||
listTrustedChannelPluginCatalogEntries(params),
|
||||
}));
|
||||
|
||||
vi.mock("../config/channel-configured.js", () => ({
|
||||
isChannelConfigured: (cfg?: unknown, channel?: unknown) => isChannelConfigured(cfg, channel),
|
||||
}));
|
||||
@@ -90,10 +88,11 @@ describe("setupChannels workspace shadow exclusion", () => {
|
||||
vi.clearAllMocks();
|
||||
resolveAgentWorkspaceDir.mockReturnValue("/tmp/openclaw-workspace");
|
||||
resolveDefaultAgentId.mockReturnValue("default");
|
||||
listChannelPluginCatalogEntries.mockReturnValue([
|
||||
listTrustedChannelPluginCatalogEntries.mockReturnValue([
|
||||
{
|
||||
id: "telegram",
|
||||
pluginId: "@openclaw/telegram-plugin",
|
||||
origin: "bundled",
|
||||
},
|
||||
]);
|
||||
getChannelSetupPlugin.mockReturnValue(undefined);
|
||||
@@ -112,13 +111,7 @@ describe("setupChannels workspace shadow exclusion", () => {
|
||||
isChannelConfigured.mockReturnValue(true);
|
||||
});
|
||||
|
||||
it("preloads configured external plugins from the bundled fallback for untrusted shadows", async () => {
|
||||
listChannelPluginCatalogEntries.mockImplementation((opts?: unknown) =>
|
||||
(opts as { excludeWorkspace?: boolean } | undefined)?.excludeWorkspace
|
||||
? [{ id: "telegram", pluginId: "@openclaw/telegram-plugin", origin: "bundled" }]
|
||||
: [{ id: "telegram", pluginId: "evil-telegram-shadow", origin: "workspace" }],
|
||||
);
|
||||
|
||||
it("preloads configured external plugins from the trusted catalog boundary", async () => {
|
||||
await setupChannels(
|
||||
{} as never,
|
||||
{} as never,
|
||||
@@ -128,10 +121,12 @@ describe("setupChannels workspace shadow exclusion", () => {
|
||||
} as never,
|
||||
);
|
||||
|
||||
const fallbackCall = listChannelPluginCatalogEntries.mock.calls.find(
|
||||
([opts]) => (opts as { excludeWorkspace?: boolean } | undefined)?.excludeWorkspace === true,
|
||||
expect(listTrustedChannelPluginCatalogEntries).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
cfg: {},
|
||||
workspaceDir: "/tmp/openclaw-workspace",
|
||||
}),
|
||||
);
|
||||
expect(fallbackCall).toBeTruthy();
|
||||
expect(loadChannelSetupPluginRegistrySnapshotForChannel).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
channel: "telegram",
|
||||
@@ -142,7 +137,7 @@ describe("setupChannels workspace shadow exclusion", () => {
|
||||
});
|
||||
|
||||
it("keeps trusted workspace overrides eligible during preload", async () => {
|
||||
listChannelPluginCatalogEntries.mockReturnValue([
|
||||
listTrustedChannelPluginCatalogEntries.mockReturnValue([
|
||||
{ id: "telegram", pluginId: "trusted-telegram-shadow", origin: "workspace" },
|
||||
]);
|
||||
|
||||
|
||||
@@ -12,14 +12,13 @@ import {
|
||||
} from "./embeddings-gemini.js";
|
||||
import {
|
||||
createGeminiBatchFetchMock,
|
||||
createGeminiFetchMock,
|
||||
createJsonResponseFetchMock,
|
||||
installFetchMock,
|
||||
mockResolvedProviderKey,
|
||||
parseFetchBody,
|
||||
readFirstFetchRequest,
|
||||
type JsonFetchMock,
|
||||
} from "./embeddings-provider.test-support.js";
|
||||
import { mockPublicPinnedHostname } from "./test-helpers/ssrf.js";
|
||||
|
||||
vi.mock("../../agents/model-auth.js", async () => {
|
||||
const { createModelAuthMockModule } = await import("../../test-utils/model-auth-mock.js");
|
||||
@@ -42,7 +41,6 @@ async function createProviderWithFetch(
|
||||
options: Partial<Parameters<typeof createGeminiEmbeddingProvider>[0]> & { model: string },
|
||||
) {
|
||||
installFetchMock(fetchMock as unknown as typeof globalThis.fetch);
|
||||
mockPublicPinnedHostname();
|
||||
mockResolvedProviderKey(authModule.resolveApiKeyForProvider);
|
||||
const { provider } = await createGeminiEmbeddingProvider({
|
||||
config: {} as never,
|
||||
@@ -169,55 +167,26 @@ describe("gemini embedding provider", () => {
|
||||
expect(parseFetchBody(legacyFetch, 0)).not.toHaveProperty("outputDimensionality");
|
||||
expect(parseFetchBody(legacyFetch, 1)).not.toHaveProperty("outputDimensionality");
|
||||
|
||||
const v2QueryFetch = createGeminiFetchMock([3, 4, Number.NaN]);
|
||||
const v2QueryProvider = await createProviderWithFetch(v2QueryFetch, {
|
||||
model: "gemini-embedding-2-preview",
|
||||
const v2Fetch = createJsonResponseFetchMock((input) => {
|
||||
const url = input instanceof URL ? input.href : typeof input === "string" ? input : input.url;
|
||||
return url.endsWith(":batchEmbedContents")
|
||||
? {
|
||||
embeddings: Array.from({ length: 2 }, () => ({
|
||||
values: [0, Number.POSITIVE_INFINITY, 5],
|
||||
})),
|
||||
}
|
||||
: { embedding: { values: [3, 4, Number.NaN] } };
|
||||
});
|
||||
await expect(v2QueryProvider.embedQuery(" ")).resolves.toEqual([]);
|
||||
await expect(v2QueryProvider.embedBatch([])).resolves.toEqual([]);
|
||||
await expect(v2QueryProvider.embedQuery("test query")).resolves.toEqual([0.6, 0.8, 0]);
|
||||
|
||||
const v2BatchFetch = createGeminiBatchFetchMock(2, [0, Number.POSITIVE_INFINITY, 5]);
|
||||
const v2BatchProvider = await createProviderWithFetch(v2BatchFetch, {
|
||||
model: "gemini-embedding-2-preview",
|
||||
});
|
||||
const batch = await v2BatchProvider.embedBatch(["text1", "text2"]);
|
||||
expect(batch).toEqual([
|
||||
[0, 0, 1],
|
||||
[0, 0, 1],
|
||||
]);
|
||||
|
||||
expect(parseFetchBody(v2QueryFetch)).toMatchObject({
|
||||
outputDimensionality: 3072,
|
||||
taskType: "RETRIEVAL_QUERY",
|
||||
content: { parts: [{ text: "test query" }] },
|
||||
});
|
||||
expect(parseFetchBody(v2BatchFetch).requests).toEqual([
|
||||
{
|
||||
model: "models/gemini-embedding-2-preview",
|
||||
content: { parts: [{ text: "text1" }] },
|
||||
taskType: "RETRIEVAL_DOCUMENT",
|
||||
outputDimensionality: 3072,
|
||||
},
|
||||
{
|
||||
model: "models/gemini-embedding-2-preview",
|
||||
content: { parts: [{ text: "text2" }] },
|
||||
taskType: "RETRIEVAL_DOCUMENT",
|
||||
outputDimensionality: 3072,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("supports custom dimensions, task type, multimodal inputs, and endpoint URL", async () => {
|
||||
const fetchMock = createGeminiBatchFetchMock(2);
|
||||
const provider = await createProviderWithFetch(fetchMock, {
|
||||
const v2Provider = await createProviderWithFetch(v2Fetch, {
|
||||
model: "gemini-embedding-2-preview",
|
||||
outputDimensionality: 768,
|
||||
taskType: "SEMANTIC_SIMILARITY",
|
||||
});
|
||||
await expect(v2Provider.embedQuery(" ")).resolves.toEqual([]);
|
||||
await expect(v2Provider.embedBatch([])).resolves.toEqual([]);
|
||||
await expect(v2Provider.embedQuery("test query")).resolves.toEqual([0.6, 0.8, 0]);
|
||||
|
||||
await provider.embedQuery("test");
|
||||
const structuredBatch = await provider.embedBatchInputs?.([
|
||||
const structuredBatch = await v2Provider.embedBatchInputs?.([
|
||||
{
|
||||
text: "Image file: diagram.png",
|
||||
parts: [
|
||||
@@ -234,19 +203,20 @@ describe("gemini embedding provider", () => {
|
||||
},
|
||||
]);
|
||||
expect(structuredBatch).toEqual([
|
||||
[0.2672612419124244, 0.5345224838248488, 0.8017837257372732],
|
||||
[0.2672612419124244, 0.5345224838248488, 0.8017837257372732],
|
||||
[0, 0, 1],
|
||||
[0, 0, 1],
|
||||
]);
|
||||
|
||||
const { url } = readFirstFetchRequest(fetchMock);
|
||||
const { url } = readFirstFetchRequest(v2Fetch);
|
||||
expect(url).toBe(
|
||||
"https://generativelanguage.googleapis.com/v1beta/models/gemini-embedding-2-preview:embedContent",
|
||||
);
|
||||
expect(parseFetchBody(fetchMock, 0)).toMatchObject({
|
||||
expect(parseFetchBody(v2Fetch, 0)).toMatchObject({
|
||||
outputDimensionality: 768,
|
||||
taskType: "SEMANTIC_SIMILARITY",
|
||||
content: { parts: [{ text: "test query" }] },
|
||||
});
|
||||
expect(parseFetchBody(fetchMock, 1).requests).toEqual([
|
||||
expect(parseFetchBody(v2Fetch, 1).requests).toEqual([
|
||||
{
|
||||
model: "models/gemini-embedding-2-preview",
|
||||
content: {
|
||||
|
||||
@@ -7,6 +7,48 @@ import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vites
|
||||
import { resolveOAuthDir } from "../config/paths.js";
|
||||
import { DEFAULT_ACCOUNT_ID } from "../routing/session-key.js";
|
||||
import { withEnvAsync } from "../test-utils/env.js";
|
||||
|
||||
vi.mock("../channels/plugins/pairing.js", () => ({
|
||||
getPairingAdapter: () => null,
|
||||
}));
|
||||
|
||||
vi.mock("../infra/file-lock.js", () => ({
|
||||
withFileLock: async (_path: string, _options: unknown, fn: () => unknown) => await fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../plugin-sdk/json-store.js", async () => {
|
||||
const fs = await import("node:fs/promises");
|
||||
const path = await import("node:path");
|
||||
|
||||
return {
|
||||
readJsonFileWithFallback: async <T>(filePath: string, fallback: T) => {
|
||||
let raw: string;
|
||||
try {
|
||||
raw = await fs.readFile(filePath, "utf8");
|
||||
} catch (err) {
|
||||
if ((err as { code?: string }).code === "ENOENT") {
|
||||
return { value: fallback, exists: false };
|
||||
}
|
||||
return { value: fallback, exists: false };
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(raw) as T;
|
||||
return {
|
||||
value: parsed ?? fallback,
|
||||
exists: true,
|
||||
};
|
||||
} catch {
|
||||
return { value: fallback, exists: true };
|
||||
}
|
||||
},
|
||||
writeJsonFileAtomically: async (filePath: string, value: unknown) => {
|
||||
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
||||
await fs.writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
import * as jsonStore from "../plugin-sdk/json-store.js";
|
||||
import {
|
||||
addChannelAllowFromStoreEntry,
|
||||
clearPairingAllowFromReadCacheForTest,
|
||||
@@ -552,7 +594,7 @@ describe("pairing store", () => {
|
||||
await withTempStateDir(async (stateDir) => {
|
||||
for (const variant of [
|
||||
{
|
||||
createReadSpy: () => vi.spyOn(fs, "readFile"),
|
||||
createReadSpy: () => vi.spyOn(jsonStore, "readJsonFileWithFallback"),
|
||||
readAllowFrom: () => readChannelAllowFromStore("telegram", process.env, "yy"),
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user