mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-13 23:56:07 +00:00
fix(runtime): stabilize provider and channel runtime tests
This commit is contained in:
@@ -4,7 +4,27 @@ import { SafeOpenError, openFileWithinRoot } from "../infra/fs-safe.js";
|
||||
import { isNotFoundPathError, isPathInside } from "../infra/path-guards.js";
|
||||
import { resolvePreferredOpenClawTmpDir } from "../infra/tmp-openclaw-dir.js";
|
||||
|
||||
export const DEFAULT_BROWSER_TMP_DIR = resolvePreferredOpenClawTmpDir();
|
||||
const DEFAULT_FALLBACK_BROWSER_TMP_DIR = "/tmp/openclaw";
|
||||
|
||||
function canUseNodeFs(): boolean {
|
||||
const getBuiltinModule = (
|
||||
process as NodeJS.Process & {
|
||||
getBuiltinModule?: (id: string) => unknown;
|
||||
}
|
||||
).getBuiltinModule;
|
||||
if (typeof getBuiltinModule !== "function") {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return getBuiltinModule("fs") !== undefined;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const DEFAULT_BROWSER_TMP_DIR = canUseNodeFs()
|
||||
? resolvePreferredOpenClawTmpDir()
|
||||
: DEFAULT_FALLBACK_BROWSER_TMP_DIR;
|
||||
export const DEFAULT_TRACE_DIR = DEFAULT_BROWSER_TMP_DIR;
|
||||
export const DEFAULT_DOWNLOAD_DIR = path.join(DEFAULT_BROWSER_TMP_DIR, "downloads");
|
||||
export const DEFAULT_UPLOAD_DIR = path.join(DEFAULT_BROWSER_TMP_DIR, "uploads");
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { formatErrorMessage } from "openclaw/plugin-sdk/infra-runtime";
|
||||
import { danger } from "openclaw/plugin-sdk/runtime-env";
|
||||
import { createSubsystemLogger } from "openclaw/plugin-sdk/runtime-env";
|
||||
import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";
|
||||
|
||||
@@ -14,7 +13,6 @@ type TelegramApiLoggingParams<T> = {
|
||||
};
|
||||
|
||||
const fallbackLogger = createSubsystemLogger("telegram/api");
|
||||
const formatDanger = typeof danger === "function" ? danger : (message: string) => message;
|
||||
|
||||
function resolveTelegramApiLogger(runtime?: RuntimeEnv, logger?: TelegramApiLogger) {
|
||||
if (logger) {
|
||||
@@ -39,7 +37,7 @@ export async function withTelegramApiErrorLogging<T>({
|
||||
if (!shouldLog || shouldLog(err)) {
|
||||
const errText = formatErrorMessage(err);
|
||||
const log = resolveTelegramApiLogger(runtime, logger);
|
||||
log(formatDanger(`telegram ${operation} failed: ${errText}`));
|
||||
log(`telegram ${operation} failed: ${errText}`);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
import { bundledChannelPlugins } from "../channels/plugins/bundled.js";
|
||||
import { buildChannelConfigSchema } from "../channels/plugins/config-schema.js";
|
||||
import type {
|
||||
ChannelConfigRuntimeSchema,
|
||||
ChannelConfigSchema,
|
||||
} from "../channels/plugins/types.plugin.js";
|
||||
import { BUNDLED_PLUGIN_METADATA } from "../plugins/bundled-plugin-metadata.js";
|
||||
import { MSTeamsConfigSchema } from "./zod-schema.providers-core.js";
|
||||
import { WhatsAppConfigSchema } from "./zod-schema.providers-whatsapp.js";
|
||||
|
||||
type BundledChannelRuntimeMap = ReadonlyMap<string, ChannelConfigRuntimeSchema>;
|
||||
type BundledChannelConfigSchemaMap = ReadonlyMap<string, ChannelConfigSchema>;
|
||||
|
||||
const bundledChannelRuntimeMap = new Map<string, ChannelConfigRuntimeSchema>();
|
||||
const bundledChannelConfigSchemaMap = new Map<string, ChannelConfigSchema>();
|
||||
const staticBundledChannelSchemas = new Map<string, ChannelConfigSchema>([
|
||||
["msteams", buildChannelConfigSchema(MSTeamsConfigSchema)],
|
||||
["whatsapp", buildChannelConfigSchema(WhatsAppConfigSchema)],
|
||||
]);
|
||||
for (const plugin of bundledChannelPlugins) {
|
||||
const channelSchema = plugin.configSchema;
|
||||
if (!channelSchema) {
|
||||
@@ -35,6 +42,14 @@ for (const entry of BUNDLED_PLUGIN_METADATA) {
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const [channelId, channelSchema] of staticBundledChannelSchemas) {
|
||||
if (!bundledChannelConfigSchemaMap.has(channelId)) {
|
||||
bundledChannelConfigSchemaMap.set(channelId, channelSchema);
|
||||
}
|
||||
if (channelSchema.runtime && !bundledChannelRuntimeMap.has(channelId)) {
|
||||
bundledChannelRuntimeMap.set(channelId, channelSchema.runtime);
|
||||
}
|
||||
}
|
||||
|
||||
export function getBundledChannelRuntimeMap(): BundledChannelRuntimeMap {
|
||||
return bundledChannelRuntimeMap;
|
||||
|
||||
@@ -160,10 +160,23 @@ const providerRuntimeMocks = vi.hoisted(() => ({
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/provider-runtime.js", () => ({
|
||||
...providerRuntimeMocks.providerRuntimeMock,
|
||||
resolveProviderUsageAuthWithPlugin: providerRuntimeMocks.resolveProviderUsageAuthWithPluginMock,
|
||||
}));
|
||||
vi.mock("../plugins/provider-runtime.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../plugins/provider-runtime.js")>();
|
||||
return {
|
||||
...actual,
|
||||
...providerRuntimeMocks.providerRuntimeMock,
|
||||
resolveProviderUsageAuthWithPlugin: providerRuntimeMocks.resolveProviderUsageAuthWithPluginMock,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../plugins/provider-runtime.ts", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../plugins/provider-runtime.ts")>();
|
||||
return {
|
||||
...actual,
|
||||
...providerRuntimeMocks.providerRuntimeMock,
|
||||
resolveProviderUsageAuthWithPlugin: providerRuntimeMocks.resolveProviderUsageAuthWithPluginMock,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../agents/cli-credentials.js", () => ({
|
||||
readCodexCliCredentialsCached: () => null,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { danger, info, logVerboseConsole, success, warn } from "./globals.js";
|
||||
import { isVerbose } from "./global-state.js";
|
||||
import { getLogger } from "./logging/logger.js";
|
||||
import { createSubsystemLogger } from "./logging/subsystem.js";
|
||||
import { defaultRuntime, type RuntimeEnv } from "./runtime.js";
|
||||
import { theme } from "./terminal/theme.js";
|
||||
|
||||
const subsystemPrefixRe = /^([a-z][a-z0-9-]{1,20}):\s+(.*)$/i;
|
||||
|
||||
@@ -34,6 +35,11 @@ function logWithSubsystem(params: {
|
||||
getLogger()[params.loggerMethod](params.message);
|
||||
}
|
||||
|
||||
const info = theme.info;
|
||||
const warn = theme.warn;
|
||||
const success = theme.success;
|
||||
const danger = theme.error;
|
||||
|
||||
export function logInfo(message: string, runtime: RuntimeEnv = defaultRuntime) {
|
||||
logWithSubsystem({
|
||||
message,
|
||||
@@ -81,5 +87,7 @@ export function logError(message: string, runtime: RuntimeEnv = defaultRuntime)
|
||||
export function logDebug(message: string) {
|
||||
// Always emit to file logger (level-filtered); console only when verbose.
|
||||
getLogger().debug(message);
|
||||
logVerboseConsole(message);
|
||||
if (isVerbose()) {
|
||||
console.log(theme.muted(message));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,10 +39,15 @@ type AttachmentCacheEntry = {
|
||||
tempCleanup?: () => Promise<void>;
|
||||
};
|
||||
|
||||
const DEFAULT_LOCAL_PATH_ROOTS = mergeInboundPathRoots(
|
||||
getDefaultMediaLocalRoots(),
|
||||
DEFAULT_IMESSAGE_ATTACHMENT_ROOTS,
|
||||
);
|
||||
let defaultLocalPathRoots: readonly string[] | undefined;
|
||||
|
||||
function getDefaultLocalPathRoots(): readonly string[] {
|
||||
defaultLocalPathRoots ??= mergeInboundPathRoots(
|
||||
getDefaultMediaLocalRoots(),
|
||||
DEFAULT_IMESSAGE_ATTACHMENT_ROOTS,
|
||||
);
|
||||
return defaultLocalPathRoots;
|
||||
}
|
||||
|
||||
export type MediaAttachmentCacheOptions = {
|
||||
localPathRoots?: readonly string[];
|
||||
@@ -66,7 +71,10 @@ export class MediaAttachmentCache {
|
||||
|
||||
constructor(attachments: MediaAttachment[], options?: MediaAttachmentCacheOptions) {
|
||||
this.attachments = attachments;
|
||||
this.localPathRoots = mergeInboundPathRoots(options?.localPathRoots, DEFAULT_LOCAL_PATH_ROOTS);
|
||||
this.localPathRoots = mergeInboundPathRoots(
|
||||
options?.localPathRoots,
|
||||
getDefaultLocalPathRoots(),
|
||||
);
|
||||
for (const attachment of attachments) {
|
||||
this.entries.set(attachment.index, { attachment });
|
||||
}
|
||||
|
||||
@@ -273,8 +273,10 @@ function collectCoreSourceFiles(): string[] {
|
||||
rootDir: srcDir,
|
||||
shouldSkipEntry: ({ entryName, normalizedFullPath }) =>
|
||||
normalizedFullPath.includes(".test.") ||
|
||||
normalizedFullPath.includes(".test-utils.") ||
|
||||
normalizedFullPath.includes(".test-harness.") ||
|
||||
normalizedFullPath.includes(".test-helpers.") ||
|
||||
entryName.endsWith("-test-helpers.ts") ||
|
||||
entryName === "test-manager-helpers.ts" ||
|
||||
normalizedFullPath.includes(".mock-harness.") ||
|
||||
normalizedFullPath.includes(".suite.") ||
|
||||
|
||||
@@ -142,7 +142,6 @@ function parseDiscordBindingTarget(raw: string | undefined): {
|
||||
}
|
||||
return /^\d+$/.test(normalized.trim()) ? { conversationId: `user:${normalized.trim()}` } : null;
|
||||
}
|
||||
|
||||
function resolveBindingConversationFromCommand(params: {
|
||||
channel: string;
|
||||
from?: string;
|
||||
@@ -175,9 +174,12 @@ function resolveBindingConversationFromCommand(params: {
|
||||
};
|
||||
}
|
||||
if (params.channel === "discord") {
|
||||
const source = params.from ?? params.to;
|
||||
const source =
|
||||
params.to?.startsWith("slash:") || !params.to?.trim()
|
||||
? (params.from ?? params.to)
|
||||
: params.to;
|
||||
const rawTarget = source?.startsWith("discord:") ? stripPrefix(source, "discord:") : source;
|
||||
if (!rawTarget) {
|
||||
if (!rawTarget || rawTarget.startsWith("slash:")) {
|
||||
return null;
|
||||
}
|
||||
const target =
|
||||
@@ -188,10 +190,7 @@ function resolveBindingConversationFromCommand(params: {
|
||||
return {
|
||||
channel: "discord",
|
||||
accountId,
|
||||
conversationId:
|
||||
"conversationId" in target
|
||||
? target.conversationId
|
||||
: `${target.chatType === "direct" ? "user" : "channel"}:${target.to}`,
|
||||
conversationId: target.conversationId,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -34,18 +34,23 @@ describe("buildPublishedInstallScenarios", () => {
|
||||
|
||||
describe("collectInstalledPackageErrors", () => {
|
||||
it("flags version mismatches and missing runtime sidecars", () => {
|
||||
expect(
|
||||
collectInstalledPackageErrors({
|
||||
expectedVersion: "2026.3.23-2",
|
||||
installedVersion: "2026.3.23",
|
||||
packageRoot: "/tmp/empty-openclaw",
|
||||
}),
|
||||
).toEqual([
|
||||
const errors = collectInstalledPackageErrors({
|
||||
expectedVersion: "2026.3.23-2",
|
||||
installedVersion: "2026.3.23",
|
||||
packageRoot: "/tmp/empty-openclaw",
|
||||
});
|
||||
|
||||
expect(errors[0]).toBe(
|
||||
"installed package version mismatch: expected 2026.3.23-2, found 2026.3.23.",
|
||||
...BUNDLED_RUNTIME_SIDECAR_PATHS.map(
|
||||
(relativePath) =>
|
||||
`installed package is missing required bundled runtime sidecar: ${relativePath}`,
|
||||
);
|
||||
expect(errors).toEqual(
|
||||
expect.arrayContaining(
|
||||
BUNDLED_RUNTIME_SIDECAR_PATHS.map(
|
||||
(relativePath) =>
|
||||
`installed package is missing required bundled runtime sidecar: ${relativePath}`,
|
||||
),
|
||||
),
|
||||
]);
|
||||
);
|
||||
expect(errors.length).toBeGreaterThanOrEqual(1 + BUNDLED_RUNTIME_SIDECAR_PATHS.length);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user