Files
openclaw/src/gateway/server-methods/secrets.test.ts
2026-03-07 10:41:05 +00:00

198 lines
5.9 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import { createSecretsHandlers } from "./secrets.js";
async function invokeSecretsReload(params: {
handlers: ReturnType<typeof createSecretsHandlers>;
respond: ReturnType<typeof vi.fn>;
}) {
await params.handlers["secrets.reload"]({
req: { type: "req", id: "1", method: "secrets.reload" },
params: {},
client: null,
isWebchatConnect: () => false,
respond: params.respond as unknown as Parameters<
ReturnType<typeof createSecretsHandlers>["secrets.reload"]
>[0]["respond"],
context: {} as never,
});
}
async function invokeSecretsResolve(params: {
handlers: ReturnType<typeof createSecretsHandlers>;
respond: ReturnType<typeof vi.fn>;
commandName: unknown;
targetIds: unknown;
}) {
await params.handlers["secrets.resolve"]({
req: { type: "req", id: "1", method: "secrets.resolve" },
params: {
commandName: params.commandName,
targetIds: params.targetIds,
},
client: null,
isWebchatConnect: () => false,
respond: params.respond as unknown as Parameters<
ReturnType<typeof createSecretsHandlers>["secrets.resolve"]
>[0]["respond"],
context: {} as never,
});
}
describe("secrets handlers", () => {
function createHandlers(overrides?: {
reloadSecrets?: () => Promise<{ warningCount: number }>;
resolveSecrets?: (params: { commandName: string; targetIds: string[] }) => Promise<{
assignments: Array<{ path: string; pathSegments: string[]; value: unknown }>;
diagnostics: string[];
inactiveRefPaths: string[];
}>;
}) {
const reloadSecrets = overrides?.reloadSecrets ?? (async () => ({ warningCount: 0 }));
const resolveSecrets =
overrides?.resolveSecrets ??
(async () => ({
assignments: [],
diagnostics: [],
inactiveRefPaths: [],
}));
return createSecretsHandlers({
reloadSecrets,
resolveSecrets,
});
}
it("responds with warning count on successful reload", async () => {
const handlers = createHandlers({
reloadSecrets: vi.fn().mockResolvedValue({ warningCount: 2 }),
});
const respond = vi.fn();
await invokeSecretsReload({ handlers, respond });
expect(respond).toHaveBeenCalledWith(true, { ok: true, warningCount: 2 });
});
it("returns unavailable when reload fails", async () => {
const handlers = createHandlers({
reloadSecrets: vi.fn().mockRejectedValue(new Error("reload failed")),
});
const respond = vi.fn();
await invokeSecretsReload({ handlers, respond });
expect(respond).toHaveBeenCalledWith(
false,
undefined,
expect.objectContaining({
code: "UNAVAILABLE",
message: "Error: reload failed",
}),
);
});
it("resolves requested command secret assignments from the active snapshot", async () => {
const resolveSecrets = vi.fn().mockResolvedValue({
assignments: [{ path: "talk.apiKey", pathSegments: ["talk", "apiKey"], value: "sk" }],
diagnostics: ["note"],
inactiveRefPaths: ["talk.apiKey"],
});
const handlers = createHandlers({ resolveSecrets });
const respond = vi.fn();
await invokeSecretsResolve({
handlers,
respond,
commandName: "memory status",
targetIds: ["talk.apiKey"],
});
expect(resolveSecrets).toHaveBeenCalledWith({
commandName: "memory status",
targetIds: ["talk.apiKey"],
});
expect(respond).toHaveBeenCalledWith(true, {
ok: true,
assignments: [{ path: "talk.apiKey", pathSegments: ["talk", "apiKey"], value: "sk" }],
diagnostics: ["note"],
inactiveRefPaths: ["talk.apiKey"],
});
});
it("rejects invalid secrets.resolve params", async () => {
const handlers = createHandlers();
const respond = vi.fn();
await invokeSecretsResolve({
handlers,
respond,
commandName: "",
targetIds: "bad",
});
expect(respond).toHaveBeenCalledWith(
false,
undefined,
expect.objectContaining({
code: "INVALID_REQUEST",
}),
);
});
it("rejects secrets.resolve params when targetIds entries are not strings", async () => {
const resolveSecrets = vi.fn();
const handlers = createHandlers({ resolveSecrets });
const respond = vi.fn();
await invokeSecretsResolve({
handlers,
respond,
commandName: "memory status",
targetIds: ["talk.apiKey", 12],
});
expect(resolveSecrets).not.toHaveBeenCalled();
expect(respond).toHaveBeenCalledWith(
false,
undefined,
expect.objectContaining({
code: "INVALID_REQUEST",
message: "invalid secrets.resolve params: targetIds",
}),
);
});
it("rejects unknown secrets.resolve target ids", async () => {
const resolveSecrets = vi.fn();
const handlers = createHandlers({ resolveSecrets });
const respond = vi.fn();
await invokeSecretsResolve({
handlers,
respond,
commandName: "memory status",
targetIds: ["unknown.target"],
});
expect(resolveSecrets).not.toHaveBeenCalled();
expect(respond).toHaveBeenCalledWith(
false,
undefined,
expect.objectContaining({
code: "INVALID_REQUEST",
message: 'invalid secrets.resolve params: unknown target id "unknown.target"',
}),
);
});
it("returns unavailable when secrets.resolve handler returns an invalid payload shape", async () => {
const resolveSecrets = vi.fn().mockResolvedValue({
assignments: [{ path: "talk.apiKey", pathSegments: [""], value: "sk" }],
diagnostics: [],
inactiveRefPaths: [],
});
const handlers = createHandlers({ resolveSecrets });
const respond = vi.fn();
await invokeSecretsResolve({
handlers,
respond,
commandName: "memory status",
targetIds: ["talk.apiKey"],
});
expect(respond).toHaveBeenCalledWith(
false,
undefined,
expect.objectContaining({
code: "UNAVAILABLE",
}),
);
});
});