fix: accept empty rendered diff output

This commit is contained in:
Gustavo Madeira Santana 2026-03-30 15:58:04 -04:00
parent 46b5cbf9c7
commit 9972f3029f
No known key found for this signature in database
3 changed files with 101 additions and 1 deletions

View File

@ -25,6 +25,7 @@ Docs: https://docs.openclaw.ai
- Flows/tasks: add a minimal SQLite-backed flow registry plus task-to-flow linkage scaffolding, so orchestrated work can start gaining a first-class parent record without changing current task delivery behavior.
- Flows/tasks: route one-task ACP and subagent updates through a parent flow owner context, so detached work can emerge back through the intended parent thread/session instead of speaking only as a raw child task.
- Matrix/history: add optional room history context for Matrix group triggers via `channels.matrix.historyLimit`, with per-agent watermarks and retry-safe snapshots so failed trigger retries do not drift into newer room messages. (#57022) thanks @chain710.
- Diffs: skip unused viewer-versus-file SSR preload work so `diffs` view-only and file-only runs do less render work while keeping mode outputs aligned. (#57909) thanks @gumadeiras.
### Fixes

View File

@ -0,0 +1,99 @@
import fs from "node:fs/promises";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { createTestPluginApi } from "../../../test/helpers/plugins/plugin-api.js";
import type { OpenClawPluginApi } from "../api.js";
import type { DiffScreenshotter } from "./browser.js";
import { DEFAULT_DIFFS_TOOL_DEFAULTS } from "./config.js";
import { createDiffStoreHarness } from "./test-helpers.js";
const { renderDiffDocumentMock } = vi.hoisted(() => ({
renderDiffDocumentMock: vi.fn(),
}));
vi.mock("./render.js", () => ({
renderDiffDocument: renderDiffDocumentMock,
}));
describe("diffs tool rendered output guards", () => {
let cleanupRootDir: () => Promise<void>;
let store: Awaited<ReturnType<typeof createDiffStoreHarness>>["store"];
beforeEach(async () => {
vi.resetModules();
renderDiffDocumentMock.mockReset();
({ store, cleanup: cleanupRootDir } = await createDiffStoreHarness(
"openclaw-diffs-tool-render-output-",
));
});
afterEach(async () => {
await cleanupRootDir();
});
it("accepts empty string image html for file output", async () => {
renderDiffDocumentMock.mockResolvedValue({
title: "Text diff",
fileCount: 1,
inputKind: "before_after",
imageHtml: "",
});
const { createDiffsTool } = await import("./tool.js");
const screenshotter = createPngScreenshotter({
assertHtml: (html) => {
expect(html).toBe("");
},
});
const tool = createDiffsTool({
api: createApi(),
store,
defaults: DEFAULT_DIFFS_TOOL_DEFAULTS,
screenshotter,
});
const result = await tool.execute?.("tool-empty-image-html", {
before: "one\n",
after: "two\n",
mode: "file",
});
expect(screenshotter.screenshotHtml).toHaveBeenCalledTimes(1);
expect((result?.details as Record<string, unknown>).filePath).toEqual(expect.any(String));
});
});
function createApi(): OpenClawPluginApi {
return createTestPluginApi({
id: "diffs",
name: "Diffs",
description: "Diffs",
source: "test",
config: {
gateway: {
port: 18789,
bind: "loopback",
},
},
runtime: {} as OpenClawPluginApi["runtime"],
}) as OpenClawPluginApi;
}
function createPngScreenshotter(
params: {
assertHtml?: (html: string) => void;
} = {},
): DiffScreenshotter {
const screenshotHtml: DiffScreenshotter["screenshotHtml"] = vi.fn(
async ({ html, outputPath }: { html: string; outputPath: string }) => {
params.assertHtml?.(html);
await fs.mkdir(path.dirname(outputPath), { recursive: true });
await fs.writeFile(outputPath, Buffer.from("png"));
return outputPath;
},
);
return {
screenshotHtml,
};
}

View File

@ -341,7 +341,7 @@ function resolveRenderTarget(mode: DiffMode): DiffRenderTarget {
}
function requireRenderedHtml(html: string | undefined, target: DiffRenderTarget): string {
if (html) {
if (html !== undefined) {
return html;
}
throw new Error(`Missing ${target} render output.`);