mirror of https://github.com/openclaw/openclaw.git
130 lines
3.7 KiB
TypeScript
130 lines
3.7 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { validateConfigObjectRaw } from "./validation.js";
|
|
|
|
describe("config validation SecretRef policy guards", () => {
|
|
it("surfaces a policy error for hooks.token SecretRef objects", () => {
|
|
const result = validateConfigObjectRaw({
|
|
hooks: {
|
|
token: {
|
|
source: "env",
|
|
provider: "default",
|
|
id: "HOOK_TOKEN",
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(result.ok).toBe(false);
|
|
if (!result.ok) {
|
|
const issue = result.issues.find((entry) => entry.path === "hooks.token");
|
|
expect(issue).toBeDefined();
|
|
expect(issue?.message).toContain("SecretRef objects are not supported at hooks.token");
|
|
expect(issue?.message).toContain(
|
|
"https://docs.openclaw.ai/reference/secretref-credential-surface",
|
|
);
|
|
expect(
|
|
result.issues.some(
|
|
(entry) =>
|
|
entry.path === "hooks.token" &&
|
|
entry.message.includes("Invalid input: expected string, received object"),
|
|
),
|
|
).toBe(false);
|
|
}
|
|
});
|
|
|
|
it("keeps standard schema errors for non-SecretRef objects", () => {
|
|
const result = validateConfigObjectRaw({
|
|
hooks: {
|
|
token: {
|
|
unexpected: "value",
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(result.ok).toBe(false);
|
|
if (!result.ok) {
|
|
const issue = result.issues.find((entry) => entry.path === "hooks.token");
|
|
expect(issue).toBeDefined();
|
|
expect(issue?.message).toBe("Invalid input: expected string, received object");
|
|
}
|
|
});
|
|
|
|
it("allows env-template strings on unsupported mutable paths", () => {
|
|
const result = validateConfigObjectRaw({
|
|
hooks: {
|
|
token: "${HOOK_TOKEN}",
|
|
},
|
|
});
|
|
|
|
expect(result.ok).toBe(true);
|
|
});
|
|
|
|
it("replaces derived unrecognized-key errors with policy guidance for discord thread binding webhookToken", () => {
|
|
const result = validateConfigObjectRaw({
|
|
channels: {
|
|
discord: {
|
|
threadBindings: {
|
|
webhookToken: {
|
|
source: "env",
|
|
provider: "default",
|
|
id: "DISCORD_THREAD_BINDING_WEBHOOK_TOKEN",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(result.ok).toBe(false);
|
|
if (!result.ok) {
|
|
const policyIssue = result.issues.find(
|
|
(entry) => entry.path === "channels.discord.threadBindings.webhookToken",
|
|
);
|
|
expect(policyIssue).toBeDefined();
|
|
expect(policyIssue?.message).toContain(
|
|
"SecretRef objects are not supported at channels.discord.threadBindings.webhookToken",
|
|
);
|
|
expect(
|
|
result.issues.some(
|
|
(entry) =>
|
|
entry.path === "channels.discord.threadBindings" &&
|
|
entry.message.includes('Unrecognized key: "webhookToken"'),
|
|
),
|
|
).toBe(false);
|
|
}
|
|
});
|
|
|
|
it("preserves unrelated unknown-key errors when policy and typos coexist", () => {
|
|
const result = validateConfigObjectRaw({
|
|
channels: {
|
|
discord: {
|
|
threadBindings: {
|
|
webhookToken: {
|
|
source: "env",
|
|
provider: "default",
|
|
id: "DISCORD_THREAD_BINDING_WEBHOOK_TOKEN",
|
|
},
|
|
webhookTokne: "typo",
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(result.ok).toBe(false);
|
|
if (!result.ok) {
|
|
expect(
|
|
result.issues.some(
|
|
(entry) =>
|
|
entry.path === "channels.discord.threadBindings.webhookToken" &&
|
|
entry.message.includes("SecretRef objects are not supported"),
|
|
),
|
|
).toBe(true);
|
|
expect(
|
|
result.issues.some(
|
|
(entry) =>
|
|
entry.path === "channels.discord.threadBindings" &&
|
|
entry.message.includes("webhookTokne"),
|
|
),
|
|
).toBe(true);
|
|
}
|
|
});
|
|
});
|