





















@@ -2,7 +2,7 @@
22import { beforeEach, describe, expect, it, vi } from "vitest";
33import type { OpenClawConfig } from "../config/types.openclaw.js";
44import { resetCoreHealthChecksForTest } from "../flows/doctor-core-checks.js";
5-import { clearHealthChecksForTest } from "../flows/health-check-registry.js";
5+import { clearHealthChecksForTest, registerHealthCheck } from "../flows/health-check-registry.js";
66import { runDoctorLintCli } from "./doctor-lint.js";
7788const mocks = vi.hoisted(() => ({
@@ -192,6 +192,134 @@ describe("runDoctorLintCli", () => {
192192}
193193});
194194195+it("runs core contribution checks plus registered extension checks", async () => {
196+mocks.readConfigFileSnapshot.mockResolvedValue({
197+exists: true,
198+valid: true,
199+config: {},
200+path: "/tmp/openclaw.json",
201+});
202+registerHealthCheck({
203+id: "plugin/example/lint",
204+kind: "plugin",
205+description: "example plugin lint check",
206+async detect() {
207+return [
208+{
209+checkId: "plugin/example/lint",
210+severity: "info",
211+message: "plugin finding",
212+},
213+];
214+},
215+});
216+217+const stdout = vi.spyOn(process.stdout, "write").mockImplementation(() => true);
218+try {
219+const exitCode = await runDoctorLintCli(runtime, {
220+json: true,
221+onlyIds: ["core/doctor/final-config-validation", "plugin/example/lint"],
222+});
223+224+expect(exitCode).toBe(1);
225+const payload = JSON.parse(String(stdout.mock.calls.at(-1)?.[0]));
226+expect(payload.checksRun).toBe(2);
227+expect(payload.findings).toEqual([
228+{
229+checkId: "plugin/example/lint",
230+severity: "info",
231+message: "plugin finding",
232+},
233+]);
234+} finally {
235+stdout.mockRestore();
236+}
237+});
238+239+it("rejects extension checks that reuse ordered core check ids", async () => {
240+mocks.readConfigFileSnapshot.mockResolvedValue({
241+exists: true,
242+valid: true,
243+config: {},
244+path: "/tmp/openclaw.json",
245+});
246+registerHealthCheck({
247+id: "core/doctor/final-config-validation",
248+kind: "plugin",
249+description: "colliding plugin lint check",
250+async detect() {
251+return [];
252+},
253+});
254+255+await expect(runDoctorLintCli(runtime, { json: true })).rejects.toThrow(
256+"health check already registered: core/doctor/final-config-validation",
257+);
258+});
259+260+it("rejects registered core-kind checks that reuse ordered core check ids", async () => {
261+mocks.readConfigFileSnapshot.mockResolvedValue({
262+exists: true,
263+valid: true,
264+config: {},
265+path: "/tmp/openclaw.json",
266+});
267+registerHealthCheck({
268+id: "core/doctor/final-config-validation",
269+kind: "core",
270+description: "colliding core-kind lint check",
271+async detect() {
272+return [];
273+},
274+});
275+276+await expect(runDoctorLintCli(runtime, { json: true })).rejects.toThrow(
277+"health check already registered: core/doctor/final-config-validation",
278+);
279+});
280+281+it("rejects extension checks that claim unused reserved core doctor ids", async () => {
282+mocks.readConfigFileSnapshot.mockResolvedValue({
283+exists: true,
284+valid: true,
285+config: {},
286+path: "/tmp/openclaw.json",
287+});
288+registerHealthCheck({
289+id: "core/doctor/not-yet-owned",
290+kind: "plugin",
291+description: "reserved plugin lint check",
292+async detect() {
293+return [];
294+},
295+});
296+297+await expect(runDoctorLintCli(runtime, { json: true })).rejects.toThrow(
298+"health check already registered: core/doctor/not-yet-owned",
299+);
300+});
301+302+it("rejects registered core-kind checks that claim unused reserved core doctor ids", async () => {
303+mocks.readConfigFileSnapshot.mockResolvedValue({
304+exists: true,
305+valid: true,
306+config: {},
307+path: "/tmp/openclaw.json",
308+});
309+registerHealthCheck({
310+id: "core/doctor/not-yet-owned",
311+kind: "core",
312+description: "reserved core-kind lint check",
313+async detect() {
314+return [];
315+},
316+});
317+318+await expect(runDoctorLintCli(runtime, { json: true })).rejects.toThrow(
319+"health check already registered: core/doctor/not-yet-owned",
320+);
321+});
322+195323it("rejects invalid severity thresholds", async () => {
196324await expect(runDoctorLintCli(runtime, { severityMin: "warnng" })).rejects.toThrow(
197325"Invalid --severity-min value",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。