






















@@ -88,9 +88,24 @@ function createAgentDir(agentId: string, includeNestedAgentDir = true) {
8888fs.mkdirSync(targetDir, { recursive: true });
8989}
909091-const OAUTH_PROMPT_MATCHER = expect.objectContaining({
92-message: expect.stringContaining("Create OAuth dir at"),
93-});
91+type RuntimeRepairPrompt = {
92+initialValue?: boolean;
93+message?: string;
94+requiresInteractiveConfirmation?: boolean;
95+};
96+97+function repairPromptCalls(confirmRuntimeRepair: {
98+mock: { calls: unknown[][] };
99+}): RuntimeRepairPrompt[] {
100+return confirmRuntimeRepair.mock.calls.map((call) => call[0] as RuntimeRepairPrompt);
101+}
102+103+function hasRepairPromptMessage(
104+confirmRuntimeRepair: { mock: { calls: unknown[][] } },
105+text: string,
106+): boolean {
107+return repairPromptCalls(confirmRuntimeRepair).some((prompt) => prompt.message?.includes(text));
108+}
9410995110async function runStateIntegrity(cfg: OpenClawConfig) {
96111setupSessionState(cfg, process.env, process.env.HOME ?? "");
@@ -153,7 +168,7 @@ describe("doctor state integrity oauth dir checks", () => {
153168it("does not prompt for oauth dir when no whatsapp/pairing config is active", async () => {
154169const cfg: OpenClawConfig = {};
155170const confirmRuntimeRepair = await runStateIntegrity(cfg);
156-expect(confirmRuntimeRepair).not.toHaveBeenCalledWith(OAUTH_PROMPT_MATCHER);
171+expect(hasRepairPromptMessage(confirmRuntimeRepair, "Create OAuth dir at")).toBe(false);
157172const text = stateIntegrityText();
158173expect(text).toContain("OAuth dir not present");
159174expect(text).not.toContain("CRITICAL: OAuth dir missing");
@@ -166,7 +181,7 @@ describe("doctor state integrity oauth dir checks", () => {
166181},
167182};
168183const confirmRuntimeRepair = await runStateIntegrity(cfg);
169-expect(confirmRuntimeRepair).not.toHaveBeenCalledWith(OAUTH_PROMPT_MATCHER);
184+expect(hasRepairPromptMessage(confirmRuntimeRepair, "Create OAuth dir at")).toBe(false);
170185expect(stateIntegrityText()).toContain("OAuth dir not present");
171186expect(stateIntegrityText()).not.toContain("CRITICAL: OAuth dir missing");
172187});
@@ -180,14 +195,14 @@ describe("doctor state integrity oauth dir checks", () => {
180195},
181196};
182197const confirmRuntimeRepair = await runStateIntegrity(cfg);
183-expect(confirmRuntimeRepair).toHaveBeenCalledWith(OAUTH_PROMPT_MATCHER);
198+expect(hasRepairPromptMessage(confirmRuntimeRepair, "Create OAuth dir at")).toBe(true);
184199});
185200186201it("prompts for oauth dir when OPENCLAW_OAUTH_DIR is explicitly configured", async () => {
187202process.env.OPENCLAW_OAUTH_DIR = path.join(tempHome, ".oauth");
188203const cfg: OpenClawConfig = {};
189204const confirmRuntimeRepair = await runStateIntegrity(cfg);
190-expect(confirmRuntimeRepair).toHaveBeenCalledWith(OAUTH_PROMPT_MATCHER);
205+expect(hasRepairPromptMessage(confirmRuntimeRepair, "Create OAuth dir at")).toBe(true);
191206expect(stateIntegrityText()).toContain("CRITICAL: OAuth dir missing");
192207});
193208@@ -258,10 +273,8 @@ describe("doctor state integrity oauth dir checks", () => {
258273expect(text).toContain("automatic restart recovery tombstoned");
259274expect(text).toContain("agent:main:subagent:wedged-child");
260275expect(text).toContain("openclaw tasks maintenance --apply");
261-expect(confirmRuntimeRepair).toHaveBeenCalledWith(
262-expect.objectContaining({
263-message: expect.stringContaining("Clear stale aborted recovery flags"),
264-}),
276+expect(hasRepairPromptMessage(confirmRuntimeRepair, "Clear stale aborted recovery flags")).toBe(
277+true,
265278);
266279});
267280@@ -372,12 +385,10 @@ describe("doctor state integrity oauth dir checks", () => {
372385"These .jsonl files are no longer referenced by sessions.json",
373386);
374387expect(stateIntegrityText()).toContain("Examples: orphan-session.jsonl");
375-expect(confirmRuntimeRepair).toHaveBeenCalledWith(
376-expect.objectContaining({
377-message: expect.stringContaining("This only renames them to *.deleted.<timestamp>."),
378-requiresInteractiveConfirmation: true,
379-}),
388+const archivePrompt = repairPromptCalls(confirmRuntimeRepair).find((prompt) =>
389+prompt.message?.includes("This only renames them to *.deleted.<timestamp>."),
380390);
391+expect(archivePrompt?.requiresInteractiveConfirmation).toBe(true);
381392const files = fs.readdirSync(sessionsDir);
382393const archivedOrphanTranscripts = files.filter((name) =>
383394name.startsWith("orphan-session.jsonl.deleted."),
@@ -396,12 +407,10 @@ describe("doctor state integrity oauth dir checks", () => {
396407);
397408await noteStateIntegrity(cfg, { confirmRuntimeRepair, note: noteMock });
398409399-expect(confirmRuntimeRepair).toHaveBeenCalledWith(
400-expect.objectContaining({
401-initialValue: false,
402-requiresInteractiveConfirmation: true,
403-}),
410+const archivePrompt = repairPromptCalls(confirmRuntimeRepair).find(
411+(prompt) => prompt.requiresInteractiveConfirmation === true,
404412);
413+expect(archivePrompt?.initialValue).toBe(false);
405414const files = fs.readdirSync(sessionsDir);
406415expect(files).toContain("orphan-session.jsonl");
407416const archivedOrphanTranscripts = files.filter((name) =>
@@ -446,9 +455,7 @@ describe("doctor state integrity oauth dir checks", () => {
446455await noteStateIntegrity(cfg, { confirmRuntimeRepair, note: noteMock });
447456448457expect(fs.existsSync(transcriptPath)).toBe(true);
449-expect(fs.readdirSync(sessionsDir)).not.toEqual(
450-expect.arrayContaining([expect.stringContaining(".deleted.")]),
451-);
458+expect(fs.readdirSync(sessionsDir).some((name) => name.includes(".deleted."))).toBe(false);
452459expect(stateIntegrityText()).not.toContain("These .jsonl files are no longer referenced");
453460} finally {
454461fs.rmSync(symlinkHome, { force: true, recursive: true });
@@ -581,13 +588,9 @@ describe("doctor state integrity oauth dir checks", () => {
581588const storePath = resolveStorePath(cfg.session?.store, { agentId: "main" });
582589const store = JSON.parse(fs.readFileSync(storePath, "utf8")) as Record<string, SessionEntry>;
583590expect(store["agent:main:main"]?.sessionId).toBe("mixed-session");
584-expect(Object.keys(store)).not.toEqual(
585-expect.arrayContaining([expect.stringContaining("heartbeat-recovered")]),
586-);
587-expect(confirmRuntimeRepair).not.toHaveBeenCalledWith(
588-expect.objectContaining({
589-message: expect.stringContaining("Move heartbeat-owned main session"),
590-}),
591+expect(Object.keys(store).some((key) => key.includes("heartbeat-recovered"))).toBe(false);
592+expect(hasRepairPromptMessage(confirmRuntimeRepair, "Move heartbeat-owned main session")).toBe(
593+false,
591594);
592595});
593596@@ -607,9 +610,7 @@ describe("doctor state integrity oauth dir checks", () => {
607610updatedAt: 1,
608611heartbeatIsolatedBaseSessionKey: "agent:main:main",
609612};
610-expect(resolveHeartbeatMainSessionRepairCandidate({ entry })).toMatchObject({
611-reason: "metadata",
612-});
613+expect(resolveHeartbeatMainSessionRepairCandidate({ entry })?.reason).toBe("metadata");
613614});
614615615616it("does not move synthetic heartbeat-owned sessions after recorded human interaction", () => {
@@ -705,9 +706,9 @@ describe("doctor state integrity oauth dir checks", () => {
705706].join("\n"),
706707);
707708const entry: SessionEntry = { sessionId: "session", updatedAt: 1 };
708-expect(resolveHeartbeatMainSessionRepairCandidate({ entry, transcriptPath })).toMatchObject({
709-reason: "transcript",
710-});
709+expect(resolveHeartbeatMainSessionRepairCandidate({ entry, transcriptPath })?.reason).toBe(
710+"transcript",
711+);
711712entry.lastInteractionAt = 2;
712713expect(resolveHeartbeatMainSessionRepairCandidate({ entry, transcriptPath })).toBeNull();
713714} finally {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。