openclaw/src/config/validation.policy.test.ts

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);
}
});
});