Files
openclaw/src/config/doc-baseline.integration.test.ts
Peter Steinberger b5265a07d7 refactor: replace 156k-line generated baselines with SHA-256 hash files
Config and Plugin SDK drift detection now compares SHA-256 hashes instead
of full JSON content. The .sha256 files (6 lines total) are tracked in git;
the full JSON baselines are gitignored and generated locally for inspection.

Same CI guarantee, zero repo churn on schema changes.
2026-04-04 16:49:21 +09:00

157 lines
5.2 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 ConfigDocBaselineEntry,
flattenConfigDocBaselineEntries,
renderConfigDocBaselineArtifacts,
writeConfigDocBaselineArtifacts,
} from "./doc-baseline.js";
describe("config doc baseline integration", () => {
const tempRoots: string[] = [];
let sharedRenderedPromise: Promise<
Awaited<ReturnType<typeof renderConfigDocBaselineArtifacts>>
> | null = null;
let sharedByPathPromise: Promise<Map<string, ConfigDocBaselineEntry>> | null = null;
function getSharedRendered() {
sharedRenderedPromise ??= renderConfigDocBaselineArtifacts();
return sharedRenderedPromise;
}
function getSharedByPath() {
sharedByPathPromise ??= getSharedRendered().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 getSharedRendered();
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("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 hash files", 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,
rendered,
});
expect(initial.wrote).toBe(true);
const current = await writeConfigDocBaselineArtifacts({
repoRoot: tempRoot,
check: true,
rendered,
});
expect(current.changed).toBe(false);
// Corrupt the hash file to simulate drift
await fs.writeFile(
path.join(tempRoot, "docs/.generated/config-baseline.sha256"),
"0000000000000000000000000000000000000000000000000000000000000000 config-baseline.json\n",
"utf8",
);
const stale = await writeConfigDocBaselineArtifacts({
repoRoot: tempRoot,
check: true,
rendered,
});
expect(stale.changed).toBe(true);
expect(stale.wrote).toBe(false);
});
});