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 });
});
});