
























@@ -0,0 +1,114 @@
1+import { promises as fs } from "node:fs";
2+import { tmpdir } from "node:os";
3+import { join } from "node:path";
4+import { afterEach, describe, expect, it } from "vitest";
5+import type { OpenClawConfig } from "../config/types.openclaw.js";
6+import { CORE_HEALTH_CHECKS } from "./doctor-core-checks.js";
7+import type { HealthCheck } from "./health-checks.js";
8+9+const runtime = { log() {}, error() {}, exit() {} };
10+11+function getCheck(id: string): HealthCheck {
12+const check = CORE_HEALTH_CHECKS.find((entry) => entry.id === id);
13+if (!check) {
14+throw new Error(`Missing health check ${id}`);
15+}
16+return check;
17+}
18+19+describe("doctor core skills readiness smoke", () => {
20+let tmp: string | undefined;
21+22+afterEach(async () => {
23+if (tmp !== undefined) {
24+await fs.rm(tmp, { recursive: true, force: true });
25+tmp = undefined;
26+}
27+});
28+29+it("detects and repairs a real unavailable workspace skill", async () => {
30+tmp = await fs.mkdtemp(join(tmpdir(), "openclaw-health-skills-"));
31+const skillDir = join(tmp, "skills", "missing-tool");
32+await fs.mkdir(skillDir, { recursive: true });
33+await fs.writeFile(
34+join(skillDir, "SKILL.md"),
35+`---
36+name: missing-tool
37+description: Missing tool
38+metadata: '{"openclaw":{"requires":{"bins":["openclaw-test-missing-skill-bin"]}}}'
39+---
40+41+# Missing tool
42+`,
43+"utf-8",
44+);
45+const cfg: OpenClawConfig = {
46+agents: {
47+defaults: {
48+workspace: tmp,
49+skills: ["missing-tool"],
50+},
51+},
52+};
53+const check = getCheck("core/doctor/skills-readiness");
54+55+const findings = await check.detect({
56+mode: "lint",
57+ runtime,
58+ cfg,
59+cwd: tmp,
60+});
61+expect(findings).toContainEqual(
62+expect.objectContaining({
63+checkId: "core/doctor/skills-readiness",
64+severity: "warning",
65+path: "skills.entries.missing-tool.enabled",
66+}),
67+);
68+await expect(
69+check.detect(
70+{
71+mode: "fix",
72+ runtime,
73+ cfg,
74+cwd: tmp,
75+},
76+{ paths: ["skills.entries.other-tool.enabled"] },
77+),
78+).resolves.toEqual([]);
79+await expect(
80+check.detect(
81+{
82+mode: "fix",
83+ runtime,
84+ cfg,
85+cwd: tmp,
86+},
87+{ paths: ["skills.entries.missing-tool.enabled"] },
88+),
89+).resolves.toContainEqual(
90+expect.objectContaining({
91+path: "skills.entries.missing-tool.enabled",
92+}),
93+);
94+95+const repaired = await check.repair?.(
96+{
97+mode: "fix",
98+ runtime,
99+ cfg,
100+cwd: tmp,
101+},
102+findings,
103+);
104+expect(repaired?.config?.skills?.entries?.["missing-tool"]).toEqual({ enabled: false });
105+expect(repaired?.changes).toContain("Disabled unavailable skill missing-tool.");
106+expect(repaired?.effects).toContainEqual(
107+expect.objectContaining({
108+kind: "config",
109+action: "disable-skill",
110+target: "skills.entries.missing-tool.enabled",
111+}),
112+);
113+});
114+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。