mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-13 11:00:50 +00:00
125 lines
3.6 KiB
TypeScript
125 lines
3.6 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";
|
|
import {
|
|
ensureTailscaleEndpoint,
|
|
resetGmailSetupUtilsCachesForTest,
|
|
resolvePythonExecutablePath,
|
|
} from "./gmail-setup-utils.js";
|
|
|
|
const itUnix = process.platform === "win32" ? it.skip : it;
|
|
const runCommandWithTimeoutMock = vi.fn();
|
|
|
|
vi.mock("../process/exec.js", () => ({
|
|
runCommandWithTimeout: (...args: unknown[]) => runCommandWithTimeoutMock(...args),
|
|
}));
|
|
|
|
beforeEach(() => {
|
|
runCommandWithTimeoutMock.mockReset();
|
|
resetGmailSetupUtilsCachesForTest();
|
|
});
|
|
|
|
describe("resolvePythonExecutablePath", () => {
|
|
itUnix(
|
|
"resolves a working python path and caches the result",
|
|
async () => {
|
|
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-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\nexit 0\n", "utf-8");
|
|
await fs.chmod(shim, 0o755);
|
|
|
|
process.env.PATH = `${shimDir}${path.delimiter}/usr/bin`;
|
|
|
|
runCommandWithTimeoutMock.mockResolvedValue({
|
|
stdout: `${realPython}\n`,
|
|
stderr: "",
|
|
code: 0,
|
|
signal: null,
|
|
killed: false,
|
|
});
|
|
|
|
const resolved = await resolvePythonExecutablePath();
|
|
expect(resolved).toBe(realPython);
|
|
|
|
process.env.PATH = "/bin";
|
|
const cached = await resolvePythonExecutablePath();
|
|
expect(cached).toBe(realPython);
|
|
expect(runCommandWithTimeoutMock).toHaveBeenCalledTimes(1);
|
|
} 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 () => {
|
|
runCommandWithTimeoutMock
|
|
.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 () => {
|
|
runCommandWithTimeoutMock.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");
|
|
});
|
|
});
|