Handle thrown exceptions from device-flow complete() in /calendar_done

This commit is contained in:
EugeneTes
2026-09-23 10:28:11 +00:00
parent 279134a305
commit f3c4025b38
2 changed files with 21 additions and 2 deletions

View File

@@ -447,4 +447,17 @@ describe("registerBridge", () => {
await h.bot.handleUpdate(textUpdate("/calendar_done")); await h.bot.handleUpdate(textUpdate("/calendar_done"));
expect(h.texts()[2]).toMatch(/No pending calendar connection/); expect(h.texts()[2]).toMatch(/No pending calendar connection/);
}); });
it("/calendar_done reports a thrown complete() exception and keeps state", async () => {
const flow = new FakeDeviceFlow();
// FakeDeviceFlow throws when completeQueue is empty; use that to simulate a network error.
const h = harness({ deviceFlow: flow });
await h.bot.handleUpdate(textUpdate("/connect_calendar"));
await h.bot.handleUpdate(textUpdate("/calendar_done"));
expect(h.texts()[1]).toMatch(/Couldn't finish Google connection/);
// State should be kept so a retry works.
flow.completeQueue.push({ kind: "success", refreshToken: "rt-recovered" });
await h.bot.handleUpdate(textUpdate("/calendar_done"));
expect((h.tokenStore as FakeTokenStore).current).toBe("rt-recovered");
});
}); });

View File

@@ -9,7 +9,7 @@ import type { Transcriber } from "../stt/openai.js";
import type { GoogleTokenStore } from "../tools/google-token-store.js"; import type { GoogleTokenStore } from "../tools/google-token-store.js";
import { describeJob } from "../tools/schedule.js"; import { describeJob } from "../tools/schedule.js";
import { InputDebouncer } from "./debouncer.js"; import { InputDebouncer } from "./debouncer.js";
import type { DeviceCode, DeviceFlowClient } from "./google-device-flow.js"; import type { CompleteResult, DeviceCode, DeviceFlowClient } from "./google-device-flow.js";
import { normalizeMessage } from "./inbound.js"; import { normalizeMessage } from "./inbound.js";
import type { Outbound } from "./outbound.js"; import type { Outbound } from "./outbound.js";
@@ -154,7 +154,13 @@ export function registerBridge(deps: BridgeDeps): void {
return; return;
} }
const active = pendingDevice; const active = pendingDevice;
const result = await deps.deviceFlow.complete(active.deviceCode); let result: CompleteResult;
try {
result = await deps.deviceFlow.complete(active.deviceCode);
} catch (err) {
await ctx.reply(`Couldn't finish Google connection: ${errorText(err)}`);
return;
}
switch (result.kind) { switch (result.kind) {
case "success": { case "success": {
try { try {