From 3e8d9bc6ea760d565c8798bc8a294e12f32b019a Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Fri, 13 Mar 2026 18:19:27 +0000 Subject: [PATCH] test: refine telegram token coverage --- src/telegram/token.test.ts | 154 ++++++++++++++++++++++++------------- 1 file changed, 100 insertions(+), 54 deletions(-) diff --git a/src/telegram/token.test.ts b/src/telegram/token.test.ts index f888ddbfc36..17e412cf584 100644 --- a/src/telegram/token.test.ts +++ b/src/telegram/token.test.ts @@ -7,50 +7,75 @@ import { withStateDirEnv } from "../test-helpers/state-dir-env.js"; import { resolveTelegramToken } from "./token.js"; import { readTelegramUpdateOffset, writeTelegramUpdateOffset } from "./update-offset-store.js"; -function withTempDir(): string { - return fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-telegram-token-")); -} - describe("resolveTelegramToken", () => { + const tempDirs: string[] = []; + + function createTempDir(): string { + const dir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-telegram-token-")); + tempDirs.push(dir); + return dir; + } + + function createTokenFile(fileName: string, contents = "file-token\n"): string { + const dir = createTempDir(); + const tokenFile = path.join(dir, fileName); + fs.writeFileSync(tokenFile, contents, "utf-8"); + return tokenFile; + } + afterEach(() => { vi.unstubAllEnvs(); + for (const dir of tempDirs.splice(0)) { + fs.rmSync(dir, { recursive: true, force: true }); + } }); - it("prefers config token over env", () => { - vi.stubEnv("TELEGRAM_BOT_TOKEN", "env-token"); - const cfg = { - channels: { telegram: { botToken: "cfg-token" } }, - } as OpenClawConfig; - const res = resolveTelegramToken(cfg); - expect(res.token).toBe("cfg-token"); - expect(res.source).toBe("config"); - }); - - it("uses env token when config is missing", () => { - vi.stubEnv("TELEGRAM_BOT_TOKEN", "env-token"); - const cfg = { - channels: { telegram: {} }, - } as OpenClawConfig; - const res = resolveTelegramToken(cfg); - expect(res.token).toBe("env-token"); - expect(res.source).toBe("env"); - }); - - it("uses tokenFile when configured", () => { - vi.stubEnv("TELEGRAM_BOT_TOKEN", ""); - const dir = withTempDir(); - const tokenFile = path.join(dir, "token.txt"); - fs.writeFileSync(tokenFile, "file-token\n", "utf-8"); - const cfg = { channels: { telegram: { tokenFile } } } as OpenClawConfig; - const res = resolveTelegramToken(cfg); - expect(res.token).toBe("file-token"); - expect(res.source).toBe("tokenFile"); - fs.rmSync(dir, { recursive: true, force: true }); + it.each([ + { + name: "prefers config token over env", + envToken: "env-token", + cfg: { + channels: { telegram: { botToken: "cfg-token" } }, + } as OpenClawConfig, + expected: { token: "cfg-token", source: "config" }, + }, + { + name: "uses env token when config is missing", + envToken: "env-token", + cfg: { + channels: { telegram: {} }, + } as OpenClawConfig, + expected: { token: "env-token", source: "env" }, + }, + { + name: "uses tokenFile when configured", + envToken: "", + cfg: { + channels: { telegram: { tokenFile: "" } }, + } as OpenClawConfig, + resolveCfg: () => + ({ + channels: { telegram: { tokenFile: createTokenFile("token.txt") } }, + }) as OpenClawConfig, + expected: { token: "file-token", source: "tokenFile" }, + }, + { + name: "falls back to config token when no env or tokenFile", + envToken: "", + cfg: { + channels: { telegram: { botToken: "cfg-token" } }, + } as OpenClawConfig, + expected: { token: "cfg-token", source: "config" }, + }, + ])("$name", ({ envToken, cfg, resolveCfg, expected }) => { + vi.stubEnv("TELEGRAM_BOT_TOKEN", envToken); + const res = resolveTelegramToken(resolveCfg ? resolveCfg() : cfg); + expect(res).toEqual(expected); }); it.runIf(process.platform !== "win32")("rejects symlinked tokenFile paths", () => { vi.stubEnv("TELEGRAM_BOT_TOKEN", ""); - const dir = withTempDir(); + const dir = createTempDir(); const tokenFile = path.join(dir, "token.txt"); const tokenLink = path.join(dir, "token-link.txt"); fs.writeFileSync(tokenFile, "file-token\n", "utf-8"); @@ -60,22 +85,11 @@ describe("resolveTelegramToken", () => { const res = resolveTelegramToken(cfg); expect(res.token).toBe(""); expect(res.source).toBe("none"); - fs.rmSync(dir, { recursive: true, force: true }); - }); - - it("falls back to config token when no env or tokenFile", () => { - vi.stubEnv("TELEGRAM_BOT_TOKEN", ""); - const cfg = { - channels: { telegram: { botToken: "cfg-token" } }, - } as OpenClawConfig; - const res = resolveTelegramToken(cfg); - expect(res.token).toBe("cfg-token"); - expect(res.source).toBe("config"); }); it("does not fall back to config when tokenFile is missing", () => { vi.stubEnv("TELEGRAM_BOT_TOKEN", ""); - const dir = withTempDir(); + const dir = createTempDir(); const tokenFile = path.join(dir, "missing-token.txt"); const cfg = { channels: { telegram: { tokenFile, botToken: "cfg-token" } }, @@ -83,7 +97,6 @@ describe("resolveTelegramToken", () => { const res = resolveTelegramToken(cfg); expect(res.token).toBe(""); expect(res.source).toBe("none"); - fs.rmSync(dir, { recursive: true, force: true }); }); it("resolves per-account tokens when the config account key casing doesn't match routing normalization", () => { @@ -121,14 +134,31 @@ describe("resolveTelegramToken", () => { expect(res.source).toBe("config"); }); - it("falls back to top-level tokenFile for non-default accounts", () => { - const dir = withTempDir(); - const tokenFile = path.join(dir, "token.txt"); - fs.writeFileSync(tokenFile, "file-token\n", "utf-8"); + it("uses account-level tokenFile before top-level fallbacks", () => { const cfg = { channels: { telegram: { - tokenFile, + botToken: "top-level-token", + tokenFile: createTokenFile("top-level-token.txt", "top-level-file-token\n"), + accounts: { + work: { + tokenFile: createTokenFile("account-token.txt", "account-file-token\n"), + }, + }, + }, + }, + } as OpenClawConfig; + + const res = resolveTelegramToken(cfg, { accountId: "work" }); + expect(res.token).toBe("account-file-token"); + expect(res.source).toBe("tokenFile"); + }); + + it("falls back to top-level tokenFile for non-default accounts", () => { + const cfg = { + channels: { + telegram: { + tokenFile: createTokenFile("token.txt"), accounts: { work: {}, }, @@ -139,7 +169,23 @@ describe("resolveTelegramToken", () => { const res = resolveTelegramToken(cfg, { accountId: "work" }); expect(res.token).toBe("file-token"); expect(res.source).toBe("tokenFile"); - fs.rmSync(dir, { recursive: true, force: true }); + }); + + it("does not use env token for non-default accounts", () => { + vi.stubEnv("TELEGRAM_BOT_TOKEN", "env-token"); + const cfg = { + channels: { + telegram: { + accounts: { + work: {}, + }, + }, + }, + } as OpenClawConfig; + + const res = resolveTelegramToken(cfg, { accountId: "work" }); + expect(res.token).toBe(""); + expect(res.source).toBe("none"); }); it("throws when botToken is an unresolved SecretRef object", () => {