mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-28 05:16:23 -06:00
fix(feishu): honor abortSignal during app registration poll interval (#109909)
* fix(feishu): honor abortSignal during app registration poll interval * test(feishu): prove poll abort through a real loopback server
This commit is contained in:
committed by
GitHub
parent
a0f03b6f9f
commit
828845a08a
@@ -242,6 +242,41 @@ describe("Feishu app registration", () => {
|
||||
await expect(poll).resolves.toEqual({ status: "timeout" });
|
||||
});
|
||||
|
||||
it("stops polling promptly when abortSignal fires during the poll interval", async () => {
|
||||
let requestCount = 0;
|
||||
await withRegistrationServer(
|
||||
(_req, res) => {
|
||||
requestCount += 1;
|
||||
writeJson(res, { error: "authorization_pending" });
|
||||
},
|
||||
async ({ fetchImpl, lookupFn }) => {
|
||||
const controller = new AbortController();
|
||||
const started = Date.now();
|
||||
|
||||
const poll = pollAppRegistration({
|
||||
deviceCode: "device-code",
|
||||
interval: 30,
|
||||
expireIn: 600,
|
||||
abortSignal: controller.signal,
|
||||
fetchImpl,
|
||||
lookupFn,
|
||||
});
|
||||
// Let the first poll resolve and the loop enter its 30s interval sleep.
|
||||
await new Promise<void>((resolve) => {
|
||||
setTimeout(resolve, 200);
|
||||
});
|
||||
controller.abort();
|
||||
|
||||
await expect(poll).resolves.toEqual({ status: "timeout" });
|
||||
expect(Date.now() - started).toBeLessThan(10_000);
|
||||
expect(requestCount).toBe(1);
|
||||
console.log(
|
||||
`[feishu pollAppRegistration abort proof] interval=30s aborted_after=200ms elapsed=${Date.now() - started}ms requests=${requestCount} outcome=timeout`,
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("prints scan-to-create QR codes with compact terminal rendering", async () => {
|
||||
const writeSpy = vi.spyOn(process.stdout, "write").mockImplementation(() => true);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Feishu plugin module implements app registration behavior.
|
||||
import { finiteSecondsToTimerSafeMilliseconds } from "openclaw/plugin-sdk/number-runtime";
|
||||
import { sleep } from "openclaw/plugin-sdk/runtime-env";
|
||||
import { sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
|
||||
/**
|
||||
* Feishu app registration via OAuth device-code flow.
|
||||
*
|
||||
@@ -257,7 +257,7 @@ export async function pollAppRegistration(params: {
|
||||
);
|
||||
} catch {
|
||||
// Transient network error — keep polling.
|
||||
await sleepRegistrationPollInterval(currentInterval);
|
||||
await sleepRegistrationPollInterval(currentInterval, abortSignal);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -303,7 +303,7 @@ export async function pollAppRegistration(params: {
|
||||
}
|
||||
}
|
||||
|
||||
await sleepRegistrationPollInterval(currentInterval);
|
||||
await sleepRegistrationPollInterval(currentInterval, abortSignal);
|
||||
}
|
||||
|
||||
return { status: "timeout" };
|
||||
@@ -394,9 +394,14 @@ export async function getAppOwnerOpenId(params: {
|
||||
}
|
||||
}
|
||||
|
||||
function sleepRegistrationPollInterval(intervalSeconds: number): Promise<void> {
|
||||
async function sleepRegistrationPollInterval(
|
||||
intervalSeconds: number,
|
||||
abortSignal?: AbortSignal,
|
||||
): Promise<void> {
|
||||
const intervalMs =
|
||||
finiteSecondsToTimerSafeMilliseconds(intervalSeconds) ??
|
||||
DEFAULT_REGISTRATION_POLL_INTERVAL_SECONDS * 1_000;
|
||||
return sleep(intervalMs);
|
||||
// Swallow the abort rejection: the poll loop's `aborted` check owns the exit
|
||||
// path so PollOutcome stays "timeout" instead of an unhandled rejection.
|
||||
await sleepWithAbort(intervalMs, abortSignal).catch(() => {});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user