Files
openclaw/src/gateway/control-ui-routing.test.ts
ademczuk 3e9c8721fb fix(gateway): let non-GET requests fall through controlUi routing when basePath is set
When controlUiBasePath is set, classifyControlUiRequest returned
method-not-allowed (405) for all non-GET/HEAD requests under basePath,
blocking plugin webhook handlers (BlueBubbles, Mattermost, etc.) from
receiving POST requests. This is a 2026.3.1 regression.

Return not-control-ui instead, matching the empty-basePath behavior, so
requests fall through to plugin HTTP handlers. Remove the now-dead
method-not-allowed type variant, handler branch, and utility function.

Closes #31983
Closes #32275

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:11:13 +00:00

67 lines
2.0 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { classifyControlUiRequest } from "./control-ui-routing.js";
describe("classifyControlUiRequest", () => {
it("falls through non-read root requests for plugin webhooks", () => {
const classified = classifyControlUiRequest({
basePath: "",
pathname: "/bluebubbles-webhook",
search: "",
method: "POST",
});
expect(classified).toEqual({ kind: "not-control-ui" });
});
it("returns not-found for legacy /ui routes when root-mounted", () => {
const classified = classifyControlUiRequest({
basePath: "",
pathname: "/ui/settings",
search: "",
method: "GET",
});
expect(classified).toEqual({ kind: "not-found" });
});
it("falls through basePath non-read methods for plugin webhooks", () => {
const classified = classifyControlUiRequest({
basePath: "/openclaw",
pathname: "/openclaw",
search: "",
method: "POST",
});
expect(classified).toEqual({ kind: "not-control-ui" });
});
it("falls through PUT/DELETE/PATCH/OPTIONS under basePath for plugin handlers", () => {
for (const method of ["PUT", "DELETE", "PATCH", "OPTIONS"]) {
const classified = classifyControlUiRequest({
basePath: "/openclaw",
pathname: "/openclaw/webhook",
search: "",
method,
});
expect(classified, `${method} should fall through`).toEqual({ kind: "not-control-ui" });
}
});
it("returns redirect for basePath entrypoint GET", () => {
const classified = classifyControlUiRequest({
basePath: "/openclaw",
pathname: "/openclaw",
search: "?foo=1",
method: "GET",
});
expect(classified).toEqual({ kind: "redirect", location: "/openclaw/?foo=1" });
});
it("classifies basePath subroutes as control ui", () => {
const classified = classifyControlUiRequest({
basePath: "/openclaw",
pathname: "/openclaw/chat",
search: "",
method: "HEAD",
});
expect(classified).toEqual({ kind: "serve" });
});
});