mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-28 04:28:58 +02:00
* fix(logging): redact gmail watcher startup args * fix(logging): normalize redaction formatting * fix(logging): harden gmail watcher log redaction * fix(logging): honor configured log tail redaction * fix(logging): skip redact pattern resolution when off * fix(logging): reuse compiled redact regexes * chore: untrack USER.md (covered by .gitignore) * chore: untrack USER.md (covered by .gitignore) * fix(logging): avoid double-resolution in log-tail redaction * fix(logging): redact across line boundaries for multiline patterns * fix(logging): guard redactSensitiveLines against empty input * chore(changelog): add Gmail watcher log redaction entry --------- Co-authored-by: Devin Robison <drobison@nvidia.com>
160 lines
4.6 KiB
TypeScript
160 lines
4.6 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { type OpenClawConfig, DEFAULT_GATEWAY_PORT } from "../config/config.js";
|
|
import {
|
|
buildDefaultHookUrl,
|
|
buildGogWatchServeLogArgs,
|
|
buildTopicPath,
|
|
parseTopicPath,
|
|
resolveGmailHookRuntimeConfig,
|
|
} from "./gmail.js";
|
|
|
|
const baseConfig = {
|
|
hooks: {
|
|
token: "hook-token",
|
|
gmail: {
|
|
account: "openclaw@gmail.com",
|
|
topic: "projects/demo/topics/gog-gmail-watch",
|
|
pushToken: "push-token",
|
|
},
|
|
},
|
|
} satisfies OpenClawConfig;
|
|
|
|
describe("gmail hook config", () => {
|
|
function resolveWithGmailOverrides(
|
|
overrides: Partial<NonNullable<OpenClawConfig["hooks"]>["gmail"]>,
|
|
) {
|
|
return resolveGmailHookRuntimeConfig(
|
|
{
|
|
hooks: {
|
|
token: "hook-token",
|
|
gmail: {
|
|
account: "openclaw@gmail.com",
|
|
topic: "projects/demo/topics/gog-gmail-watch",
|
|
pushToken: "push-token",
|
|
...overrides,
|
|
},
|
|
},
|
|
},
|
|
{},
|
|
);
|
|
}
|
|
|
|
function expectResolvedPaths(
|
|
result: ReturnType<typeof resolveGmailHookRuntimeConfig>,
|
|
expected: { servePath: string; publicPath: string; target?: string },
|
|
) {
|
|
expect(result.ok).toBe(true);
|
|
if (!result.ok) {
|
|
return;
|
|
}
|
|
expect(result.value.serve.path).toBe(expected.servePath);
|
|
expect(result.value.tailscale.path).toBe(expected.publicPath);
|
|
if (expected.target !== undefined) {
|
|
expect(result.value.tailscale.target).toBe(expected.target);
|
|
}
|
|
}
|
|
|
|
it("builds default hook url", () => {
|
|
expect(buildDefaultHookUrl("/hooks", DEFAULT_GATEWAY_PORT)).toBe(
|
|
`http://127.0.0.1:${DEFAULT_GATEWAY_PORT}/hooks/gmail`,
|
|
);
|
|
});
|
|
|
|
it("parses topic path", () => {
|
|
const topic = buildTopicPath("proj", "topic");
|
|
expect(parseTopicPath(topic)).toEqual({
|
|
projectId: "proj",
|
|
topicName: "topic",
|
|
});
|
|
});
|
|
|
|
it("resolves runtime config with defaults", () => {
|
|
const result = resolveGmailHookRuntimeConfig(baseConfig, {});
|
|
expect(result.ok).toBe(true);
|
|
if (result.ok) {
|
|
expect(result.value.account).toBe("openclaw@gmail.com");
|
|
expect(result.value.label).toBe("INBOX");
|
|
expect(result.value.includeBody).toBe(true);
|
|
expect(result.value.serve.port).toBe(8788);
|
|
expect(result.value.hookUrl).toBe(`http://127.0.0.1:${DEFAULT_GATEWAY_PORT}/hooks/gmail`);
|
|
}
|
|
});
|
|
|
|
it("builds watch serve log args without secrets", () => {
|
|
const result = resolveGmailHookRuntimeConfig(baseConfig, {});
|
|
expect(result.ok).toBe(true);
|
|
if (!result.ok) {
|
|
return;
|
|
}
|
|
|
|
const args = buildGogWatchServeLogArgs(result.value);
|
|
expect(args).not.toContain("push-token");
|
|
expect(args).not.toContain("hook-token");
|
|
expect(args).not.toContain("--token");
|
|
expect(args).not.toContain("--hook-token");
|
|
// --token, --hook-url, and --hook-token are stripped from the log args.
|
|
expect(args).toEqual([
|
|
"gmail",
|
|
"watch",
|
|
"serve",
|
|
"--account",
|
|
"openclaw@gmail.com",
|
|
"--bind",
|
|
"127.0.0.1",
|
|
"--port",
|
|
"8788",
|
|
"--path",
|
|
"/gmail-pubsub",
|
|
"--include-body",
|
|
"--max-bytes",
|
|
"20000",
|
|
]);
|
|
});
|
|
|
|
it("fails without hook token", () => {
|
|
const result = resolveGmailHookRuntimeConfig(
|
|
{
|
|
hooks: {
|
|
gmail: {
|
|
account: "openclaw@gmail.com",
|
|
topic: "projects/demo/topics/gog-gmail-watch",
|
|
pushToken: "push-token",
|
|
},
|
|
},
|
|
},
|
|
{},
|
|
);
|
|
expect(result.ok).toBe(false);
|
|
});
|
|
|
|
it("defaults serve path to / when tailscale is enabled", () => {
|
|
const result = resolveWithGmailOverrides({ tailscale: { mode: "funnel" } });
|
|
expectResolvedPaths(result, { servePath: "/", publicPath: "/gmail-pubsub" });
|
|
});
|
|
|
|
it("keeps the default public path when serve path is explicit", () => {
|
|
const result = resolveWithGmailOverrides({
|
|
serve: { path: "/gmail-pubsub" },
|
|
tailscale: { mode: "funnel" },
|
|
});
|
|
expectResolvedPaths(result, { servePath: "/", publicPath: "/gmail-pubsub" });
|
|
});
|
|
|
|
it("keeps custom public path when serve path is set", () => {
|
|
const result = resolveWithGmailOverrides({
|
|
serve: { path: "/custom" },
|
|
tailscale: { mode: "funnel" },
|
|
});
|
|
expectResolvedPaths(result, { servePath: "/", publicPath: "/custom" });
|
|
});
|
|
|
|
it("keeps serve path when tailscale target is set", () => {
|
|
const target = "http://127.0.0.1:8788/custom";
|
|
const result = resolveWithGmailOverrides({
|
|
serve: { path: "/custom" },
|
|
tailscale: { mode: "funnel", target },
|
|
});
|
|
expectResolvedPaths(result, { servePath: "/custom", publicPath: "/custom", target });
|
|
});
|
|
});
|