Spracherkennung für: .ts vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]
import { describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../../config/config.js";
import { runDoctorRepairSequence } from "./repair-sequencing.js";
vi.mock("./shared/channel-doctor.js", () => ({
collectChannelDoctorRepairMutations: ({ cfg }: { cfg: OpenClawConfig }) => {
const allowFrom = cfg.channels?.discord?.allowFrom as unknown[] | undefined;
if (allowFrom?.[0] === 123) {
return [
{
config: {
...cfg,
channels: {
...cfg.channels,
discord: {
...cfg.channels?.discord,
allowFrom: ["123"],
},
},
},
changes: ["channels.discord.allowFrom: converted 1 numeric ID to strings"],
},
];
}
if (allowFrom?.[0] === 106232522769186816) {
return [
{
config: cfg,
changes: [],
warnings: [
"channels.discord.allowFrom[0] cannot be auto-repaired because it is not a safe integer",
],
},
];
}
return [];
},
createChannelDoctorEmptyAllowlistPolicyHooks: () => ({
extraWarningsForAccount: () => [],
shouldSkipDefaultEmptyGroupAllowlistWarning: () => false,
}),
}));
vi.mock("./shared/empty-allowlist-scan.js", () => ({
scanEmptyAllowlistPolicyWarnings: (cfg: OpenClawConfig) =>
cfg.channels?.signal
? ["channels.signal.accounts.ops\u001B[31m-team\u001B[0m\r\nnext.dmPolicy warning"]
: [],
}));
vi.mock("./shared/allowlist-policy-repair.js", () => ({
maybeRepairAllowlistPolicyAllowFrom: async (cfg: OpenClawConfig) => ({
config: cfg,
changes: [],
}),
}));
vi.mock("./shared/bundled-plugin-load-paths.js", () => ({
maybeRepairBundledPluginLoadPaths: (cfg: OpenClawConfig) => ({
config: cfg,
changes: [],
}),
}));
vi.mock("./shared/open-policy-allowfrom.js", () => ({
maybeRepairOpenPolicyAllowFrom: (cfg: OpenClawConfig) => ({
config: cfg,
changes: [],
}),
}));
vi.mock("./shared/stale-plugin-config.js", () => ({
maybeRepairStalePluginConfig: (cfg: OpenClawConfig) => ({
config: cfg,
changes: [],
}),
}));
vi.mock("./shared/legacy-tools-by-sender.js", () => ({
maybeRepairLegacyToolsBySenderKeys: (cfg: OpenClawConfig) => {
const channels = cfg.channels as Record<string, unknown> | undefined;
const tools = channels?.tools as
| { exec?: { toolsBySender?: Record<string, unknown> } }
| undefined;
const bySender = tools?.exec?.toolsBySender;
const rawKey = bySender
? Object.keys(bySender).find((key) => !key.startsWith("id:"))
: undefined;
if (!bySender || !rawKey) {
return { config: cfg, changes: [] };
}
const targetKey = `id:${rawKey.trim()}`;
return {
config: {
...cfg,
channels: {
...cfg.channels,
tools: {
...(channels?.tools as Record<string, unknown> | undefined),
exec: {
...tools?.exec,
toolsBySender: {
[targetKey]: bySender[rawKey],
},
},
},
},
},
changes: [
`channels.tools.exec.toolsBySender: migrated 1 legacy key to typed id: entries (${rawKey} -> ${targetKey})`,
],
};
},
}));
vi.mock("./shared/exec-safe-bins.js", () => ({
maybeRepairExecSafeBinProfiles: (cfg: OpenClawConfig) => ({
config: cfg,
changes: [],
}),
}));
describe("doctor repair sequencing", () => {
it("applies ordered repairs and sanitizes empty-allowlist warnings", async () => {
const result = await runDoctorRepairSequence({
state: {
cfg: {
channels: {
discord: {
allowFrom: [123],
},
tools: {
exec: {
toolsBySender: {
"bad\u001B[31m-key\u001B[0m\r\nnext": { enabled: true },
},
},
},
signal: {
accounts: {
"ops\u001B[31m-team\u001B[0m\r\nnext": {
dmPolicy: "allowlist",
},
},
},
},
} as unknown as OpenClawConfig,
candidate: {
channels: {
discord: {
allowFrom: [123],
},
tools: {
exec: {
toolsBySender: {
"bad\u001B[31m-key\u001B[0m\r\nnext": { enabled: true },
},
},
},
signal: {
accounts: {
"ops\u001B[31m-team\u001B[0m\r\nnext": {
dmPolicy: "allowlist",
},
},
},
},
} as unknown as OpenClawConfig,
pendingChanges: false,
fixHints: [],
},
doctorFixCommand: "openclaw doctor --fix",
});
expect(result.state.pendingChanges).toBe(true);
expect(result.state.candidate.channels?.discord?.allowFrom).toEqual(["123"]);
expect(result.changeNotes).toEqual(
expect.arrayContaining([
expect.stringContaining("channels.discord.allowFrom: converted 1 numeric ID to strings"),
expect.stringContaining(
"channels.tools.exec.toolsBySender: migrated 1 legacy key to typed id: entries",
),
]),
);
expect(result.changeNotes.join("\n")).toContain("bad-keynext -> id:bad-keynext");
expect(result.changeNotes.join("\n")).not.toContain("\u001B");
expect(result.changeNotes.join("\n")).not.toContain("\r");
expect(result.warningNotes).toEqual(
expect.arrayContaining([
expect.stringContaining("channels.signal.accounts.ops-teamnext.dmPolicy"),
]),
);
expect(result.warningNotes.join("\n")).not.toContain("\u001B");
expect(result.warningNotes.join("\n")).not.toContain("\r");
});
it("emits Discord warnings when unsafe numeric ids block repair", async () => {
const result = await runDoctorRepairSequence({
state: {
cfg: {
channels: {
discord: {
allowFrom: [106232522769186816],
},
},
} as unknown as OpenClawConfig,
candidate: {
channels: {
discord: {
allowFrom: [106232522769186816],
},
},
} as unknown as OpenClawConfig,
pendingChanges: false,
fixHints: [],
},
doctorFixCommand: "openclaw doctor --fix",
});
expect(result.changeNotes).toEqual([]);
expect(result.warningNotes).toHaveLength(1);
expect(result.warningNotes[0]).toContain("cannot be auto-repaired");
expect(result.warningNotes[0]).toContain("channels.discord.allowFrom[0]");
expect(result.state.pendingChanges).toBe(false);
expect(result.state.candidate.channels?.discord?.allowFrom).toEqual([106232522769186816]);
});
});
¤ Dauer der Verarbeitung: 0.28 Sekunden
(vorverarbeitet am 2026-04-27)
¤
*© Formatika GbR, Deutschland
|
|