mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-23 14:45:46 +00:00
test: share cli and channel setup fixtures
This commit is contained in:
@@ -1,20 +1,10 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { registerBrowserManageCommands } from "./browser-cli-manage.js";
|
||||
import { createBrowserProgram } from "./browser-cli-test-helpers.js";
|
||||
import type { CliRuntimeCapture } from "./test-runtime-capture.js";
|
||||
|
||||
const runtimeState = vi.hoisted(() => ({ capture: null as CliRuntimeCapture | null }));
|
||||
|
||||
function getRuntimeCapture(): CliRuntimeCapture {
|
||||
if (!runtimeState.capture) {
|
||||
throw new Error("runtime capture not initialized");
|
||||
}
|
||||
return runtimeState.capture;
|
||||
}
|
||||
|
||||
function getRuntime() {
|
||||
return getRuntimeCapture().defaultRuntime;
|
||||
}
|
||||
import {
|
||||
createBrowserProgram,
|
||||
getBrowserCliRuntime,
|
||||
getBrowserCliRuntimeCapture,
|
||||
} from "./browser-cli-test-helpers.js";
|
||||
|
||||
const mocks = vi.hoisted(() => {
|
||||
return {
|
||||
@@ -32,19 +22,15 @@ vi.mock("./browser-cli-shared.js", () => ({
|
||||
callBrowserRequest: mocks.callBrowserRequest,
|
||||
}));
|
||||
|
||||
vi.mock("./cli-utils.js", () => ({
|
||||
runCommandWithRuntime: async (
|
||||
_runtime: unknown,
|
||||
action: () => Promise<void>,
|
||||
onError: (err: unknown) => void,
|
||||
) => await action().catch(onError),
|
||||
vi.mock("./cli-utils.js", async () => ({
|
||||
...(await (await import("./browser-cli-test-helpers.js")).createBrowserCliUtilsMockModule()),
|
||||
}));
|
||||
|
||||
vi.mock("../runtime.js", async () => {
|
||||
const { createCliRuntimeCapture } = await import("./test-runtime-capture.js");
|
||||
runtimeState.capture ??= createCliRuntimeCapture();
|
||||
return { defaultRuntime: runtimeState.capture.defaultRuntime };
|
||||
});
|
||||
vi.mock(
|
||||
"../runtime.js",
|
||||
async () =>
|
||||
await (await import("./browser-cli-test-helpers.js")).createBrowserCliRuntimeMockModule(),
|
||||
);
|
||||
|
||||
function createProgram() {
|
||||
const { program, browser, parentOpts } = createBrowserProgram();
|
||||
@@ -55,7 +41,7 @@ function createProgram() {
|
||||
describe("browser manage output", () => {
|
||||
beforeEach(() => {
|
||||
mocks.callBrowserRequest.mockClear();
|
||||
getRuntimeCapture().resetRuntimeCapture();
|
||||
getBrowserCliRuntimeCapture().resetRuntimeCapture();
|
||||
});
|
||||
|
||||
it("shows chrome-mcp transport for existing-session status without fake CDP fields", async () => {
|
||||
@@ -88,7 +74,7 @@ describe("browser manage output", () => {
|
||||
from: "user",
|
||||
});
|
||||
|
||||
const output = getRuntime().log.mock.calls.at(-1)?.[0] as string;
|
||||
const output = getBrowserCliRuntime().log.mock.calls.at(-1)?.[0] as string;
|
||||
expect(output).toContain("transport: chrome-mcp");
|
||||
expect(output).not.toContain("cdpPort:");
|
||||
expect(output).not.toContain("cdpUrl:");
|
||||
@@ -124,7 +110,7 @@ describe("browser manage output", () => {
|
||||
from: "user",
|
||||
});
|
||||
|
||||
const output = getRuntime().log.mock.calls.at(-1)?.[0] as string;
|
||||
const output = getBrowserCliRuntime().log.mock.calls.at(-1)?.[0] as string;
|
||||
expect(output).toContain(
|
||||
"userDataDir: /Users/test/Library/Application Support/BraveSoftware/Brave-Browser",
|
||||
);
|
||||
@@ -155,7 +141,7 @@ describe("browser manage output", () => {
|
||||
const program = createProgram();
|
||||
await program.parseAsync(["browser", "profiles"], { from: "user" });
|
||||
|
||||
const output = getRuntime().log.mock.calls.at(-1)?.[0] as string;
|
||||
const output = getBrowserCliRuntime().log.mock.calls.at(-1)?.[0] as string;
|
||||
expect(output).toContain("chrome-live: running (2 tabs) [existing-session]");
|
||||
expect(output).toContain("transport: chrome-mcp");
|
||||
expect(output).not.toContain("port: 0");
|
||||
@@ -183,7 +169,7 @@ describe("browser manage output", () => {
|
||||
{ from: "user" },
|
||||
);
|
||||
|
||||
const output = getRuntime().log.mock.calls.at(-1)?.[0] as string;
|
||||
const output = getBrowserCliRuntime().log.mock.calls.at(-1)?.[0] as string;
|
||||
expect(output).toContain('Created profile "chrome-live"');
|
||||
expect(output).toContain("transport: chrome-mcp");
|
||||
expect(output).not.toContain("port: 0");
|
||||
@@ -220,7 +206,7 @@ describe("browser manage output", () => {
|
||||
from: "user",
|
||||
});
|
||||
|
||||
const output = getRuntime().log.mock.calls.at(-1)?.[0] as string;
|
||||
const output = getBrowserCliRuntime().log.mock.calls.at(-1)?.[0] as string;
|
||||
expect(output).toContain("cdpUrl: https://example.com/chrome?token=supers…7890");
|
||||
expect(output).not.toContain("alice");
|
||||
expect(output).not.toContain("supersecretpasswordvalue1234");
|
||||
|
||||
@@ -1,16 +1,6 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { registerBrowserManageCommands } from "./browser-cli-manage.js";
|
||||
import { createBrowserProgram } from "./browser-cli-test-helpers.js";
|
||||
import type { CliRuntimeCapture } from "./test-runtime-capture.js";
|
||||
|
||||
const runtimeState = vi.hoisted(() => ({ capture: null as CliRuntimeCapture | null }));
|
||||
|
||||
function getRuntimeCapture(): CliRuntimeCapture {
|
||||
if (!runtimeState.capture) {
|
||||
throw new Error("runtime capture not initialized");
|
||||
}
|
||||
return runtimeState.capture;
|
||||
}
|
||||
import { createBrowserProgram, getBrowserCliRuntimeCapture } from "./browser-cli-test-helpers.js";
|
||||
|
||||
const mocks = vi.hoisted(() => {
|
||||
return {
|
||||
@@ -36,19 +26,15 @@ vi.mock("./browser-cli-shared.js", () => ({
|
||||
callBrowserRequest: mocks.callBrowserRequest,
|
||||
}));
|
||||
|
||||
vi.mock("./cli-utils.js", () => ({
|
||||
runCommandWithRuntime: async (
|
||||
_runtime: unknown,
|
||||
action: () => Promise<void>,
|
||||
onError: (err: unknown) => void,
|
||||
) => await action().catch(onError),
|
||||
vi.mock("./cli-utils.js", async () => ({
|
||||
...(await (await import("./browser-cli-test-helpers.js")).createBrowserCliUtilsMockModule()),
|
||||
}));
|
||||
|
||||
vi.mock("../runtime.js", async () => {
|
||||
const { createCliRuntimeCapture } = await import("./test-runtime-capture.js");
|
||||
runtimeState.capture ??= createCliRuntimeCapture();
|
||||
return { defaultRuntime: runtimeState.capture.defaultRuntime };
|
||||
});
|
||||
vi.mock(
|
||||
"../runtime.js",
|
||||
async () =>
|
||||
await (await import("./browser-cli-test-helpers.js")).createBrowserCliRuntimeMockModule(),
|
||||
);
|
||||
|
||||
describe("browser manage start timeout option", () => {
|
||||
function createProgram() {
|
||||
@@ -60,7 +46,7 @@ describe("browser manage start timeout option", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
mocks.callBrowserRequest.mockClear();
|
||||
getRuntimeCapture().resetRuntimeCapture();
|
||||
getBrowserCliRuntimeCapture().resetRuntimeCapture();
|
||||
});
|
||||
|
||||
it("uses parent --timeout for browser start instead of hardcoded 15s", async () => {
|
||||
|
||||
@@ -1,20 +1,10 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { registerBrowserStateCommands } from "./browser-cli-state.js";
|
||||
import { createBrowserProgram as createBrowserProgramShared } from "./browser-cli-test-helpers.js";
|
||||
import type { CliRuntimeCapture } from "./test-runtime-capture.js";
|
||||
|
||||
const runtimeState = vi.hoisted(() => ({ capture: null as CliRuntimeCapture | null }));
|
||||
|
||||
function getRuntimeCapture(): CliRuntimeCapture {
|
||||
if (!runtimeState.capture) {
|
||||
throw new Error("runtime capture not initialized");
|
||||
}
|
||||
return runtimeState.capture;
|
||||
}
|
||||
|
||||
function getRuntime() {
|
||||
return getRuntimeCapture().defaultRuntime;
|
||||
}
|
||||
import {
|
||||
createBrowserProgram as createBrowserProgramShared,
|
||||
getBrowserCliRuntime,
|
||||
getBrowserCliRuntimeCapture,
|
||||
} from "./browser-cli-test-helpers.js";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
callBrowserRequest: vi.fn(async (..._args: unknown[]) => ({ ok: true })),
|
||||
@@ -29,11 +19,11 @@ vi.mock("./browser-cli-resize.js", () => ({
|
||||
runBrowserResizeWithOutput: mocks.runBrowserResizeWithOutput,
|
||||
}));
|
||||
|
||||
vi.mock("../runtime.js", async () => {
|
||||
const { createCliRuntimeCapture } = await import("./test-runtime-capture.js");
|
||||
runtimeState.capture ??= createCliRuntimeCapture();
|
||||
return { defaultRuntime: runtimeState.capture.defaultRuntime };
|
||||
});
|
||||
vi.mock(
|
||||
"../runtime.js",
|
||||
async () =>
|
||||
await (await import("./browser-cli-test-helpers.js")).createBrowserCliRuntimeMockModule(),
|
||||
);
|
||||
|
||||
describe("browser state option collisions", () => {
|
||||
const createStateProgram = ({ withGatewayUrl = false } = {}) => {
|
||||
@@ -64,8 +54,8 @@ describe("browser state option collisions", () => {
|
||||
beforeEach(() => {
|
||||
mocks.callBrowserRequest.mockClear();
|
||||
mocks.runBrowserResizeWithOutput.mockClear();
|
||||
getRuntimeCapture().resetRuntimeCapture();
|
||||
getRuntime().exit.mockImplementation(() => {});
|
||||
getBrowserCliRuntimeCapture().resetRuntimeCapture();
|
||||
getBrowserCliRuntime().exit.mockImplementation(() => {});
|
||||
});
|
||||
|
||||
it("forwards parent-captured --target-id on `browser cookies set`", async () => {
|
||||
@@ -145,37 +135,39 @@ describe("browser state option collisions", () => {
|
||||
await runBrowserCommand(["set", "offline", "maybe"]);
|
||||
|
||||
expect(mocks.callBrowserRequest).not.toHaveBeenCalled();
|
||||
expect(getRuntime().error).toHaveBeenCalledWith(expect.stringContaining("Expected on|off"));
|
||||
expect(getRuntime().exit).toHaveBeenCalledWith(1);
|
||||
expect(getBrowserCliRuntime().error).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Expected on|off"),
|
||||
);
|
||||
expect(getBrowserCliRuntime().exit).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
it("errors when set media receives an invalid value", async () => {
|
||||
await runBrowserCommand(["set", "media", "sepia"]);
|
||||
|
||||
expect(mocks.callBrowserRequest).not.toHaveBeenCalled();
|
||||
expect(getRuntime().error).toHaveBeenCalledWith(
|
||||
expect(getBrowserCliRuntime().error).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Expected dark|light|none"),
|
||||
);
|
||||
expect(getRuntime().exit).toHaveBeenCalledWith(1);
|
||||
expect(getBrowserCliRuntime().exit).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
it("errors when headers JSON is missing", async () => {
|
||||
await runBrowserCommand(["set", "headers"]);
|
||||
|
||||
expect(mocks.callBrowserRequest).not.toHaveBeenCalled();
|
||||
expect(getRuntime().error).toHaveBeenCalledWith(
|
||||
expect(getBrowserCliRuntime().error).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Missing headers JSON"),
|
||||
);
|
||||
expect(getRuntime().exit).toHaveBeenCalledWith(1);
|
||||
expect(getBrowserCliRuntime().exit).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
it("errors when headers JSON is not an object", async () => {
|
||||
await runBrowserCommand(["set", "headers", "--json", "[]"]);
|
||||
|
||||
expect(mocks.callBrowserRequest).not.toHaveBeenCalled();
|
||||
expect(getRuntime().error).toHaveBeenCalledWith(
|
||||
expect(getBrowserCliRuntime().error).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Headers JSON must be a JSON object"),
|
||||
);
|
||||
expect(getRuntime().exit).toHaveBeenCalledWith(1);
|
||||
expect(getBrowserCliRuntime().exit).toHaveBeenCalledWith(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Command } from "commander";
|
||||
import type { BrowserParentOpts } from "./browser-cli-shared.js";
|
||||
import { createCliRuntimeCapture } from "./test-runtime-capture.js";
|
||||
import type { CliRuntimeCapture } from "./test-runtime-capture.js";
|
||||
|
||||
export function createBrowserProgram(params?: { withGatewayUrl?: boolean }): {
|
||||
program: Command;
|
||||
@@ -17,3 +19,37 @@ export function createBrowserProgram(params?: { withGatewayUrl?: boolean }): {
|
||||
const parentOpts = (cmd: Command) => cmd.parent?.opts?.() as BrowserParentOpts;
|
||||
return { program, browser, parentOpts };
|
||||
}
|
||||
|
||||
const browserCliRuntimeState = { capture: null as CliRuntimeCapture | null };
|
||||
|
||||
export function getBrowserCliRuntimeCapture(): CliRuntimeCapture {
|
||||
if (!browserCliRuntimeState.capture) {
|
||||
throw new Error("runtime capture not initialized");
|
||||
}
|
||||
return browserCliRuntimeState.capture;
|
||||
}
|
||||
|
||||
export function getBrowserCliRuntime() {
|
||||
return getBrowserCliRuntimeCapture().defaultRuntime;
|
||||
}
|
||||
|
||||
export async function mockBrowserCliDefaultRuntime() {
|
||||
browserCliRuntimeState.capture ??= createCliRuntimeCapture();
|
||||
return { defaultRuntime: browserCliRuntimeState.capture.defaultRuntime };
|
||||
}
|
||||
|
||||
export async function runCommandWithRuntimeMock(
|
||||
_runtime: unknown,
|
||||
action: () => Promise<void>,
|
||||
onError: (err: unknown) => void,
|
||||
) {
|
||||
return await action().catch(onError);
|
||||
}
|
||||
|
||||
export async function createBrowserCliUtilsMockModule() {
|
||||
return { runCommandWithRuntime: runCommandWithRuntimeMock };
|
||||
}
|
||||
|
||||
export async function createBrowserCliRuntimeMockModule() {
|
||||
return await mockBrowserCliDefaultRuntime();
|
||||
}
|
||||
|
||||
@@ -45,6 +45,78 @@ vi.mock("@clack/prompts", () => ({
|
||||
|
||||
const { registerSecretsCli } = await import("./secrets-cli.js");
|
||||
|
||||
function createManualSecretsPlan() {
|
||||
return {
|
||||
version: 1,
|
||||
protocolVersion: 1,
|
||||
generatedAt: "2026-02-26T00:00:00.000Z",
|
||||
generatedBy: "manual",
|
||||
targets: [],
|
||||
};
|
||||
}
|
||||
|
||||
function createConfigureInteractiveResult(options?: {
|
||||
targets?: unknown[];
|
||||
changed?: boolean;
|
||||
resolvabilityComplete?: boolean;
|
||||
}) {
|
||||
return {
|
||||
plan: {
|
||||
version: 1,
|
||||
protocolVersion: 1,
|
||||
generatedAt: "2026-02-26T00:00:00.000Z",
|
||||
generatedBy: "openclaw secrets configure",
|
||||
targets: options?.targets ?? [],
|
||||
},
|
||||
preflight: {
|
||||
mode: "dry-run" as const,
|
||||
changed: options?.changed ?? false,
|
||||
changedFiles: options?.changed ? ["/tmp/openclaw.json"] : [],
|
||||
checks: {
|
||||
resolvability: true,
|
||||
resolvabilityComplete: options?.resolvabilityComplete ?? true,
|
||||
},
|
||||
refsChecked: 0,
|
||||
skippedExecRefs: 0,
|
||||
warningCount: 0,
|
||||
warnings: [],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createSecretsApplyResult(options?: {
|
||||
mode?: "dry-run" | "write";
|
||||
changed?: boolean;
|
||||
resolvabilityComplete?: boolean;
|
||||
}) {
|
||||
return {
|
||||
mode: options?.mode ?? "dry-run",
|
||||
changed: options?.changed ?? false,
|
||||
changedFiles: options?.changed ? ["/tmp/openclaw.json"] : [],
|
||||
checks: {
|
||||
resolvability: true,
|
||||
resolvabilityComplete: options?.resolvabilityComplete ?? true,
|
||||
},
|
||||
refsChecked: 0,
|
||||
skippedExecRefs: 0,
|
||||
warningCount: 0,
|
||||
warnings: [],
|
||||
};
|
||||
}
|
||||
|
||||
async function withPlanFile(run: (planPath: string) => Promise<void>) {
|
||||
const planPath = path.join(
|
||||
os.tmpdir(),
|
||||
`openclaw-secrets-cli-test-${Date.now()}-${Math.random().toString(16).slice(2)}.json`,
|
||||
);
|
||||
await fs.writeFile(planPath, `${JSON.stringify(createManualSecretsPlan())}\n`, "utf8");
|
||||
try {
|
||||
await run(planPath);
|
||||
} finally {
|
||||
await fs.rm(planPath, { force: true });
|
||||
}
|
||||
}
|
||||
|
||||
describe("secrets CLI", () => {
|
||||
const createProgram = () => {
|
||||
const program = new Command();
|
||||
@@ -142,12 +214,9 @@ describe("secrets CLI", () => {
|
||||
});
|
||||
|
||||
it("runs secrets configure then apply when confirmed", async () => {
|
||||
runSecretsConfigureInteractive.mockResolvedValue({
|
||||
plan: {
|
||||
version: 1,
|
||||
protocolVersion: 1,
|
||||
generatedAt: "2026-02-26T00:00:00.000Z",
|
||||
generatedBy: "openclaw secrets configure",
|
||||
runSecretsConfigureInteractive.mockResolvedValue(
|
||||
createConfigureInteractiveResult({
|
||||
changed: true,
|
||||
targets: [
|
||||
{
|
||||
type: "skills.entries.apiKey",
|
||||
@@ -160,35 +229,10 @@ describe("secrets CLI", () => {
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
preflight: {
|
||||
mode: "dry-run",
|
||||
changed: true,
|
||||
changedFiles: ["/tmp/openclaw.json"],
|
||||
checks: {
|
||||
resolvability: true,
|
||||
resolvabilityComplete: true,
|
||||
},
|
||||
refsChecked: 1,
|
||||
skippedExecRefs: 0,
|
||||
warningCount: 0,
|
||||
warnings: [],
|
||||
},
|
||||
});
|
||||
}),
|
||||
);
|
||||
confirm.mockResolvedValue(true);
|
||||
runSecretsApply.mockResolvedValue({
|
||||
mode: "write",
|
||||
changed: true,
|
||||
changedFiles: ["/tmp/openclaw.json"],
|
||||
checks: {
|
||||
resolvability: true,
|
||||
resolvabilityComplete: true,
|
||||
},
|
||||
refsChecked: 1,
|
||||
skippedExecRefs: 0,
|
||||
warningCount: 0,
|
||||
warnings: [],
|
||||
});
|
||||
runSecretsApply.mockResolvedValue(createSecretsApplyResult({ mode: "write", changed: true }));
|
||||
|
||||
await createProgram().parseAsync(["secrets", "configure"], { from: "user" });
|
||||
expect(runSecretsConfigureInteractive).toHaveBeenCalled();
|
||||
@@ -209,28 +253,7 @@ describe("secrets CLI", () => {
|
||||
});
|
||||
|
||||
it("forwards --agent to secrets configure", async () => {
|
||||
runSecretsConfigureInteractive.mockResolvedValue({
|
||||
plan: {
|
||||
version: 1,
|
||||
protocolVersion: 1,
|
||||
generatedAt: "2026-02-26T00:00:00.000Z",
|
||||
generatedBy: "openclaw secrets configure",
|
||||
targets: [],
|
||||
},
|
||||
preflight: {
|
||||
mode: "dry-run",
|
||||
changed: false,
|
||||
changedFiles: [],
|
||||
checks: {
|
||||
resolvability: true,
|
||||
resolvabilityComplete: true,
|
||||
},
|
||||
refsChecked: 0,
|
||||
skippedExecRefs: 0,
|
||||
warningCount: 0,
|
||||
warnings: [],
|
||||
},
|
||||
});
|
||||
runSecretsConfigureInteractive.mockResolvedValue(createConfigureInteractiveResult());
|
||||
confirm.mockResolvedValue(false);
|
||||
|
||||
await createProgram().parseAsync(["secrets", "configure", "--agent", "ops"], { from: "user" });
|
||||
@@ -243,154 +266,57 @@ describe("secrets CLI", () => {
|
||||
});
|
||||
|
||||
it("forwards --allow-exec to secrets apply dry-run", async () => {
|
||||
const planPath = path.join(
|
||||
os.tmpdir(),
|
||||
`openclaw-secrets-cli-test-${Date.now()}-${Math.random().toString(16).slice(2)}.json`,
|
||||
);
|
||||
await fs.writeFile(
|
||||
planPath,
|
||||
`${JSON.stringify({
|
||||
version: 1,
|
||||
protocolVersion: 1,
|
||||
generatedAt: new Date().toISOString(),
|
||||
generatedBy: "manual",
|
||||
targets: [],
|
||||
})}\n`,
|
||||
"utf8",
|
||||
);
|
||||
runSecretsApply.mockResolvedValue({
|
||||
mode: "dry-run",
|
||||
changed: false,
|
||||
changedFiles: [],
|
||||
checks: {
|
||||
resolvability: true,
|
||||
resolvabilityComplete: true,
|
||||
},
|
||||
refsChecked: 0,
|
||||
skippedExecRefs: 0,
|
||||
warningCount: 0,
|
||||
warnings: [],
|
||||
});
|
||||
await withPlanFile(async (planPath) => {
|
||||
runSecretsApply.mockResolvedValue(createSecretsApplyResult());
|
||||
|
||||
await createProgram().parseAsync(
|
||||
["secrets", "apply", "--from", planPath, "--dry-run", "--allow-exec"],
|
||||
{
|
||||
from: "user",
|
||||
},
|
||||
);
|
||||
expect(runSecretsApply).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
write: false,
|
||||
allowExec: true,
|
||||
}),
|
||||
);
|
||||
await fs.rm(planPath, { force: true });
|
||||
await createProgram().parseAsync(
|
||||
["secrets", "apply", "--from", planPath, "--dry-run", "--allow-exec"],
|
||||
{
|
||||
from: "user",
|
||||
},
|
||||
);
|
||||
expect(runSecretsApply).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
write: false,
|
||||
allowExec: true,
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("forwards --allow-exec to secrets apply write mode", async () => {
|
||||
const planPath = path.join(
|
||||
os.tmpdir(),
|
||||
`openclaw-secrets-cli-test-${Date.now()}-${Math.random().toString(16).slice(2)}.json`,
|
||||
);
|
||||
await fs.writeFile(
|
||||
planPath,
|
||||
`${JSON.stringify({
|
||||
version: 1,
|
||||
protocolVersion: 1,
|
||||
generatedAt: new Date().toISOString(),
|
||||
generatedBy: "manual",
|
||||
targets: [],
|
||||
})}\n`,
|
||||
"utf8",
|
||||
);
|
||||
runSecretsApply.mockResolvedValue({
|
||||
mode: "write",
|
||||
changed: false,
|
||||
changedFiles: [],
|
||||
checks: {
|
||||
resolvability: true,
|
||||
resolvabilityComplete: true,
|
||||
},
|
||||
refsChecked: 0,
|
||||
skippedExecRefs: 0,
|
||||
warningCount: 0,
|
||||
warnings: [],
|
||||
});
|
||||
await withPlanFile(async (planPath) => {
|
||||
runSecretsApply.mockResolvedValue(createSecretsApplyResult({ mode: "write" }));
|
||||
|
||||
await createProgram().parseAsync(["secrets", "apply", "--from", planPath, "--allow-exec"], {
|
||||
from: "user",
|
||||
await createProgram().parseAsync(["secrets", "apply", "--from", planPath, "--allow-exec"], {
|
||||
from: "user",
|
||||
});
|
||||
expect(runSecretsApply).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
write: true,
|
||||
allowExec: true,
|
||||
}),
|
||||
);
|
||||
});
|
||||
expect(runSecretsApply).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
write: true,
|
||||
allowExec: true,
|
||||
}),
|
||||
);
|
||||
await fs.rm(planPath, { force: true });
|
||||
});
|
||||
|
||||
it("does not print skipped-exec note when apply dry-run skippedExecRefs is zero", async () => {
|
||||
const planPath = path.join(
|
||||
os.tmpdir(),
|
||||
`openclaw-secrets-cli-test-${Date.now()}-${Math.random().toString(16).slice(2)}.json`,
|
||||
);
|
||||
await fs.writeFile(
|
||||
planPath,
|
||||
`${JSON.stringify({
|
||||
version: 1,
|
||||
protocolVersion: 1,
|
||||
generatedAt: new Date().toISOString(),
|
||||
generatedBy: "manual",
|
||||
targets: [],
|
||||
})}\n`,
|
||||
"utf8",
|
||||
);
|
||||
runSecretsApply.mockResolvedValue({
|
||||
mode: "dry-run",
|
||||
changed: false,
|
||||
changedFiles: [],
|
||||
checks: {
|
||||
resolvability: true,
|
||||
resolvabilityComplete: false,
|
||||
},
|
||||
refsChecked: 0,
|
||||
skippedExecRefs: 0,
|
||||
warningCount: 0,
|
||||
warnings: [],
|
||||
});
|
||||
await withPlanFile(async (planPath) => {
|
||||
runSecretsApply.mockResolvedValue(createSecretsApplyResult({ resolvabilityComplete: false }));
|
||||
|
||||
await createProgram().parseAsync(["secrets", "apply", "--from", planPath, "--dry-run"], {
|
||||
from: "user",
|
||||
await createProgram().parseAsync(["secrets", "apply", "--from", planPath, "--dry-run"], {
|
||||
from: "user",
|
||||
});
|
||||
expect(runtimeLogs.some((line) => line.includes("Secrets apply dry-run note: skipped"))).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
expect(runtimeLogs.some((line) => line.includes("Secrets apply dry-run note: skipped"))).toBe(
|
||||
false,
|
||||
);
|
||||
await fs.rm(planPath, { force: true });
|
||||
});
|
||||
|
||||
it("does not print skipped-exec note when configure preflight skippedExecRefs is zero", async () => {
|
||||
runSecretsConfigureInteractive.mockResolvedValue({
|
||||
plan: {
|
||||
version: 1,
|
||||
protocolVersion: 1,
|
||||
generatedAt: "2026-02-26T00:00:00.000Z",
|
||||
generatedBy: "openclaw secrets configure",
|
||||
targets: [],
|
||||
},
|
||||
preflight: {
|
||||
mode: "dry-run",
|
||||
changed: false,
|
||||
changedFiles: [],
|
||||
checks: {
|
||||
resolvability: true,
|
||||
resolvabilityComplete: false,
|
||||
},
|
||||
refsChecked: 0,
|
||||
skippedExecRefs: 0,
|
||||
warningCount: 0,
|
||||
warnings: [],
|
||||
},
|
||||
});
|
||||
runSecretsConfigureInteractive.mockResolvedValue(
|
||||
createConfigureInteractiveResult({ resolvabilityComplete: false }),
|
||||
);
|
||||
confirm.mockResolvedValue(false);
|
||||
|
||||
await createProgram().parseAsync(["secrets", "configure"], { from: "user" });
|
||||
@@ -398,41 +324,8 @@ describe("secrets CLI", () => {
|
||||
});
|
||||
|
||||
it("forwards --allow-exec to configure preflight and apply", async () => {
|
||||
runSecretsConfigureInteractive.mockResolvedValue({
|
||||
plan: {
|
||||
version: 1,
|
||||
protocolVersion: 1,
|
||||
generatedAt: "2026-02-26T00:00:00.000Z",
|
||||
generatedBy: "openclaw secrets configure",
|
||||
targets: [],
|
||||
},
|
||||
preflight: {
|
||||
mode: "dry-run",
|
||||
changed: false,
|
||||
changedFiles: [],
|
||||
checks: {
|
||||
resolvability: true,
|
||||
resolvabilityComplete: true,
|
||||
},
|
||||
refsChecked: 0,
|
||||
skippedExecRefs: 0,
|
||||
warningCount: 0,
|
||||
warnings: [],
|
||||
},
|
||||
});
|
||||
runSecretsApply.mockResolvedValue({
|
||||
mode: "write",
|
||||
changed: false,
|
||||
changedFiles: [],
|
||||
checks: {
|
||||
resolvability: true,
|
||||
resolvabilityComplete: true,
|
||||
},
|
||||
refsChecked: 0,
|
||||
skippedExecRefs: 0,
|
||||
warningCount: 0,
|
||||
warnings: [],
|
||||
});
|
||||
runSecretsConfigureInteractive.mockResolvedValue(createConfigureInteractiveResult());
|
||||
runSecretsApply.mockResolvedValue(createSecretsApplyResult({ mode: "write" }));
|
||||
|
||||
await createProgram().parseAsync(["secrets", "configure", "--apply", "--yes", "--allow-exec"], {
|
||||
from: "user",
|
||||
|
||||
@@ -45,6 +45,22 @@ function createProgram() {
|
||||
return program;
|
||||
}
|
||||
|
||||
function primeDeepAuditConfig(sourceConfig = { gateway: { mode: "local" } }) {
|
||||
loadConfig.mockReturnValue(sourceConfig);
|
||||
resolveCommandSecretRefsViaGateway.mockResolvedValue({
|
||||
resolvedConfig: sourceConfig,
|
||||
diagnostics: [],
|
||||
targetStatesByPath: {},
|
||||
hadUnresolvedTargets: false,
|
||||
});
|
||||
runSecurityAudit.mockResolvedValue({
|
||||
ts: 0,
|
||||
summary: { critical: 0, warn: 0, info: 0 },
|
||||
findings: [],
|
||||
});
|
||||
return sourceConfig;
|
||||
}
|
||||
|
||||
describe("security CLI", () => {
|
||||
beforeEach(() => {
|
||||
resetRuntimeCapture();
|
||||
@@ -131,27 +147,31 @@ describe("security CLI", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("forwards --token to deep probe auth without altering command-level resolver mode", async () => {
|
||||
const sourceConfig = { gateway: { mode: "local" } };
|
||||
loadConfig.mockReturnValue(sourceConfig);
|
||||
resolveCommandSecretRefsViaGateway.mockResolvedValue({
|
||||
resolvedConfig: sourceConfig,
|
||||
diagnostics: [],
|
||||
targetStatesByPath: {},
|
||||
hadUnresolvedTargets: false,
|
||||
});
|
||||
runSecurityAudit.mockResolvedValue({
|
||||
ts: 0,
|
||||
summary: { critical: 0, warn: 0, info: 0 },
|
||||
findings: [],
|
||||
});
|
||||
|
||||
await createProgram().parseAsync(
|
||||
["security", "audit", "--deep", "--token", "explicit-token", "--json"],
|
||||
{
|
||||
from: "user",
|
||||
it.each([
|
||||
{
|
||||
title: "forwards --token to deep probe auth without altering command-level resolver mode",
|
||||
argv: ["--token", "explicit-token"],
|
||||
deepProbeAuth: { token: "explicit-token" },
|
||||
},
|
||||
{
|
||||
title: "forwards --password to deep probe auth without altering command-level resolver mode",
|
||||
argv: ["--password", "explicit-password"],
|
||||
deepProbeAuth: { password: "explicit-password" },
|
||||
},
|
||||
{
|
||||
title: "forwards both --token and --password to deep probe auth",
|
||||
argv: ["--token", "explicit-token", "--password", "explicit-password"],
|
||||
deepProbeAuth: {
|
||||
token: "explicit-token",
|
||||
password: "explicit-password",
|
||||
},
|
||||
);
|
||||
},
|
||||
])("$title", async ({ argv, deepProbeAuth }) => {
|
||||
primeDeepAuditConfig();
|
||||
|
||||
await createProgram().parseAsync(["security", "audit", "--deep", ...argv, "--json"], {
|
||||
from: "user",
|
||||
});
|
||||
|
||||
expect(resolveCommandSecretRefsViaGateway).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
@@ -161,84 +181,7 @@ describe("security CLI", () => {
|
||||
expect(runSecurityAudit).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
deep: true,
|
||||
deepProbeAuth: { token: "explicit-token" },
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("forwards --password to deep probe auth without altering command-level resolver mode", async () => {
|
||||
const sourceConfig = { gateway: { mode: "local" } };
|
||||
loadConfig.mockReturnValue(sourceConfig);
|
||||
resolveCommandSecretRefsViaGateway.mockResolvedValue({
|
||||
resolvedConfig: sourceConfig,
|
||||
diagnostics: [],
|
||||
targetStatesByPath: {},
|
||||
hadUnresolvedTargets: false,
|
||||
});
|
||||
runSecurityAudit.mockResolvedValue({
|
||||
ts: 0,
|
||||
summary: { critical: 0, warn: 0, info: 0 },
|
||||
findings: [],
|
||||
});
|
||||
|
||||
await createProgram().parseAsync(
|
||||
["security", "audit", "--deep", "--password", "explicit-password", "--json"],
|
||||
{
|
||||
from: "user",
|
||||
},
|
||||
);
|
||||
|
||||
expect(resolveCommandSecretRefsViaGateway).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
mode: "read_only_status",
|
||||
}),
|
||||
);
|
||||
expect(runSecurityAudit).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
deep: true,
|
||||
deepProbeAuth: { password: "explicit-password" },
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("forwards both --token and --password to deep probe auth", async () => {
|
||||
const sourceConfig = { gateway: { mode: "local" } };
|
||||
loadConfig.mockReturnValue(sourceConfig);
|
||||
resolveCommandSecretRefsViaGateway.mockResolvedValue({
|
||||
resolvedConfig: sourceConfig,
|
||||
diagnostics: [],
|
||||
targetStatesByPath: {},
|
||||
hadUnresolvedTargets: false,
|
||||
});
|
||||
runSecurityAudit.mockResolvedValue({
|
||||
ts: 0,
|
||||
summary: { critical: 0, warn: 0, info: 0 },
|
||||
findings: [],
|
||||
});
|
||||
|
||||
await createProgram().parseAsync(
|
||||
[
|
||||
"security",
|
||||
"audit",
|
||||
"--deep",
|
||||
"--token",
|
||||
"explicit-token",
|
||||
"--password",
|
||||
"explicit-password",
|
||||
"--json",
|
||||
],
|
||||
{
|
||||
from: "user",
|
||||
},
|
||||
);
|
||||
|
||||
expect(runSecurityAudit).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
deep: true,
|
||||
deepProbeAuth: {
|
||||
token: "explicit-token",
|
||||
password: "explicit-password",
|
||||
},
|
||||
deepProbeAuth,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -9,6 +9,10 @@ import {
|
||||
} from "./channel-setup/plugin-install.js";
|
||||
import { setDefaultChannelPluginRegistryForTests } from "./channel-test-helpers.js";
|
||||
import { configMocks, offsetMocks } from "./channels.mock-harness.js";
|
||||
import {
|
||||
createMSTeamsCatalogEntry,
|
||||
createMSTeamsSetupPlugin,
|
||||
} from "./channels.plugin-install.test-helpers.js";
|
||||
import { baseConfigSnapshot, createTestRuntime } from "./test-runtime-config-helpers.js";
|
||||
|
||||
const catalogMocks = vi.hoisted(() => ({
|
||||
@@ -37,55 +41,14 @@ vi.mock("../plugins/manifest-registry.js", async (importOriginal) => {
|
||||
|
||||
vi.mock("./channel-setup/plugin-install.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("./channel-setup/plugin-install.js")>();
|
||||
return {
|
||||
...actual,
|
||||
ensureChannelSetupPluginInstalled: vi.fn(async ({ cfg }) => ({ cfg, installed: true })),
|
||||
loadChannelSetupPluginRegistrySnapshotForChannel: vi.fn(() => createTestRegistry()),
|
||||
};
|
||||
const { createMockChannelSetupPluginInstallModule } =
|
||||
await import("./channels.plugin-install.test-helpers.js");
|
||||
return createMockChannelSetupPluginInstallModule(actual);
|
||||
});
|
||||
|
||||
const runtime = createTestRuntime();
|
||||
let channelsAddCommand: typeof import("./channels.js").channelsAddCommand;
|
||||
|
||||
function createMSTeamsCatalogEntry(): ChannelPluginCatalogEntry {
|
||||
return {
|
||||
id: "msteams",
|
||||
pluginId: "@openclaw/msteams-plugin",
|
||||
meta: {
|
||||
id: "msteams",
|
||||
label: "Microsoft Teams",
|
||||
selectionLabel: "Microsoft Teams",
|
||||
docsPath: "/channels/msteams",
|
||||
blurb: "teams channel",
|
||||
},
|
||||
install: {
|
||||
npmSpec: "@openclaw/msteams",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createMSTeamsSetupPlugin(): ChannelPlugin {
|
||||
return {
|
||||
...createChannelTestPluginBase({
|
||||
id: "msteams",
|
||||
label: "Microsoft Teams",
|
||||
docsPath: "/channels/msteams",
|
||||
}),
|
||||
setup: {
|
||||
applyAccountConfig: vi.fn(({ cfg, input }) => ({
|
||||
...cfg,
|
||||
channels: {
|
||||
...cfg.channels,
|
||||
msteams: {
|
||||
enabled: true,
|
||||
tenantId: input.token,
|
||||
},
|
||||
},
|
||||
})),
|
||||
},
|
||||
} as ChannelPlugin;
|
||||
}
|
||||
|
||||
function registerMSTeamsSetupPlugin(pluginId = "@openclaw/msteams-plugin"): void {
|
||||
vi.mocked(loadChannelSetupPluginRegistrySnapshotForChannel).mockReturnValue(
|
||||
createTestRegistry([{ pluginId, plugin: createMSTeamsSetupPlugin(), source: "test" }]),
|
||||
@@ -124,6 +87,17 @@ function createSignalPlugin(
|
||||
} as ChannelPlugin;
|
||||
}
|
||||
|
||||
async function runSignalAddCommand(afterAccountConfigWritten: SignalAfterAccountConfigWritten) {
|
||||
const plugin = createSignalPlugin(afterAccountConfigWritten);
|
||||
setActivePluginRegistry(createTestRegistry([{ pluginId: "signal", plugin, source: "test" }]));
|
||||
configMocks.readConfigFileSnapshot.mockResolvedValue({ ...baseConfigSnapshot });
|
||||
await channelsAddCommand(
|
||||
{ channel: "signal", account: "ops", signalNumber: "+15550001" },
|
||||
runtime,
|
||||
{ hasFlags: true },
|
||||
);
|
||||
}
|
||||
|
||||
describe("channelsAddCommand", () => {
|
||||
beforeAll(async () => {
|
||||
({ channelsAddCommand } = await import("./channels.js"));
|
||||
@@ -352,15 +326,7 @@ describe("channelsAddCommand", () => {
|
||||
|
||||
it("runs post-setup hooks after writing config", async () => {
|
||||
const afterAccountConfigWritten = vi.fn().mockResolvedValue(undefined);
|
||||
const plugin = createSignalPlugin(afterAccountConfigWritten);
|
||||
setActivePluginRegistry(createTestRegistry([{ pluginId: "signal", plugin, source: "test" }]));
|
||||
configMocks.readConfigFileSnapshot.mockResolvedValue({ ...baseConfigSnapshot });
|
||||
|
||||
await channelsAddCommand(
|
||||
{ channel: "signal", account: "ops", signalNumber: "+15550001" },
|
||||
runtime,
|
||||
{ hasFlags: true },
|
||||
);
|
||||
await runSignalAddCommand(afterAccountConfigWritten);
|
||||
|
||||
expect(configMocks.writeConfigFile).toHaveBeenCalledTimes(1);
|
||||
expect(afterAccountConfigWritten).toHaveBeenCalledTimes(1);
|
||||
@@ -391,15 +357,7 @@ describe("channelsAddCommand", () => {
|
||||
|
||||
it("keeps the saved config when a post-setup hook fails", async () => {
|
||||
const afterAccountConfigWritten = vi.fn().mockRejectedValue(new Error("hook failed"));
|
||||
const plugin = createSignalPlugin(afterAccountConfigWritten);
|
||||
setActivePluginRegistry(createTestRegistry([{ pluginId: "signal", plugin, source: "test" }]));
|
||||
configMocks.readConfigFileSnapshot.mockResolvedValue({ ...baseConfigSnapshot });
|
||||
|
||||
await channelsAddCommand(
|
||||
{ channel: "signal", account: "ops", signalNumber: "+15550001" },
|
||||
runtime,
|
||||
{ hasFlags: true },
|
||||
);
|
||||
await runSignalAddCommand(afterAccountConfigWritten);
|
||||
|
||||
expect(configMocks.writeConfigFile).toHaveBeenCalledTimes(1);
|
||||
expect(runtime.exit).not.toHaveBeenCalled();
|
||||
|
||||
79
src/commands/channels.plugin-install.test-helpers.ts
Normal file
79
src/commands/channels.plugin-install.test-helpers.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { vi } from "vitest";
|
||||
import type { ChannelPluginCatalogEntry } from "../channels/plugins/catalog.js";
|
||||
import type { ChannelPlugin } from "../channels/plugins/types.js";
|
||||
import { createChannelTestPluginBase, createTestRegistry } from "../test-utils/channel-plugins.js";
|
||||
|
||||
export function createMockChannelSetupPluginInstallModule(
|
||||
actual: typeof import("./channel-setup/plugin-install.js"),
|
||||
) {
|
||||
return {
|
||||
...actual,
|
||||
ensureChannelSetupPluginInstalled: vi.fn(async ({ cfg }) => ({ cfg, installed: true })),
|
||||
loadChannelSetupPluginRegistrySnapshotForChannel: vi.fn(() => createTestRegistry()),
|
||||
};
|
||||
}
|
||||
|
||||
export function createMSTeamsCatalogEntry(): ChannelPluginCatalogEntry {
|
||||
return {
|
||||
id: "msteams",
|
||||
pluginId: "@openclaw/msteams-plugin",
|
||||
meta: {
|
||||
id: "msteams",
|
||||
label: "Microsoft Teams",
|
||||
selectionLabel: "Microsoft Teams",
|
||||
docsPath: "/channels/msteams",
|
||||
blurb: "teams channel",
|
||||
},
|
||||
install: {
|
||||
npmSpec: "@openclaw/msteams",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function createMSTeamsSetupPlugin(): ChannelPlugin {
|
||||
return {
|
||||
...createChannelTestPluginBase({
|
||||
id: "msteams",
|
||||
label: "Microsoft Teams",
|
||||
docsPath: "/channels/msteams",
|
||||
}),
|
||||
setup: {
|
||||
applyAccountConfig: vi.fn(({ cfg, input }) => ({
|
||||
...cfg,
|
||||
channels: {
|
||||
...cfg.channels,
|
||||
msteams: {
|
||||
enabled: true,
|
||||
tenantId: input.token,
|
||||
},
|
||||
},
|
||||
})),
|
||||
},
|
||||
} as ChannelPlugin;
|
||||
}
|
||||
|
||||
export function createMSTeamsDeletePlugin(): ChannelPlugin {
|
||||
return {
|
||||
...createChannelTestPluginBase({
|
||||
id: "msteams",
|
||||
label: "Microsoft Teams",
|
||||
docsPath: "/channels/msteams",
|
||||
}),
|
||||
config: {
|
||||
...createChannelTestPluginBase({
|
||||
id: "msteams",
|
||||
label: "Microsoft Teams",
|
||||
docsPath: "/channels/msteams",
|
||||
}).config,
|
||||
deleteAccount: vi.fn(({ cfg }: { cfg: Record<string, unknown> }) => {
|
||||
const channels = (cfg.channels as Record<string, unknown> | undefined) ?? {};
|
||||
const nextChannels = { ...channels };
|
||||
delete nextChannels.msteams;
|
||||
return {
|
||||
...cfg,
|
||||
channels: nextChannels,
|
||||
};
|
||||
}),
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,12 +1,16 @@
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { ChannelPluginCatalogEntry } from "../channels/plugins/catalog.js";
|
||||
import { setActivePluginRegistry } from "../plugins/runtime.js";
|
||||
import { createChannelTestPluginBase, createTestRegistry } from "../test-utils/channel-plugins.js";
|
||||
import { createTestRegistry } from "../test-utils/channel-plugins.js";
|
||||
import {
|
||||
ensureChannelSetupPluginInstalled,
|
||||
loadChannelSetupPluginRegistrySnapshotForChannel,
|
||||
} from "./channel-setup/plugin-install.js";
|
||||
import { configMocks } from "./channels.mock-harness.js";
|
||||
import {
|
||||
createMSTeamsCatalogEntry,
|
||||
createMSTeamsDeletePlugin,
|
||||
} from "./channels.plugin-install.test-helpers.js";
|
||||
import { baseConfigSnapshot, createTestRuntime } from "./test-runtime-config-helpers.js";
|
||||
|
||||
const catalogMocks = vi.hoisted(() => ({
|
||||
@@ -23,11 +27,9 @@ vi.mock("../channels/plugins/catalog.js", async (importOriginal) => {
|
||||
|
||||
vi.mock("./channel-setup/plugin-install.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("./channel-setup/plugin-install.js")>();
|
||||
return {
|
||||
...actual,
|
||||
ensureChannelSetupPluginInstalled: vi.fn(async ({ cfg }) => ({ cfg, installed: true })),
|
||||
loadChannelSetupPluginRegistrySnapshotForChannel: vi.fn(() => createTestRegistry()),
|
||||
};
|
||||
const { createMockChannelSetupPluginInstallModule } =
|
||||
await import("./channels.plugin-install.test-helpers.js");
|
||||
return createMockChannelSetupPluginInstallModule(actual);
|
||||
});
|
||||
|
||||
const runtime = createTestRuntime();
|
||||
@@ -70,44 +72,9 @@ describe("channelsRemoveCommand", () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
const catalogEntry: ChannelPluginCatalogEntry = {
|
||||
id: "msteams",
|
||||
pluginId: "@openclaw/msteams-plugin",
|
||||
meta: {
|
||||
id: "msteams",
|
||||
label: "Microsoft Teams",
|
||||
selectionLabel: "Microsoft Teams",
|
||||
docsPath: "/channels/msteams",
|
||||
blurb: "teams channel",
|
||||
},
|
||||
install: {
|
||||
npmSpec: "@openclaw/msteams",
|
||||
},
|
||||
};
|
||||
const catalogEntry: ChannelPluginCatalogEntry = createMSTeamsCatalogEntry();
|
||||
catalogMocks.listChannelPluginCatalogEntries.mockReturnValue([catalogEntry]);
|
||||
const scopedPlugin = {
|
||||
...createChannelTestPluginBase({
|
||||
id: "msteams",
|
||||
label: "Microsoft Teams",
|
||||
docsPath: "/channels/msteams",
|
||||
}),
|
||||
config: {
|
||||
...createChannelTestPluginBase({
|
||||
id: "msteams",
|
||||
label: "Microsoft Teams",
|
||||
docsPath: "/channels/msteams",
|
||||
}).config,
|
||||
deleteAccount: vi.fn(({ cfg }: { cfg: Record<string, unknown> }) => {
|
||||
const channels = (cfg.channels as Record<string, unknown> | undefined) ?? {};
|
||||
const nextChannels = { ...channels };
|
||||
delete nextChannels.msteams;
|
||||
return {
|
||||
...cfg,
|
||||
channels: nextChannels,
|
||||
};
|
||||
}),
|
||||
},
|
||||
};
|
||||
const scopedPlugin = createMSTeamsDeletePlugin();
|
||||
vi.mocked(loadChannelSetupPluginRegistrySnapshotForChannel)
|
||||
.mockReturnValueOnce(createTestRegistry())
|
||||
.mockReturnValueOnce(
|
||||
|
||||
Reference in New Issue
Block a user