Files
clawdbot/src/hooks/gmail-setup-utils.test.ts
Peter Steinberger c379191f80 chore: migrate to oxlint and oxfmt
Co-authored-by: Christoph Nakazawa <christoph.pojer@gmail.com>
2026-01-14 15:02:19 +00:00

128 lines
3.7 KiB
TypeScript

import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { beforeEach, describe, expect, it, vi } from "vitest";
const itUnix = process.platform === "win32" ? it.skip : it;
beforeEach(() => {
vi.resetModules();
});
describe("resolvePythonExecutablePath", () => {
itUnix(
"resolves a working python path and caches the result",
async () => {
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-python-"));
const originalPath = process.env.PATH;
try {
const realPython = path.join(tmp, "python-real");
await fs.writeFile(realPython, "#!/bin/sh\nexit 0\n", "utf-8");
await fs.chmod(realPython, 0o755);
const shimDir = path.join(tmp, "shims");
await fs.mkdir(shimDir, { recursive: true });
const shim = path.join(shimDir, "python3");
await fs.writeFile(
shim,
`#!/bin/sh\nif [ "$1" = "-c" ]; then\n echo "${realPython}"\n exit 0\nfi\nexit 1\n`,
"utf-8",
);
await fs.chmod(shim, 0o755);
process.env.PATH = `${shimDir}${path.delimiter}/usr/bin`;
const { resolvePythonExecutablePath } = await import("./gmail-setup-utils.js");
const resolved = await resolvePythonExecutablePath();
expect(resolved).toBe(realPython);
process.env.PATH = "/bin";
const cached = await resolvePythonExecutablePath();
expect(cached).toBe(realPython);
} finally {
process.env.PATH = originalPath;
await fs.rm(tmp, { recursive: true, force: true });
}
},
60_000,
);
});
describe("ensureTailscaleEndpoint", () => {
it("includes stdout and exit code when tailscale serve fails", async () => {
vi.doMock("../process/exec.js", () => ({
runCommandWithTimeout: vi.fn(),
}));
const { ensureTailscaleEndpoint } = await import("./gmail-setup-utils.js");
const { runCommandWithTimeout } = await import("../process/exec.js");
const runCommand = vi.mocked(runCommandWithTimeout);
runCommand
.mockResolvedValueOnce({
stdout: JSON.stringify({ Self: { DNSName: "host.tailnet.ts.net." } }),
stderr: "",
code: 0,
signal: null,
killed: false,
})
.mockResolvedValueOnce({
stdout: "tailscale output",
stderr: "Warning: client version mismatch",
code: 1,
signal: null,
killed: false,
});
let message = "";
try {
await ensureTailscaleEndpoint({
mode: "serve",
path: "/gmail-pubsub",
port: 8788,
});
} catch (err) {
message = err instanceof Error ? err.message : String(err);
}
expect(message).toContain("code=1");
expect(message).toContain("stderr: Warning: client version mismatch");
expect(message).toContain("stdout: tailscale output");
});
it("includes JSON parse failure details with stdout", async () => {
vi.doMock("../process/exec.js", () => ({
runCommandWithTimeout: vi.fn(),
}));
const { ensureTailscaleEndpoint } = await import("./gmail-setup-utils.js");
const { runCommandWithTimeout } = await import("../process/exec.js");
const runCommand = vi.mocked(runCommandWithTimeout);
runCommand.mockResolvedValueOnce({
stdout: "not-json",
stderr: "",
code: 0,
signal: null,
killed: false,
});
let message = "";
try {
await ensureTailscaleEndpoint({
mode: "funnel",
path: "/gmail-pubsub",
port: 8788,
});
} catch (err) {
message = err instanceof Error ? err.message : String(err);
}
expect(message).toContain("returned invalid JSON");
expect(message).toContain("stdout: not-json");
expect(message).toContain("code=0");
});
});