mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:30:42 +00:00
refactor: remove unused test helpers
This commit is contained in:
@@ -137,14 +137,6 @@ export function getGatewayRequests(): Array<GatewayRequest> {
|
||||
return getCallGatewayMock().mock.calls.map((call: unknown[]) => call[0] as GatewayRequest);
|
||||
}
|
||||
|
||||
export function getGatewayMethods(): Array<string | undefined> {
|
||||
return getGatewayRequests().map((request) => request.method);
|
||||
}
|
||||
|
||||
export function findGatewayRequest(method: string): GatewayRequest | undefined {
|
||||
return getGatewayRequests().find((request) => request.method === method);
|
||||
}
|
||||
|
||||
export async function waitForSessionsSpawnEvent(
|
||||
label: string,
|
||||
predicate: () => boolean,
|
||||
|
||||
@@ -1,91 +1,4 @@
|
||||
import fs from "node:fs/promises";
|
||||
import http from "node:http";
|
||||
import { createRequire } from "node:module";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import {
|
||||
writeBundleProbeMcpServer,
|
||||
writeClaudeBundle,
|
||||
writeExecutable,
|
||||
} from "./bundle-mcp-shared.test-harness.js";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const SDK_SERVER_MCP_PATH = require.resolve("@modelcontextprotocol/sdk/server/mcp.js");
|
||||
const SDK_SERVER_SSE_PATH = require.resolve("@modelcontextprotocol/sdk/server/sse.js");
|
||||
|
||||
const tempDirs: string[] = [];
|
||||
|
||||
export async function cleanupBundleMcpHarness(): Promise<void> {
|
||||
const { __testing } = await import("./pi-bundle-mcp-tools.js");
|
||||
await __testing.resetSessionMcpRuntimeManager();
|
||||
await Promise.all(tempDirs.splice(0).map((dir) => fs.rm(dir, { recursive: true, force: true })));
|
||||
}
|
||||
|
||||
export async function makeTempDir(prefix: string): Promise<string> {
|
||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), prefix));
|
||||
tempDirs.push(dir);
|
||||
return dir;
|
||||
}
|
||||
|
||||
export { writeBundleProbeMcpServer, writeClaudeBundle, writeExecutable };
|
||||
|
||||
export async function waitForFileText(filePath: string, timeoutMs = 5_000): Promise<string> {
|
||||
const start = Date.now();
|
||||
while (Date.now() - start < timeoutMs) {
|
||||
const content = await fs.readFile(filePath, "utf8").catch(() => undefined);
|
||||
if (content != null) {
|
||||
return content;
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, 5));
|
||||
}
|
||||
throw new Error(`Timed out waiting for ${filePath}`);
|
||||
}
|
||||
|
||||
export async function startSseProbeServer(
|
||||
probeText = "FROM-SSE",
|
||||
): Promise<{ port: number; close: () => Promise<void> }> {
|
||||
const { McpServer } = await import(SDK_SERVER_MCP_PATH);
|
||||
const { SSEServerTransport } = await import(SDK_SERVER_SSE_PATH);
|
||||
|
||||
const mcpServer = new McpServer({ name: "sse-probe", version: "1.0.0" });
|
||||
mcpServer.tool("sse_probe", "SSE MCP probe", async () => {
|
||||
return {
|
||||
content: [{ type: "text", text: probeText }],
|
||||
};
|
||||
});
|
||||
|
||||
let sseTransport:
|
||||
| {
|
||||
handlePostMessage: (req: http.IncomingMessage, res: http.ServerResponse) => Promise<void>;
|
||||
}
|
||||
| undefined;
|
||||
const httpServer = http.createServer(async (req, res) => {
|
||||
if (req.url === "/sse") {
|
||||
sseTransport = new SSEServerTransport("/messages", res);
|
||||
await mcpServer.connect(sseTransport);
|
||||
} else if (req.url?.startsWith("/messages") && req.method === "POST") {
|
||||
if (sseTransport) {
|
||||
await sseTransport.handlePostMessage(req, res);
|
||||
} else {
|
||||
res.writeHead(400).end("No SSE session");
|
||||
}
|
||||
} else {
|
||||
res.writeHead(404).end();
|
||||
}
|
||||
});
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
httpServer.listen(0, "127.0.0.1", resolve);
|
||||
});
|
||||
const address = httpServer.address();
|
||||
const port = typeof address === "object" && address ? address.port : 0;
|
||||
|
||||
return {
|
||||
port,
|
||||
close: async () => {
|
||||
await new Promise<void>((resolve, reject) =>
|
||||
httpServer.close((error) => (error ? reject(error) : resolve())),
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,66 +1,7 @@
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { createTempHomeEnv } from "../test-utils/temp-home.js";
|
||||
|
||||
export function setTempStateDir(workspaceDir: string): string {
|
||||
const stateDir = path.join(workspaceDir, "state");
|
||||
process.env.OPENCLAW_STATE_DIR = stateDir;
|
||||
return stateDir;
|
||||
}
|
||||
|
||||
export async function withTempWorkspace(
|
||||
run: (params: { workspaceDir: string; stateDir: string }) => Promise<void>,
|
||||
) {
|
||||
const tempHome = await createTempHomeEnv("openclaw-skills-install-home-");
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-skills-install-"));
|
||||
try {
|
||||
const stateDir = setTempStateDir(workspaceDir);
|
||||
await run({ workspaceDir, stateDir });
|
||||
} finally {
|
||||
await fs.rm(workspaceDir, { recursive: true, force: true }).catch(() => undefined);
|
||||
await tempHome.restore();
|
||||
}
|
||||
}
|
||||
|
||||
export async function writeDownloadSkill(params: {
|
||||
workspaceDir: string;
|
||||
name: string;
|
||||
installId: string;
|
||||
url: string;
|
||||
archive: "tar.gz" | "tar.bz2" | "zip";
|
||||
stripComponents?: number;
|
||||
targetDir: string;
|
||||
}): Promise<string> {
|
||||
const skillDir = path.join(params.workspaceDir, "skills", params.name);
|
||||
await fs.mkdir(skillDir, { recursive: true });
|
||||
const meta = {
|
||||
openclaw: {
|
||||
install: [
|
||||
{
|
||||
id: params.installId,
|
||||
kind: "download",
|
||||
url: params.url,
|
||||
archive: params.archive,
|
||||
extract: true,
|
||||
stripComponents: params.stripComponents,
|
||||
targetDir: params.targetDir,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
await fs.writeFile(
|
||||
path.join(skillDir, "SKILL.md"),
|
||||
`---
|
||||
name: ${params.name}
|
||||
description: test skill
|
||||
metadata: ${JSON.stringify(meta)}
|
||||
---
|
||||
|
||||
# ${params.name}
|
||||
`,
|
||||
"utf-8",
|
||||
);
|
||||
await fs.writeFile(path.join(skillDir, "runner.js"), "export {};\n", "utf-8");
|
||||
return skillDir;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { resetAnnounceQueuesForTests } from "./subagent-announce-queue.js";
|
||||
import { subagentRuns } from "./subagent-registry-memory.js";
|
||||
import { listRunsForRequesterFromRuns } from "./subagent-registry-queries.js";
|
||||
import type { SubagentRunRecord } from "./subagent-registry.types.js";
|
||||
|
||||
export function resetSubagentRegistryForTests() {
|
||||
@@ -11,10 +10,3 @@ export function resetSubagentRegistryForTests() {
|
||||
export function addSubagentRunForTests(entry: SubagentRunRecord) {
|
||||
subagentRuns.set(entry.runId, entry);
|
||||
}
|
||||
|
||||
export function listSubagentRunsForRequester(
|
||||
requesterSessionKey: string,
|
||||
options?: { requesterRunId?: string },
|
||||
) {
|
||||
return listRunsForRequesterFromRuns(subagentRuns, requesterSessionKey, options);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,3 @@ export function createSuccessfulAudioMediaDecision() {
|
||||
export function createSuccessfulImageMediaDecision() {
|
||||
return createSuccessfulMediaDecision("image");
|
||||
}
|
||||
|
||||
export function createSuccessfulVideoMediaDecision() {
|
||||
return createSuccessfulMediaDecision("video");
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import path from "node:path";
|
||||
import { withTempHome as withTempHomeBase } from "openclaw/plugin-sdk/test-env";
|
||||
import { afterEach, beforeEach, expect, vi } from "vitest";
|
||||
import { afterEach, beforeEach, vi } from "vitest";
|
||||
import { clearRuntimeAuthProfileStoreSnapshots } from "../agents/auth-profiles.js";
|
||||
import { resetSkillsRefreshForTest } from "../agents/skills/refresh.js";
|
||||
import { clearSessionStoreCacheForTest, loadSessionStore } from "../config/sessions.js";
|
||||
import { clearSessionStoreCacheForTest } from "../config/sessions.js";
|
||||
import { resetSystemEventsForTest } from "../infra/system-events.js";
|
||||
import { createEmptyPluginRegistry } from "../plugins/registry-empty.js";
|
||||
import type { PluginProviderRegistration } from "../plugins/registry.js";
|
||||
@@ -22,12 +20,8 @@ import {
|
||||
runPreparedReplyMock,
|
||||
runReplyAgentMock,
|
||||
} from "./reply.directive.directive-behavior.e2e-mocks.js";
|
||||
import { withFastReplyConfig, withFullRuntimeReplyConfig } from "./reply/get-reply-fast-path.js";
|
||||
|
||||
export const MAIN_SESSION_KEY = "agent:main:main";
|
||||
type RunPreparedReply = typeof import("./reply/get-reply-run.js").runPreparedReply;
|
||||
|
||||
export const DEFAULT_TEST_MODEL_CATALOG: Array<{
|
||||
const DEFAULT_TEST_MODEL_CATALOG: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
provider: string;
|
||||
@@ -44,8 +38,6 @@ export const DEFAULT_TEST_MODEL_CATALOG: Array<{
|
||||
{ id: "gpt-4.1-mini", name: "GPT-4.1 Mini", provider: "openai" },
|
||||
];
|
||||
|
||||
export type ReplyPayloadText = { text?: string | null } | null | undefined;
|
||||
|
||||
const OPENAI_XHIGH_MODEL_IDS = [
|
||||
"gpt-5.4",
|
||||
"gpt-5.4-pro",
|
||||
@@ -97,116 +89,6 @@ function createDirectiveBehaviorProviderRegistry(): ReturnType<typeof createEmpt
|
||||
return registry;
|
||||
}
|
||||
|
||||
export function replyText(res: ReplyPayloadText | ReplyPayloadText[]): string | undefined {
|
||||
if (Array.isArray(res)) {
|
||||
return typeof res[0]?.text === "string" ? res[0]?.text : undefined;
|
||||
}
|
||||
return typeof res?.text === "string" ? res.text : undefined;
|
||||
}
|
||||
|
||||
export function replyTexts(res: ReplyPayloadText | ReplyPayloadText[]): string[] {
|
||||
const payloads = Array.isArray(res) ? res : [res];
|
||||
return payloads
|
||||
.map((entry) => (typeof entry?.text === "string" ? entry.text : undefined))
|
||||
.filter((value): value is string => Boolean(value));
|
||||
}
|
||||
|
||||
export function makeEmbeddedTextResult(text = "done") {
|
||||
return {
|
||||
payloads: [{ text }],
|
||||
meta: {
|
||||
durationMs: 5,
|
||||
agentMeta: { sessionId: "s", provider: "p", model: "m" },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function mockEmbeddedTextResult(text = "done") {
|
||||
runEmbeddedPiAgentMock.mockResolvedValue(makeEmbeddedTextResult(text));
|
||||
}
|
||||
|
||||
export async function withTempHome<T>(fn: (home: string) => Promise<T>): Promise<T> {
|
||||
return withTempHomeBase(
|
||||
async (home) => {
|
||||
return await fn(home);
|
||||
},
|
||||
{
|
||||
env: {
|
||||
OPENCLAW_AGENT_DIR: (home) => path.join(home, ".openclaw", "agent"),
|
||||
PI_CODING_AGENT_DIR: (home) => path.join(home, ".openclaw", "agent"),
|
||||
},
|
||||
prefix: "openclaw-reply-",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export function sessionStorePath(home: string): string {
|
||||
return path.join(home, "sessions.json");
|
||||
}
|
||||
|
||||
export function makeWhatsAppDirectiveConfig(
|
||||
home: string,
|
||||
defaults: Record<string, unknown>,
|
||||
extra: Record<string, unknown> = {},
|
||||
) {
|
||||
return withFastReplyConfig({
|
||||
agents: {
|
||||
defaults: {
|
||||
workspace: path.join(home, "openclaw"),
|
||||
...defaults,
|
||||
},
|
||||
},
|
||||
channels: { whatsapp: { allowFrom: ["*"] } },
|
||||
session: { store: sessionStorePath(home) },
|
||||
...extra,
|
||||
});
|
||||
}
|
||||
|
||||
export const AUTHORIZED_WHATSAPP_COMMAND = {
|
||||
From: "+1222",
|
||||
To: "+1222",
|
||||
Provider: "whatsapp",
|
||||
SenderE164: "+1222",
|
||||
CommandAuthorized: true,
|
||||
} as const;
|
||||
|
||||
export function makeElevatedDirectiveConfig(home: string) {
|
||||
return makeWhatsAppDirectiveConfig(
|
||||
home,
|
||||
{
|
||||
model: "anthropic/claude-opus-4-6",
|
||||
elevatedDefault: "on",
|
||||
},
|
||||
{
|
||||
tools: {
|
||||
elevated: {
|
||||
allowFrom: { whatsapp: ["+1222"] },
|
||||
},
|
||||
},
|
||||
channels: { whatsapp: { allowFrom: ["+1222"] } },
|
||||
session: { store: sessionStorePath(home) },
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export function assertModelSelection(
|
||||
storePath: string,
|
||||
selection: { model?: string; provider?: string } = {},
|
||||
) {
|
||||
const store = loadSessionStore(storePath);
|
||||
const entry = store[MAIN_SESSION_KEY];
|
||||
expect(entry).toBeDefined();
|
||||
expect(entry?.modelOverride).toBe(selection.model);
|
||||
expect(entry?.providerOverride).toBe(selection.provider);
|
||||
}
|
||||
|
||||
export function assertElevatedOffStatusReply(text: string | undefined) {
|
||||
expect(text).toContain("Elevated mode disabled.");
|
||||
const optionsLine = text?.split("\n").find((line) => line.trim().startsWith("⚙️"));
|
||||
expect(optionsLine).toBeTruthy();
|
||||
expect(optionsLine).not.toContain("elevated");
|
||||
}
|
||||
|
||||
export function installDirectiveBehaviorE2EHooks() {
|
||||
beforeEach(async () => {
|
||||
await resetSkillsRefreshForTest();
|
||||
@@ -246,97 +128,3 @@ export function installDirectiveBehaviorE2EHooks() {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
}
|
||||
|
||||
export function installFreshDirectiveBehaviorReplyMocks(params?: {
|
||||
onActualRunPreparedReply?: (runPreparedReply: RunPreparedReply) => void;
|
||||
runPreparedReply?: (...args: Parameters<RunPreparedReply>) => unknown;
|
||||
}) {
|
||||
vi.doMock("../agents/pi-embedded.js", () => ({
|
||||
abortEmbeddedPiRun: vi.fn().mockReturnValue(false),
|
||||
compactEmbeddedPiSession: (...args: unknown[]) => compactEmbeddedPiSessionMock(...args),
|
||||
runEmbeddedPiAgent: (...args: unknown[]) => runEmbeddedPiAgentMock(...args),
|
||||
queueEmbeddedPiMessage: vi.fn().mockReturnValue(false),
|
||||
resolveEmbeddedSessionLane: (key: string) => `session:${key.trim() || "main"}`,
|
||||
isEmbeddedPiRunActive: vi.fn().mockReturnValue(false),
|
||||
isEmbeddedPiRunStreaming: vi.fn().mockReturnValue(false),
|
||||
}));
|
||||
vi.doMock("../agents/pi-embedded.runtime.js", () => ({
|
||||
abortEmbeddedPiRun: vi.fn().mockReturnValue(false),
|
||||
compactEmbeddedPiSession: (...args: unknown[]) => compactEmbeddedPiSessionMock(...args),
|
||||
runEmbeddedPiAgent: (...args: unknown[]) => runEmbeddedPiAgentMock(...args),
|
||||
queueEmbeddedPiMessage: vi.fn().mockReturnValue(false),
|
||||
resolveActiveEmbeddedRunSessionId: vi.fn().mockReturnValue(undefined),
|
||||
resolveEmbeddedSessionLane: (key: string) => `session:${key.trim() || "main"}`,
|
||||
isEmbeddedPiRunActive: vi.fn().mockReturnValue(false),
|
||||
isEmbeddedPiRunStreaming: vi.fn().mockReturnValue(false),
|
||||
waitForEmbeddedPiRunEnd: vi.fn().mockResolvedValue(true),
|
||||
}));
|
||||
vi.doMock("../agents/model-catalog.js", () => ({
|
||||
loadModelCatalog: loadModelCatalogMock,
|
||||
}));
|
||||
vi.doMock("../cli/command-secret-gateway.js", () => ({
|
||||
resolveCommandSecretRefsViaGateway: (...args: unknown[]) =>
|
||||
resolveCommandSecretRefsViaGatewayMock(...args),
|
||||
}));
|
||||
vi.doMock("../agents/auth-profiles/session-override.js", () => ({
|
||||
clearSessionAuthProfileOverride: (...args: unknown[]) =>
|
||||
clearSessionAuthProfileOverrideMock(...args),
|
||||
resolveSessionAuthProfileOverride: (...args: unknown[]) =>
|
||||
resolveSessionAuthProfileOverrideMock(...args),
|
||||
}));
|
||||
vi.doMock("../plugins/hook-runner-global.js", () => ({
|
||||
getGlobalHookRunner: () => undefined,
|
||||
}));
|
||||
vi.doMock("./reply/agent-runner.runtime.js", () => ({
|
||||
runReplyAgent: (...args: unknown[]) => runReplyAgentMock(...args),
|
||||
}));
|
||||
vi.doMock("./reply/get-reply-run.js", () => ({
|
||||
runPreparedReply: (...args: unknown[]) => runPreparedReplyMock(...args),
|
||||
}));
|
||||
if (params?.runPreparedReply || params?.onActualRunPreparedReply) {
|
||||
if (params.runPreparedReply && !params.onActualRunPreparedReply) {
|
||||
vi.doMock("./reply/get-reply-run.js", () => ({
|
||||
runPreparedReply: (...args: Parameters<RunPreparedReply>) =>
|
||||
params.runPreparedReply?.(...args),
|
||||
}));
|
||||
return;
|
||||
}
|
||||
vi.doMock("./reply/get-reply-run.js", async () => {
|
||||
const actual = await vi.importActual<typeof import("./reply/get-reply-run.js")>(
|
||||
"./reply/get-reply-run.js",
|
||||
);
|
||||
params.onActualRunPreparedReply?.(actual.runPreparedReply);
|
||||
return {
|
||||
...actual,
|
||||
runPreparedReply: (...args: Parameters<RunPreparedReply>) =>
|
||||
params.runPreparedReply?.(...args),
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function makeRestrictedElevatedDisabledConfig(home: string) {
|
||||
return withFullRuntimeReplyConfig({
|
||||
agents: {
|
||||
defaults: {
|
||||
model: "anthropic/claude-opus-4-6",
|
||||
workspace: path.join(home, "openclaw"),
|
||||
},
|
||||
list: [
|
||||
{
|
||||
id: "restricted",
|
||||
tools: {
|
||||
elevated: { enabled: false },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
tools: {
|
||||
elevated: {
|
||||
allowFrom: { whatsapp: ["+1222"] },
|
||||
},
|
||||
},
|
||||
channels: { whatsapp: { allowFrom: ["+1222"] } },
|
||||
session: { store: path.join(home, "sessions.json") },
|
||||
} as const);
|
||||
}
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
import path from "node:path";
|
||||
import { withTempHome as withTempHomeBase } from "openclaw/plugin-sdk/test-env";
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
|
||||
type AgentDefaultConfig = NonNullable<NonNullable<OpenClawConfig["agents"]>["defaults"]>;
|
||||
type LoadConfigMock = {
|
||||
mockReturnValue(value: OpenClawConfig): unknown;
|
||||
};
|
||||
|
||||
export async function withAgentCommandTempHome<T>(
|
||||
prefix: string,
|
||||
fn: (home: string) => Promise<T>,
|
||||
): Promise<T> {
|
||||
return withTempHomeBase(fn, { prefix });
|
||||
}
|
||||
|
||||
export function mockAgentCommandConfig(
|
||||
configSpy: LoadConfigMock,
|
||||
home: string,
|
||||
storePath: string,
|
||||
agentOverrides?: Partial<AgentDefaultConfig>,
|
||||
): OpenClawConfig {
|
||||
const cfg = {
|
||||
agents: {
|
||||
defaults: {
|
||||
model: { primary: "anthropic/claude-opus-4-6" },
|
||||
models: { "anthropic/claude-opus-4-6": {} },
|
||||
workspace: path.join(home, "openclaw"),
|
||||
...agentOverrides,
|
||||
},
|
||||
},
|
||||
session: { store: storePath, mainKey: "main" },
|
||||
} as OpenClawConfig;
|
||||
configSpy.mockReturnValue(cfg);
|
||||
return cfg;
|
||||
}
|
||||
|
||||
export function createDefaultAgentCommandResult() {
|
||||
return {
|
||||
payloads: [{ text: "ok" }],
|
||||
meta: {
|
||||
durationMs: 5,
|
||||
agentMeta: { sessionId: "s", provider: "p", model: "m" },
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -44,14 +44,8 @@ vi.mock("./agents.command-shared.js", () => ({
|
||||
|
||||
export const runtime = createTestRuntime();
|
||||
|
||||
let agentsCommandModulePromise: Promise<typeof import("./agents.js")> | undefined;
|
||||
let agentsBindCommandModulePromise: Promise<typeof import("./agents.commands.bind.js")> | undefined;
|
||||
|
||||
export async function loadFreshAgentsCommandModuleForTest() {
|
||||
agentsCommandModulePromise ??= import("./agents.js");
|
||||
return await agentsCommandModulePromise;
|
||||
}
|
||||
|
||||
export async function loadFreshAgentsBindCommandModuleForTest() {
|
||||
agentsBindCommandModulePromise ??= import("./agents.commands.bind.js");
|
||||
return await agentsBindCommandModulePromise;
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import type { WizardPrompter } from "../wizard/prompts.js";
|
||||
import { ensureModelAllowlistEntry } from "./model-allowlist.js";
|
||||
|
||||
export async function applyDefaultModelChoice(params: {
|
||||
config: OpenClawConfig;
|
||||
setDefaultModel: boolean;
|
||||
defaultModel: string;
|
||||
applyDefaultConfig: (config: OpenClawConfig) => OpenClawConfig;
|
||||
applyProviderConfig: (config: OpenClawConfig) => OpenClawConfig;
|
||||
noteDefault?: string;
|
||||
noteAgentModel: (model: string) => Promise<void>;
|
||||
prompter: WizardPrompter;
|
||||
}): Promise<{ config: OpenClawConfig; agentModelOverride?: string }> {
|
||||
if (params.setDefaultModel) {
|
||||
const next = params.applyDefaultConfig(params.config);
|
||||
if (params.noteDefault) {
|
||||
await params.prompter.note(`Default model set to ${params.noteDefault}`, "Model configured");
|
||||
}
|
||||
return { config: next };
|
||||
}
|
||||
|
||||
const next = params.applyProviderConfig(params.config);
|
||||
const nextWithModel = ensureModelAllowlistEntry({
|
||||
cfg: next,
|
||||
modelRef: params.defaultModel,
|
||||
});
|
||||
await params.noteAgentModel(params.defaultModel);
|
||||
return { config: nextWithModel, agentModelOverride: params.defaultModel };
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export { ensureModelAllowlistEntry } from "../plugins/provider-model-allowlist.js";
|
||||
@@ -1,4 +1,3 @@
|
||||
import fs from "node:fs/promises";
|
||||
import type { RuntimeEnv } from "../runtime.js";
|
||||
|
||||
type RuntimeLike = Pick<RuntimeEnv, "log" | "error" | "exit">;
|
||||
@@ -9,13 +8,6 @@ export type NonInteractiveRuntime = {
|
||||
exit: RuntimeLike["exit"];
|
||||
};
|
||||
|
||||
const NON_INTERACTIVE_DEFAULT_OPTIONS = {
|
||||
nonInteractive: true,
|
||||
skipHealth: true,
|
||||
skipChannels: true,
|
||||
json: true,
|
||||
} as const;
|
||||
|
||||
export function createThrowingRuntime(): NonInteractiveRuntime {
|
||||
return {
|
||||
log: () => {},
|
||||
@@ -27,28 +19,3 @@ export function createThrowingRuntime(): NonInteractiveRuntime {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export async function runNonInteractiveSetup(
|
||||
options: Record<string, unknown>,
|
||||
runtime: NonInteractiveRuntime,
|
||||
): Promise<void> {
|
||||
const { runNonInteractiveSetup: run } = await import("./onboard-non-interactive.js");
|
||||
await run(options, runtime);
|
||||
}
|
||||
|
||||
export async function runNonInteractiveSetupWithDefaults(
|
||||
runtime: NonInteractiveRuntime,
|
||||
options: Record<string, unknown>,
|
||||
): Promise<void> {
|
||||
await runNonInteractiveSetup(
|
||||
{
|
||||
...NON_INTERACTIVE_DEFAULT_OPTIONS,
|
||||
...options,
|
||||
},
|
||||
runtime,
|
||||
);
|
||||
}
|
||||
|
||||
export async function readJsonFile<T>(filePath: string): Promise<T> {
|
||||
return JSON.parse(await fs.readFile(filePath, "utf8")) as T;
|
||||
}
|
||||
|
||||
@@ -1,24 +1,9 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { OpenClawConfig } from "../config/config.js";
|
||||
import {
|
||||
applyOpencodeZenModelDefault,
|
||||
OPENCODE_ZEN_DEFAULT_MODEL,
|
||||
} from "../plugin-sdk/opencode.js";
|
||||
import type { WizardPrompter } from "../wizard/prompts.js";
|
||||
import { applyDefaultModelChoice } from "./auth-choice.default-model.js";
|
||||
|
||||
function makePrompter(): WizardPrompter {
|
||||
return {
|
||||
intro: async () => {},
|
||||
outro: async () => {},
|
||||
note: async () => {},
|
||||
select: (async <T>() => "" as T) as WizardPrompter["select"],
|
||||
multiselect: (async <T>() => [] as T[]) as WizardPrompter["multiselect"],
|
||||
text: async () => "",
|
||||
confirm: async () => false,
|
||||
progress: () => ({ update: () => {}, stop: () => {} }),
|
||||
};
|
||||
}
|
||||
|
||||
function expectPrimaryModelChanged(
|
||||
applied: { changed: boolean; next: OpenClawConfig },
|
||||
@@ -36,66 +21,6 @@ function expectConfigUnchanged(
|
||||
expect(applied.next).toEqual(cfg);
|
||||
}
|
||||
|
||||
describe("applyDefaultModelChoice", () => {
|
||||
it("ensures allowlist entry exists when returning an agent override", async () => {
|
||||
const defaultModel = "vercel-ai-gateway/anthropic/claude-opus-4.6";
|
||||
const noteAgentModel = vi.fn(async () => {});
|
||||
const applied = await applyDefaultModelChoice({
|
||||
config: {},
|
||||
setDefaultModel: false,
|
||||
defaultModel,
|
||||
// Simulate a provider function that does not explicitly add the entry.
|
||||
applyProviderConfig: (config: OpenClawConfig) => config,
|
||||
applyDefaultConfig: (config: OpenClawConfig) => config,
|
||||
noteAgentModel,
|
||||
prompter: makePrompter(),
|
||||
});
|
||||
|
||||
expect(noteAgentModel).toHaveBeenCalledWith(defaultModel);
|
||||
expect(applied.agentModelOverride).toBe(defaultModel);
|
||||
expect(applied.config.agents?.defaults?.models?.[defaultModel]).toEqual({});
|
||||
});
|
||||
|
||||
it("adds canonical allowlist key for anthropic aliases", async () => {
|
||||
const defaultModel = "anthropic/opus-4.6";
|
||||
const applied = await applyDefaultModelChoice({
|
||||
config: {},
|
||||
setDefaultModel: false,
|
||||
defaultModel,
|
||||
applyProviderConfig: (config: OpenClawConfig) => config,
|
||||
applyDefaultConfig: (config: OpenClawConfig) => config,
|
||||
noteAgentModel: async () => {},
|
||||
prompter: makePrompter(),
|
||||
});
|
||||
|
||||
expect(applied.config.agents?.defaults?.models?.[defaultModel]).toEqual({});
|
||||
expect(applied.config.agents?.defaults?.models?.["anthropic/claude-opus-4-6"]).toEqual({});
|
||||
});
|
||||
|
||||
it("uses applyDefaultConfig path when setDefaultModel is true", async () => {
|
||||
const defaultModel = "openai/gpt-5.5";
|
||||
const applied = await applyDefaultModelChoice({
|
||||
config: {},
|
||||
setDefaultModel: true,
|
||||
defaultModel,
|
||||
applyProviderConfig: (config: OpenClawConfig) => config,
|
||||
applyDefaultConfig: () => ({
|
||||
agents: {
|
||||
defaults: {
|
||||
model: { primary: defaultModel },
|
||||
},
|
||||
},
|
||||
}),
|
||||
noteDefault: defaultModel,
|
||||
noteAgentModel: async () => {},
|
||||
prompter: makePrompter(),
|
||||
});
|
||||
|
||||
expect(applied.agentModelOverride).toBeUndefined();
|
||||
expect(applied.config.agents?.defaults?.model).toEqual({ primary: defaultModel });
|
||||
});
|
||||
});
|
||||
|
||||
describe("applyOpencodeZenModelDefault", () => {
|
||||
it("sets defaults when model is unset", () => {
|
||||
const cfg: OpenClawConfig = { agents: { defaults: {} } };
|
||||
|
||||
@@ -25,20 +25,3 @@ export function expectSchemaValid(
|
||||
const res = schema.safeParse(config);
|
||||
expect(res.success).toBe(true);
|
||||
}
|
||||
|
||||
export function expectSchemaValidationIssue(params: {
|
||||
schema: { safeParse: (value: unknown) => SchemaParseResult };
|
||||
config: unknown;
|
||||
expectedPath: string;
|
||||
expectedMessage?: string;
|
||||
}) {
|
||||
const res = params.schema.safeParse(params.config);
|
||||
expect(res.success).toBe(false);
|
||||
if (!res.success) {
|
||||
const issue = res.error.issues[0];
|
||||
expect(issue?.path.join(".")).toBe(params.expectedPath);
|
||||
if (params.expectedMessage !== undefined) {
|
||||
expect(issue?.message).toContain(params.expectedMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { type PluginManifestRegistry } from "../plugins/manifest-registry.js";
|
||||
import { clearPluginSetupRegistryCache } from "../plugins/setup-registry.js";
|
||||
import {
|
||||
cleanupTrackedTempDirs,
|
||||
makeTrackedTempDir,
|
||||
mkdirSafeDir,
|
||||
} from "../plugins/test-helpers/fs-fixtures.js";
|
||||
import { cleanupTrackedTempDirs, makeTrackedTempDir } from "../plugins/test-helpers/fs-fixtures.js";
|
||||
|
||||
const tempDirs: string[] = [];
|
||||
|
||||
@@ -29,28 +24,6 @@ export function makeIsolatedEnv(overrides: NodeJS.ProcessEnv = {}): NodeJS.Proce
|
||||
};
|
||||
}
|
||||
|
||||
export function writePluginManifestFixture(params: {
|
||||
rootDir: string;
|
||||
id: string;
|
||||
channels: string[];
|
||||
}): void {
|
||||
mkdirSafeDir(params.rootDir);
|
||||
fs.writeFileSync(
|
||||
path.join(params.rootDir, "openclaw.plugin.json"),
|
||||
JSON.stringify(
|
||||
{
|
||||
id: params.id,
|
||||
channels: params.channels,
|
||||
configSchema: { type: "object" },
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
"utf-8",
|
||||
);
|
||||
fs.writeFileSync(path.join(params.rootDir, "index.ts"), "export default {}", "utf-8");
|
||||
}
|
||||
|
||||
export function makeRegistry(
|
||||
plugins: Array<{
|
||||
id: string;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { expect, vi } from "vitest";
|
||||
import type { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
import type { ResolvedGatewayAuth } from "./auth.js";
|
||||
import { createGatewayRequest, createHooksConfig } from "./hooks-test-helpers.js";
|
||||
import { canonicalizePathVariant, isProtectedPluginRoutePath } from "./security-path.js";
|
||||
import { canonicalizePathVariant } from "./security-path.js";
|
||||
import { createGatewayHttpServer } from "./server-http.js";
|
||||
import { createHooksRequestHandler } from "./server/hooks-request-handler.js";
|
||||
import { withTempConfig } from "./test-temp-config.js";
|
||||
@@ -311,7 +311,3 @@ export async function expectAuthorizedVariants(params: {
|
||||
expect(response.getBody(), variant.label).toContain('"route":"channel-canonicalized"');
|
||||
}
|
||||
}
|
||||
|
||||
export function defaultProtectedPluginRoutePath(pathname: string): boolean {
|
||||
return isProtectedPluginRoutePath(pathname);
|
||||
}
|
||||
|
||||
@@ -164,38 +164,6 @@ function buildSseResponse(events: unknown[]): Response {
|
||||
});
|
||||
}
|
||||
|
||||
export function buildOpenAIResponsesTextSse(text: string): Response {
|
||||
return buildSseResponse([
|
||||
{
|
||||
type: "response.output_item.added",
|
||||
item: {
|
||||
type: "message",
|
||||
id: "msg_test_1",
|
||||
role: "assistant",
|
||||
content: [],
|
||||
status: "in_progress",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "response.output_item.done",
|
||||
item: {
|
||||
type: "message",
|
||||
id: "msg_test_1",
|
||||
role: "assistant",
|
||||
status: "completed",
|
||||
content: [{ type: "output_text", text, annotations: [] }],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "response.completed",
|
||||
response: {
|
||||
status: "completed",
|
||||
usage: { input_tokens: 10, output_tokens: 10, total_tokens: 20 },
|
||||
},
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
async function buildOpenAIResponsesSse(params: OpenAIResponsesParams): Promise<Response> {
|
||||
const events: OpenAIResponseStreamEvent[] = [];
|
||||
for await (const event of fakeOpenAIResponsesStream(params)) {
|
||||
|
||||
@@ -13,39 +13,3 @@ export function createPngBufferWithDimensions(params: { width: number; height: n
|
||||
]);
|
||||
return Buffer.concat([signature, ihdrLength, ihdrType, ihdrData, ihdrCrc, iend]);
|
||||
}
|
||||
|
||||
export function createJpegBufferWithDimensions(params: { width: number; height: number }): Buffer {
|
||||
if (params.width > 0xffff || params.height > 0xffff) {
|
||||
throw new Error("Synthetic JPEG helper only supports 16-bit dimensions");
|
||||
}
|
||||
|
||||
const app0 = Buffer.from([
|
||||
0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01,
|
||||
0x00, 0x00,
|
||||
]);
|
||||
const sof0 = Buffer.from([
|
||||
0xff,
|
||||
0xc0,
|
||||
0x00,
|
||||
0x11,
|
||||
0x08,
|
||||
params.height >> 8,
|
||||
params.height & 0xff,
|
||||
params.width >> 8,
|
||||
params.width & 0xff,
|
||||
0x03,
|
||||
0x01,
|
||||
0x11,
|
||||
0x00,
|
||||
0x02,
|
||||
0x11,
|
||||
0x00,
|
||||
0x03,
|
||||
0x11,
|
||||
0x00,
|
||||
]);
|
||||
const sos = Buffer.from([
|
||||
0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00,
|
||||
]);
|
||||
return Buffer.concat([Buffer.from([0xff, 0xd8]), app0, sof0, sos, Buffer.from([0xff, 0xd9])]);
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export { ensureStaticModelAllowlistEntry as ensureModelAllowlistEntry } from "../agents/model-allowlist-entry.js";
|
||||
Reference in New Issue
Block a user