mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-22 15:31:07 +00:00
test: trim import-heavy startup paths
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { parseFeishuConversationId } from "../../extensions/feishu/src/conversation-id.js";
|
||||
import { parseTelegramTopicConversation } from "../../extensions/telegram/runtime-api.js";
|
||||
import { resolveAgentWorkspaceDir } from "../agents/agent-scope.js";
|
||||
import type { ChannelConfiguredBindingProvider, ChannelPlugin } from "../channels/plugins/types.js";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import { setActivePluginRegistry } from "../plugins/runtime.js";
|
||||
import { createChannelTestPluginBase, createTestRegistry } from "../test-utils/channel-plugins.js";
|
||||
import { parseTelegramTopicConversation } from "./conversation-id.js";
|
||||
import * as persistentBindingsResolveModule from "./persistent-bindings.resolve.js";
|
||||
import { buildConfiguredAcpSessionKey } from "./persistent-bindings.types.js";
|
||||
const managerMocks = vi.hoisted(() => ({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import { normalizeTelegramMessagingTarget } from "../../extensions/telegram/api.js";
|
||||
import { normalizeTelegramMessagingTarget } from "../../extensions/telegram/src/normalize.js";
|
||||
import { setActivePluginRegistry } from "../plugins/runtime.js";
|
||||
import { createChannelTestPluginBase, createTestRegistry } from "../test-utils/channel-plugins.js";
|
||||
import { extractMessagingToolSend } from "./pi-embedded-subscribe.tools.js";
|
||||
|
||||
@@ -3,24 +3,33 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildConfigDocBaseline,
|
||||
type ConfigDocBaseline,
|
||||
renderConfigDocBaselineStatefile,
|
||||
writeConfigDocBaselineStatefile,
|
||||
} from "./doc-baseline.js";
|
||||
|
||||
describe("config doc baseline integration", () => {
|
||||
const tempRoots: string[] = [];
|
||||
let sharedBaselinePromise: Promise<Awaited<ReturnType<typeof buildConfigDocBaseline>>> | null =
|
||||
null;
|
||||
const generatedBaselineJsonPath = path.resolve(
|
||||
process.cwd(),
|
||||
"docs/.generated/config-baseline.json",
|
||||
);
|
||||
const generatedBaselineJsonlPath = path.resolve(
|
||||
process.cwd(),
|
||||
"docs/.generated/config-baseline.jsonl",
|
||||
);
|
||||
let sharedBaselinePromise: Promise<ConfigDocBaseline> | null = null;
|
||||
let sharedRenderedPromise: Promise<
|
||||
Awaited<ReturnType<typeof renderConfigDocBaselineStatefile>>
|
||||
> | null = null;
|
||||
let sharedByPathPromise: Promise<
|
||||
Map<string, Awaited<ReturnType<typeof buildConfigDocBaseline>>["entries"][number]>
|
||||
> | null = null;
|
||||
let sharedGeneratedJsonPromise: Promise<string> | null = null;
|
||||
let sharedGeneratedJsonlPromise: Promise<string> | null = null;
|
||||
let sharedByPathPromise: Promise<Map<string, ConfigDocBaseline["entries"][number]>> | null = null;
|
||||
|
||||
function getSharedBaseline() {
|
||||
sharedBaselinePromise ??= buildConfigDocBaseline();
|
||||
sharedBaselinePromise ??= fs
|
||||
.readFile(generatedBaselineJsonPath, "utf8")
|
||||
.then((raw) => JSON.parse(raw) as ConfigDocBaseline);
|
||||
return sharedBaselinePromise;
|
||||
}
|
||||
|
||||
@@ -29,6 +38,16 @@ describe("config doc baseline integration", () => {
|
||||
return sharedRenderedPromise;
|
||||
}
|
||||
|
||||
function getGeneratedJson() {
|
||||
sharedGeneratedJsonPromise ??= fs.readFile(generatedBaselineJsonPath, "utf8");
|
||||
return sharedGeneratedJsonPromise;
|
||||
}
|
||||
|
||||
function getGeneratedJsonl() {
|
||||
sharedGeneratedJsonlPromise ??= fs.readFile(generatedBaselineJsonlPath, "utf8");
|
||||
return sharedGeneratedJsonlPromise;
|
||||
}
|
||||
|
||||
function getSharedByPath() {
|
||||
sharedByPathPromise ??= getSharedBaseline().then(
|
||||
(baseline) => new Map(baseline.entries.map((entry) => [entry.path, entry])),
|
||||
@@ -45,13 +64,25 @@ describe("config doc baseline integration", () => {
|
||||
});
|
||||
|
||||
it("is deterministic across repeated runs", async () => {
|
||||
const first = await getSharedRendered();
|
||||
const second = await renderConfigDocBaselineStatefile();
|
||||
const baseline = await getSharedBaseline();
|
||||
const first = await renderConfigDocBaselineStatefile(baseline);
|
||||
const second = await renderConfigDocBaselineStatefile(baseline);
|
||||
|
||||
expect(second.json).toBe(first.json);
|
||||
expect(second.jsonl).toBe(first.jsonl);
|
||||
});
|
||||
|
||||
it("matches the checked-in generated baseline artifacts", async () => {
|
||||
const [rendered, generatedJson, generatedJsonl] = await Promise.all([
|
||||
getSharedRendered(),
|
||||
getGeneratedJson(),
|
||||
getGeneratedJsonl(),
|
||||
]);
|
||||
|
||||
expect(rendered.json).toBe(generatedJson);
|
||||
expect(rendered.jsonl).toBe(generatedJsonl);
|
||||
});
|
||||
|
||||
it("includes core, channel, and plugin config metadata", async () => {
|
||||
const byPath = await getSharedByPath();
|
||||
|
||||
|
||||
@@ -1,23 +1,70 @@
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { parseTelegramTarget } from "../../../extensions/telegram/api.js";
|
||||
import { telegramOutbound, whatsappOutbound } from "../../../test/channel-outbounds.js";
|
||||
import type { ChannelOutboundAdapter } from "../../channels/plugins/types.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import { setActivePluginRegistry } from "../../plugins/runtime.js";
|
||||
import { createOutboundTestPlugin, createTestRegistry } from "../../test-utils/channel-plugins.js";
|
||||
import { isWhatsAppGroupJid, normalizeWhatsAppTarget } from "../../whatsapp/normalize.js";
|
||||
import { resolveOutboundTarget } from "./targets.js";
|
||||
|
||||
const telegramMessaging = {
|
||||
parseExplicitTarget: ({ raw }: { raw: string }) => {
|
||||
const target = parseTelegramTarget(raw);
|
||||
return {
|
||||
to: target.chatId,
|
||||
threadId: target.messageThreadId,
|
||||
chatType: target.chatType === "unknown" ? undefined : target.chatType,
|
||||
};
|
||||
},
|
||||
type TelegramTargetParts = {
|
||||
chatId: string;
|
||||
messageThreadId?: number;
|
||||
chatType: "direct" | "group" | "unknown";
|
||||
};
|
||||
|
||||
function parseTelegramTestTarget(raw: string): TelegramTargetParts {
|
||||
const trimmed = raw
|
||||
.trim()
|
||||
.replace(/^(telegram|tg):/i, "")
|
||||
.trim();
|
||||
const topicMatch = /^(.+?):topic:(\d+)$/u.exec(trimmed);
|
||||
if (topicMatch) {
|
||||
const chatId = topicMatch[1];
|
||||
return {
|
||||
chatId,
|
||||
messageThreadId: Number.parseInt(topicMatch[2], 10),
|
||||
chatType: chatId.startsWith("-") ? "group" : "direct",
|
||||
};
|
||||
}
|
||||
|
||||
const threadMatch = /^(.+):(\d+)$/u.exec(trimmed);
|
||||
if (threadMatch) {
|
||||
const chatId = threadMatch[1];
|
||||
return {
|
||||
chatId,
|
||||
messageThreadId: Number.parseInt(threadMatch[2], 10),
|
||||
chatType: chatId.startsWith("-") ? "group" : "direct",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
chatId: trimmed,
|
||||
chatType: trimmed.startsWith("-") ? "group" : "direct",
|
||||
};
|
||||
}
|
||||
|
||||
const telegramMessaging = {
|
||||
parseExplicitTarget: ({ raw }: { raw: string }) => parseTelegramTestMessagingTarget(raw),
|
||||
};
|
||||
|
||||
export function inferTelegramTestChatType(to: string): "direct" | "group" | undefined {
|
||||
const chatType = parseTelegramTestTarget(to).chatType;
|
||||
return chatType === "unknown" ? undefined : chatType;
|
||||
}
|
||||
|
||||
export function parseTelegramTestMessagingTarget(raw: string): {
|
||||
to: string;
|
||||
threadId?: number;
|
||||
chatType?: "direct" | "group";
|
||||
} {
|
||||
const target = parseTelegramTestTarget(raw);
|
||||
return {
|
||||
to: target.chatId,
|
||||
threadId: target.messageThreadId,
|
||||
chatType: target.chatType === "unknown" ? undefined : target.chatType,
|
||||
};
|
||||
}
|
||||
|
||||
const whatsappMessaging = {
|
||||
inferTargetChatType: ({ to }: { to: string }) => {
|
||||
const normalized = normalizeWhatsAppTarget(to);
|
||||
@@ -31,6 +78,24 @@ const whatsappMessaging = {
|
||||
},
|
||||
};
|
||||
|
||||
export const telegramOutboundStub: ChannelOutboundAdapter = {
|
||||
deliveryMode: "direct",
|
||||
};
|
||||
|
||||
export const whatsappOutboundStub: ChannelOutboundAdapter = {
|
||||
deliveryMode: "gateway",
|
||||
resolveTarget: ({ to }) => {
|
||||
const normalized = typeof to === "string" ? normalizeWhatsAppTarget(to) : undefined;
|
||||
if (normalized) {
|
||||
return { ok: true as const, to: normalized };
|
||||
}
|
||||
return {
|
||||
ok: false as const,
|
||||
error: new Error("WhatsApp target required"),
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export function installResolveOutboundTargetPluginRegistryHooks(): void {
|
||||
beforeEach(() => {
|
||||
setActivePluginRegistry(
|
||||
@@ -41,7 +106,7 @@ export function installResolveOutboundTargetPluginRegistryHooks(): void {
|
||||
...createOutboundTestPlugin({
|
||||
id: "whatsapp",
|
||||
label: "WhatsApp",
|
||||
outbound: whatsappOutbound,
|
||||
outbound: whatsappOutboundStub,
|
||||
messaging: whatsappMessaging,
|
||||
}),
|
||||
config: {
|
||||
@@ -60,7 +125,7 @@ export function installResolveOutboundTargetPluginRegistryHooks(): void {
|
||||
...createOutboundTestPlugin({
|
||||
id: "telegram",
|
||||
label: "Telegram",
|
||||
outbound: telegramOutbound,
|
||||
outbound: telegramOutboundStub,
|
||||
messaging: telegramMessaging,
|
||||
}),
|
||||
config: {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import { parseTelegramTarget } from "../../../extensions/telegram/src/targets.js";
|
||||
import { telegramOutbound, whatsappOutbound } from "../../../test/channel-outbounds.js";
|
||||
import type { ChannelOutboundAdapter } from "../../channels/plugins/types.js";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import type { SessionEntry } from "../../config/sessions/types.js";
|
||||
@@ -14,25 +12,19 @@ import {
|
||||
} from "./targets.js";
|
||||
import type { SessionDeliveryTarget } from "./targets.js";
|
||||
import {
|
||||
inferTelegramTestChatType,
|
||||
installResolveOutboundTargetPluginRegistryHooks,
|
||||
parseTelegramTestMessagingTarget,
|
||||
runResolveOutboundTargetCoreTests,
|
||||
telegramOutboundStub,
|
||||
whatsappOutboundStub,
|
||||
} from "./targets.shared-test.js";
|
||||
|
||||
runResolveOutboundTargetCoreTests();
|
||||
|
||||
const telegramMessaging = {
|
||||
parseExplicitTarget: ({ raw }: { raw: string }) => {
|
||||
const target = parseTelegramTarget(raw);
|
||||
return {
|
||||
to: target.chatId,
|
||||
threadId: target.messageThreadId,
|
||||
chatType: target.chatType === "unknown" ? undefined : target.chatType,
|
||||
};
|
||||
},
|
||||
inferTargetChatType: ({ to }: { to: string }) => {
|
||||
const target = parseTelegramTarget(to);
|
||||
return target.chatType === "unknown" ? undefined : target.chatType;
|
||||
},
|
||||
parseExplicitTarget: ({ raw }: { raw: string }) => parseTelegramTestMessagingTarget(raw),
|
||||
inferTargetChatType: ({ to }: { to: string }) => inferTelegramTestChatType(to),
|
||||
};
|
||||
|
||||
const whatsappMessaging = {
|
||||
@@ -72,7 +64,7 @@ beforeEach(() => {
|
||||
pluginId: "telegram",
|
||||
plugin: createOutboundTestPlugin({
|
||||
id: "telegram",
|
||||
outbound: telegramOutbound,
|
||||
outbound: telegramOutboundStub,
|
||||
messaging: telegramMessaging,
|
||||
}),
|
||||
source: "test",
|
||||
@@ -81,7 +73,7 @@ beforeEach(() => {
|
||||
pluginId: "whatsapp",
|
||||
plugin: createOutboundTestPlugin({
|
||||
id: "whatsapp",
|
||||
outbound: whatsappOutbound,
|
||||
outbound: whatsappOutboundStub,
|
||||
messaging: whatsappMessaging,
|
||||
}),
|
||||
source: "test",
|
||||
@@ -155,7 +147,7 @@ describe("resolveOutboundTarget defaultTo config fallback", () => {
|
||||
pluginId: "telegram",
|
||||
plugin: createOutboundTestPlugin({
|
||||
id: "telegram",
|
||||
outbound: telegramOutbound,
|
||||
outbound: telegramOutboundStub,
|
||||
messaging: telegramMessaging,
|
||||
}),
|
||||
source: "test",
|
||||
|
||||
@@ -9,10 +9,7 @@ import { buildPluginSdkEntrySources, pluginSdkEntrypoints } from "./entrypoints.
|
||||
const require = createRequire(import.meta.url);
|
||||
const tsdownModuleUrl = pathToFileURL(require.resolve("tsdown")).href;
|
||||
const bundledRepresentativeEntrypoints = [
|
||||
"index",
|
||||
"runtime",
|
||||
"channel-runtime",
|
||||
"provider-setup",
|
||||
"matrix-runtime-heavy",
|
||||
"windows-spawn",
|
||||
] as const;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,8 +5,7 @@ import {
|
||||
resolveProviderPluginChoice,
|
||||
resolveProviderWizardOptions,
|
||||
} from "../provider-wizard.js";
|
||||
import type { ProviderPlugin } from "../types.js";
|
||||
import { providerContractPluginIds, uniqueProviderContractProviders } from "./registry.js";
|
||||
import type { ProviderAuthMethod, ProviderPlugin } from "../types.js";
|
||||
|
||||
const resolvePluginProvidersMock = vi.fn();
|
||||
|
||||
@@ -14,6 +13,94 @@ vi.mock("../providers.js", () => ({
|
||||
resolvePluginProviders: (...args: unknown[]) => resolvePluginProvidersMock(...args),
|
||||
}));
|
||||
|
||||
function createAuthMethod(
|
||||
params: Pick<ProviderAuthMethod, "id" | "label"> &
|
||||
Partial<Pick<ProviderAuthMethod, "hint" | "wizard">>,
|
||||
): ProviderAuthMethod {
|
||||
return {
|
||||
id: params.id,
|
||||
label: params.label,
|
||||
...(params.hint ? { hint: params.hint } : {}),
|
||||
...(params.wizard ? { wizard: params.wizard } : {}),
|
||||
kind: "api_key",
|
||||
run: async () => ({ profiles: [] }),
|
||||
};
|
||||
}
|
||||
|
||||
const TEST_PROVIDERS: ProviderPlugin[] = [
|
||||
{
|
||||
id: "alpha",
|
||||
label: "Alpha",
|
||||
auth: [
|
||||
createAuthMethod({
|
||||
id: "api-key",
|
||||
label: "API key",
|
||||
wizard: {
|
||||
choiceLabel: "Alpha key",
|
||||
choiceHint: "Use an API key",
|
||||
groupId: "alpha",
|
||||
groupLabel: "Alpha",
|
||||
onboardingScopes: ["text-inference"],
|
||||
},
|
||||
}),
|
||||
createAuthMethod({
|
||||
id: "oauth",
|
||||
label: "OAuth",
|
||||
wizard: {
|
||||
choiceId: "alpha-oauth",
|
||||
choiceLabel: "Alpha OAuth",
|
||||
groupId: "alpha",
|
||||
groupLabel: "Alpha",
|
||||
groupHint: "Recommended",
|
||||
},
|
||||
}),
|
||||
],
|
||||
wizard: {
|
||||
modelPicker: {
|
||||
label: "Alpha custom",
|
||||
hint: "Pick Alpha models",
|
||||
methodId: "oauth",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "beta",
|
||||
label: "Beta",
|
||||
auth: [createAuthMethod({ id: "token", label: "Token" })],
|
||||
wizard: {
|
||||
setup: {
|
||||
choiceLabel: "Beta setup",
|
||||
groupId: "beta",
|
||||
groupLabel: "Beta",
|
||||
},
|
||||
modelPicker: {
|
||||
label: "Beta custom",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "gamma",
|
||||
label: "Gamma",
|
||||
auth: [
|
||||
createAuthMethod({ id: "default", label: "Default auth" }),
|
||||
createAuthMethod({ id: "alt", label: "Alt auth" }),
|
||||
],
|
||||
wizard: {
|
||||
setup: {
|
||||
methodId: "alt",
|
||||
choiceId: "gamma-alt",
|
||||
choiceLabel: "Gamma alt",
|
||||
groupId: "gamma",
|
||||
groupLabel: "Gamma",
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const TEST_PROVIDER_IDS = TEST_PROVIDERS.map((provider) => provider.id).toSorted((left, right) =>
|
||||
left.localeCompare(right),
|
||||
);
|
||||
|
||||
function resolveExpectedWizardChoiceValues(providers: ProviderPlugin[]) {
|
||||
const values: string[] = [];
|
||||
|
||||
@@ -43,6 +130,11 @@ function resolveExpectedWizardChoiceValues(providers: ProviderPlugin[]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (provider.auth.length === 1) {
|
||||
values.push(setup.choiceId?.trim() || provider.id);
|
||||
continue;
|
||||
}
|
||||
|
||||
values.push(
|
||||
...provider.auth.map((method) => buildProviderPluginMethodChoice(provider.id, method.id)),
|
||||
);
|
||||
@@ -73,15 +165,15 @@ function resolveExpectedModelPickerValues(providers: ProviderPlugin[]) {
|
||||
describe("provider wizard contract", () => {
|
||||
beforeEach(() => {
|
||||
resolvePluginProvidersMock.mockReset();
|
||||
resolvePluginProvidersMock.mockReturnValue(uniqueProviderContractProviders);
|
||||
resolvePluginProvidersMock.mockReturnValue(TEST_PROVIDERS);
|
||||
});
|
||||
|
||||
it("exposes every registered provider setup choice through the shared wizard layer", () => {
|
||||
it("exposes every wizard setup choice through the shared wizard layer", () => {
|
||||
const options = resolveProviderWizardOptions({
|
||||
config: {
|
||||
plugins: {
|
||||
enabled: true,
|
||||
allow: providerContractPluginIds,
|
||||
allow: TEST_PROVIDER_IDS,
|
||||
slots: {
|
||||
memory: "none",
|
||||
},
|
||||
@@ -92,7 +184,7 @@ describe("provider wizard contract", () => {
|
||||
|
||||
expect(
|
||||
options.map((option) => option.value).toSorted((left, right) => left.localeCompare(right)),
|
||||
).toEqual(resolveExpectedWizardChoiceValues(uniqueProviderContractProviders));
|
||||
).toEqual(resolveExpectedWizardChoiceValues(TEST_PROVIDERS));
|
||||
expect(options.map((option) => option.value)).toEqual([
|
||||
...new Set(options.map((option) => option.value)),
|
||||
]);
|
||||
@@ -101,7 +193,7 @@ describe("provider wizard contract", () => {
|
||||
it("round-trips every shared wizard choice back to its provider and auth method", () => {
|
||||
for (const option of resolveProviderWizardOptions({ config: {}, env: process.env })) {
|
||||
const resolved = resolveProviderPluginChoice({
|
||||
providers: uniqueProviderContractProviders,
|
||||
providers: TEST_PROVIDERS,
|
||||
choice: option.value,
|
||||
});
|
||||
expect(resolved).not.toBeNull();
|
||||
@@ -110,15 +202,15 @@ describe("provider wizard contract", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("exposes every registered model-picker entry through the shared wizard layer", () => {
|
||||
it("exposes every model-picker entry through the shared wizard layer", () => {
|
||||
const entries = resolveProviderModelPickerEntries({ config: {}, env: process.env });
|
||||
|
||||
expect(
|
||||
entries.map((entry) => entry.value).toSorted((left, right) => left.localeCompare(right)),
|
||||
).toEqual(resolveExpectedModelPickerValues(uniqueProviderContractProviders));
|
||||
).toEqual(resolveExpectedModelPickerValues(TEST_PROVIDERS));
|
||||
for (const entry of entries) {
|
||||
const resolved = resolveProviderPluginChoice({
|
||||
providers: uniqueProviderContractProviders,
|
||||
providers: TEST_PROVIDERS,
|
||||
choice: entry.value,
|
||||
});
|
||||
expect(resolved).not.toBeNull();
|
||||
|
||||
@@ -7,7 +7,7 @@ import type {
|
||||
SessionBindingAdapter,
|
||||
SessionBindingRecord,
|
||||
} from "../infra/outbound/session-binding-service.js";
|
||||
import { createEmptyPluginRegistry } from "./registry.js";
|
||||
import { createEmptyPluginRegistry } from "./registry-empty.js";
|
||||
import { setActivePluginRegistry } from "./runtime.js";
|
||||
|
||||
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-plugin-binding-"));
|
||||
|
||||
Reference in New Issue
Block a user