Make GOOGLE_REFRESH_TOKEN optional in config

This commit is contained in:
EugeneTes
2026-09-23 10:03:09 +00:00
parent 1a4960e5bb
commit f765784acb
2 changed files with 49 additions and 5 deletions

View File

@@ -71,4 +71,47 @@ describe("loadConfig", () => {
extraArgs: ["--device", "iPhone 15", "--viewport-size", "1280,720"], extraArgs: ["--device", "iPhone 15", "--viewport-size", "1280,720"],
}); });
}); });
it("accepts Google client_id + client_secret without a refresh token", () => {
const cfg = loadConfig(
{ ...base, GOOGLE_CLIENT_ID: "cid", GOOGLE_CLIENT_SECRET: "csecret" },
"/p",
);
expect(cfg.google).toEqual({ clientId: "cid", clientSecret: "csecret", refreshToken: undefined });
});
it("accepts Google client_id + client_secret + refresh_token together", () => {
const cfg = loadConfig(
{
...base,
GOOGLE_CLIENT_ID: "cid",
GOOGLE_CLIENT_SECRET: "csecret",
GOOGLE_REFRESH_TOKEN: "rt",
},
"/p",
);
expect(cfg.google).toEqual({ clientId: "cid", clientSecret: "csecret", refreshToken: "rt" });
});
it("rejects Google config that has client_id without client_secret", () => {
expect(() =>
loadConfig({ ...base, GOOGLE_CLIENT_ID: "cid" }, "/p"),
).toThrow(/GOOGLE_CLIENT_ID.*GOOGLE_CLIENT_SECRET/);
});
it("rejects Google config that has client_secret without client_id", () => {
expect(() =>
loadConfig({ ...base, GOOGLE_CLIENT_SECRET: "csecret" }, "/p"),
).toThrow(/GOOGLE_CLIENT_ID.*GOOGLE_CLIENT_SECRET/);
});
it("rejects a lone GOOGLE_REFRESH_TOKEN without client_id/secret", () => {
expect(() =>
loadConfig({ ...base, GOOGLE_REFRESH_TOKEN: "rt" }, "/p"),
).toThrow(/GOOGLE_CLIENT_ID.*GOOGLE_CLIENT_SECRET/);
});
it("leaves google undefined when no GOOGLE_* vars are set", () => {
expect(loadConfig(base, "/p").google).toBeUndefined();
});
}); });

View File

@@ -18,7 +18,7 @@ export interface Config {
export interface GoogleConfig { export interface GoogleConfig {
clientId: string; clientId: string;
clientSecret: string; clientSecret: string;
refreshToken: string; refreshToken?: string;
} }
export interface PlaywrightConfig { export interface PlaywrightConfig {
@@ -55,14 +55,15 @@ export function loadConfig(env: NodeJS.ProcessEnv = process.env, cwd: string = p
const googleClientId = blankToUndefined(env.GOOGLE_CLIENT_ID); const googleClientId = blankToUndefined(env.GOOGLE_CLIENT_ID);
const googleClientSecret = blankToUndefined(env.GOOGLE_CLIENT_SECRET); const googleClientSecret = blankToUndefined(env.GOOGLE_CLIENT_SECRET);
const googleRefreshToken = blankToUndefined(env.GOOGLE_REFRESH_TOKEN); const googleRefreshToken = blankToUndefined(env.GOOGLE_REFRESH_TOKEN);
const googleParts = [googleClientId, googleClientSecret, googleRefreshToken]; const hasClientId = Boolean(googleClientId);
if (googleParts.some(Boolean) && !googleParts.every(Boolean)) { const hasClientSecret = Boolean(googleClientSecret);
if (hasClientId !== hasClientSecret || (!hasClientId && googleRefreshToken)) {
throw new ConfigError( throw new ConfigError(
"Google Calendar needs all three of GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GOOGLE_REFRESH_TOKEN, or none.", "Google Calendar needs both GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET (refresh token is optional; if set at startup it will be migrated into the workspace store).",
); );
} }
const google: GoogleConfig | undefined = const google: GoogleConfig | undefined =
googleClientId && googleClientSecret && googleRefreshToken googleClientId && googleClientSecret
? { clientId: googleClientId, clientSecret: googleClientSecret, refreshToken: googleRefreshToken } ? { clientId: googleClientId, clientSecret: googleClientSecret, refreshToken: googleRefreshToken }
: undefined; : undefined;