mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-25 08:02:04 +00:00
208 lines
7.5 KiB
TypeScript
208 lines
7.5 KiB
TypeScript
import fs from "node:fs/promises";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { afterEach, describe, expect, it } from "vitest";
|
|
import {
|
|
type ConfigDocBaseline,
|
|
type ConfigDocBaselineEntry,
|
|
type ConfigDocBaselineArtifacts,
|
|
flattenConfigDocBaselineEntries,
|
|
renderConfigDocBaselineArtifacts,
|
|
writeConfigDocBaselineArtifacts,
|
|
} from "./doc-baseline.js";
|
|
|
|
describe("config doc baseline integration", () => {
|
|
const tempRoots: string[] = [];
|
|
const generatedBaselinePaths = {
|
|
combined: path.resolve(process.cwd(), "docs/.generated/config-baseline.json"),
|
|
core: path.resolve(process.cwd(), "docs/.generated/config-baseline.core.json"),
|
|
channel: path.resolve(process.cwd(), "docs/.generated/config-baseline.channel.json"),
|
|
plugin: path.resolve(process.cwd(), "docs/.generated/config-baseline.plugin.json"),
|
|
} satisfies Record<keyof ConfigDocBaselineArtifacts, string>;
|
|
let sharedBaselinePromise: Promise<ConfigDocBaseline> | null = null;
|
|
let sharedRenderedPromise: Promise<
|
|
Awaited<ReturnType<typeof renderConfigDocBaselineArtifacts>>
|
|
> | null = null;
|
|
const sharedGeneratedJsonPromises: Partial<
|
|
Record<keyof ConfigDocBaselineArtifacts, Promise<string>>
|
|
> = {};
|
|
let sharedByPathPromise: Promise<Map<string, ConfigDocBaselineEntry>> | null = null;
|
|
|
|
function getSharedBaseline() {
|
|
sharedBaselinePromise ??= fs
|
|
.readFile(generatedBaselinePaths.combined, "utf8")
|
|
.then((raw) => JSON.parse(raw) as ConfigDocBaseline);
|
|
return sharedBaselinePromise;
|
|
}
|
|
|
|
function getSharedRendered() {
|
|
sharedRenderedPromise ??= renderConfigDocBaselineArtifacts(getSharedBaseline());
|
|
return sharedRenderedPromise;
|
|
}
|
|
|
|
function getGeneratedJson(kind: keyof ConfigDocBaselineArtifacts) {
|
|
sharedGeneratedJsonPromises[kind] ??= fs.readFile(generatedBaselinePaths[kind], "utf8");
|
|
return sharedGeneratedJsonPromises[kind];
|
|
}
|
|
|
|
function getSharedByPath() {
|
|
sharedByPathPromise ??= getSharedBaseline().then(
|
|
(baseline) =>
|
|
new Map(flattenConfigDocBaselineEntries(baseline).map((entry) => [entry.path, entry])),
|
|
);
|
|
return sharedByPathPromise;
|
|
}
|
|
|
|
afterEach(async () => {
|
|
await Promise.all(
|
|
tempRoots.splice(0).map(async (tempRoot) => {
|
|
await fs.rm(tempRoot, { recursive: true, force: true });
|
|
}),
|
|
);
|
|
});
|
|
|
|
it("is deterministic across repeated runs", async () => {
|
|
const baseline = await getSharedBaseline();
|
|
const first = await renderConfigDocBaselineArtifacts(baseline);
|
|
const second = await renderConfigDocBaselineArtifacts(baseline);
|
|
|
|
expect(second.json.combined).toBe(first.json.combined);
|
|
expect(second.json.core).toBe(first.json.core);
|
|
expect(second.json.channel).toBe(first.json.channel);
|
|
expect(second.json.plugin).toBe(first.json.plugin);
|
|
});
|
|
|
|
it("matches the checked-in generated baseline artifacts", async () => {
|
|
const [rendered, generatedCombined, generatedCore, generatedChannel, generatedPlugin] =
|
|
await Promise.all([
|
|
getSharedRendered(),
|
|
getGeneratedJson("combined"),
|
|
getGeneratedJson("core"),
|
|
getGeneratedJson("channel"),
|
|
getGeneratedJson("plugin"),
|
|
]);
|
|
|
|
expect(rendered.json.combined).toBe(generatedCombined);
|
|
expect(rendered.json.core).toBe(generatedCore);
|
|
expect(rendered.json.channel).toBe(generatedChannel);
|
|
expect(rendered.json.plugin).toBe(generatedPlugin);
|
|
});
|
|
|
|
it("includes core, channel, and plugin config metadata", async () => {
|
|
const byPath = await getSharedByPath();
|
|
|
|
expect(byPath.get("gateway.auth.token")).toMatchObject({
|
|
kind: "core",
|
|
sensitive: true,
|
|
});
|
|
expect(byPath.get("channels.telegram.botToken")).toMatchObject({
|
|
kind: "channel",
|
|
sensitive: true,
|
|
});
|
|
expect(byPath.get("plugins.entries.voice-call.config.twilio.authToken")).toMatchObject({
|
|
kind: "plugin",
|
|
sensitive: true,
|
|
});
|
|
});
|
|
|
|
it("preserves help text and tags from merged schema hints", async () => {
|
|
const byPath = await getSharedByPath();
|
|
const tokenEntry = byPath.get("gateway.auth.token");
|
|
|
|
expect(tokenEntry?.help).toContain("gateway access");
|
|
expect(tokenEntry?.tags).toContain("auth");
|
|
expect(tokenEntry?.tags).toContain("security");
|
|
});
|
|
|
|
it("omits legacy hooks.internal.handlers from the generated baseline", async () => {
|
|
const byPath = await getSharedByPath();
|
|
|
|
expect(byPath.get("hooks.internal.handlers")).toBeUndefined();
|
|
expect(byPath.get("hooks.internal.handlers.*.module")).toBeUndefined();
|
|
});
|
|
|
|
it("uses human-readable channel metadata for top-level channel sections", async () => {
|
|
const byPath = await getSharedByPath();
|
|
|
|
expect(byPath.get("channels.discord")).toMatchObject({
|
|
label: "Discord",
|
|
help: "very well supported right now.",
|
|
});
|
|
expect(byPath.get("channels.msteams")).toMatchObject({
|
|
label: "Microsoft Teams",
|
|
help: "Teams SDK; enterprise support.",
|
|
});
|
|
expect(byPath.get("channels.matrix")).toMatchObject({
|
|
label: "Matrix",
|
|
help: "open protocol; install the plugin to enable.",
|
|
});
|
|
expect(byPath.get("channels.msteams")?.label).not.toContain("@openclaw/");
|
|
expect(byPath.get("channels.matrix")?.help).not.toContain("homeserver");
|
|
});
|
|
|
|
it("matches array help hints that still use [] notation", async () => {
|
|
const byPath = await getSharedByPath();
|
|
|
|
expect(byPath.get("session.sendPolicy.rules.*.match.keyPrefix")).toMatchObject({
|
|
help: expect.stringContaining("prefer rawKeyPrefix when exact full-key matching is required"),
|
|
sensitive: false,
|
|
});
|
|
});
|
|
|
|
it("walks union branches for nested config keys", async () => {
|
|
const byPath = await getSharedByPath();
|
|
|
|
expect(byPath.get("bindings.*")).toMatchObject({
|
|
hasChildren: true,
|
|
});
|
|
expect(byPath.get("bindings.*.type")).toBeDefined();
|
|
expect(byPath.get("bindings.*.match.channel")).toBeDefined();
|
|
expect(byPath.get("bindings.*.match.peer.id")).toBeDefined();
|
|
});
|
|
|
|
it("supports check mode for stale generated artifacts", async () => {
|
|
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-config-doc-baseline-"));
|
|
tempRoots.push(tempRoot);
|
|
const rendered = getSharedRendered();
|
|
|
|
const initial = await writeConfigDocBaselineArtifacts({
|
|
repoRoot: tempRoot,
|
|
combinedPath: "docs/.generated/config-baseline.json",
|
|
corePath: "docs/.generated/config-baseline.core.json",
|
|
channelPath: "docs/.generated/config-baseline.channel.json",
|
|
pluginPath: "docs/.generated/config-baseline.plugin.json",
|
|
rendered,
|
|
});
|
|
expect(initial.wrote).toBe(true);
|
|
|
|
const current = await writeConfigDocBaselineArtifacts({
|
|
repoRoot: tempRoot,
|
|
combinedPath: "docs/.generated/config-baseline.json",
|
|
corePath: "docs/.generated/config-baseline.core.json",
|
|
channelPath: "docs/.generated/config-baseline.channel.json",
|
|
pluginPath: "docs/.generated/config-baseline.plugin.json",
|
|
check: true,
|
|
rendered,
|
|
});
|
|
expect(current.changed).toBe(false);
|
|
|
|
await fs.writeFile(
|
|
path.join(tempRoot, "docs/.generated/config-baseline.json"),
|
|
'{"generatedBy":"broken","entries":[]}\n',
|
|
"utf8",
|
|
);
|
|
|
|
const stale = await writeConfigDocBaselineArtifacts({
|
|
repoRoot: tempRoot,
|
|
combinedPath: "docs/.generated/config-baseline.json",
|
|
corePath: "docs/.generated/config-baseline.core.json",
|
|
channelPath: "docs/.generated/config-baseline.channel.json",
|
|
pluginPath: "docs/.generated/config-baseline.plugin.json",
|
|
check: true,
|
|
rendered,
|
|
});
|
|
expect(stale.changed).toBe(true);
|
|
expect(stale.wrote).toBe(false);
|
|
});
|
|
});
|