






















@@ -124,14 +124,6 @@ async function waitForMicrotaskCondition(
124124throw new Error(message);
125125}
126126127-function firstSleepWithAbortCall(): [number, AbortSignal | undefined] {
128-const call = hoisted.sleepWithAbort.mock.calls[0];
129-if (!call) {
130-throw new Error("expected sleepWithAbort call");
131-}
132-return call as [number, AbortSignal | undefined];
133-}
134-135127function firstStartAccountContext(
136128startAccount: ReturnType<typeof vi.fn>,
137129): ChannelGatewayContext<TestAccount> {
@@ -378,14 +370,56 @@ describe("server-channels auto restart", () => {
378370expect(manager.isManuallyStopped("discord", DEFAULT_ACCOUNT_ID)).toBe(false);
379371380372releaseFirstTask.resolve();
381-await flushMicrotasks();
382-await vi.advanceTimersByTimeAsync(10);
383-await flushMicrotasks();
373+await waitForMicrotaskCondition(
374+() => startAccount.mock.calls.length === 2,
375+"expected timed-out recovery stop to restart without backoff",
376+);
377+378+expect(startAccount).toHaveBeenCalledTimes(2);
379+expect(hoisted.sleepWithAbort).not.toHaveBeenCalled();
380+});
381+382+it("restarts immediately when recovery stop timeout settles with an error", async () => {
383+const rejectFirstTask = createDeferred();
384+let startCount = 0;
385+const startAccount = vi.fn(async ({ abortSignal }: { abortSignal: AbortSignal }) => {
386+startCount += 1;
387+abortSignal.addEventListener("abort", () => {}, { once: true });
388+if (startCount === 1) {
389+await rejectFirstTask.promise;
390+throw new Error("late worker exit");
391+}
392+await new Promise<void>(() => {});
393+});
394+installTestRegistry(
395+createTestPlugin({
396+ startAccount,
397+}),
398+);
399+const manager = createManager();
384400401+await manager.startChannels();
402+const recoveryStopTask = manager.stopChannel("discord", DEFAULT_ACCOUNT_ID, {
403+manual: false,
404+});
405+await vi.advanceTimersByTimeAsync(5_000);
406+await recoveryStopTask;
407+408+rejectFirstTask.resolve();
409+await waitForMicrotaskCondition(
410+() => startAccount.mock.calls.length === 2,
411+"expected rejected timed-out recovery stop to restart without backoff",
412+);
413+414+const account = manager.getRuntimeSnapshot().channelAccounts.discord?.[DEFAULT_ACCOUNT_ID];
385415expect(startAccount).toHaveBeenCalledTimes(2);
416+expect(account?.running).toBe(true);
417+expect(account?.restartPending).toBe(false);
418+expect(account?.lastError).toBeNull();
419+expect(hoisted.sleepWithAbort).not.toHaveBeenCalled();
386420});
387421388-it("lets manual stops cancel recovery backoff after recovery stop times out", async () => {
422+it("lets manual stops cancel recovery restart after recovery stop times out", async () => {
389423const releaseFirstTask = createDeferred();
390424const startAccount = vi.fn(
391425async ({ abortSignal }: { abortSignal: AbortSignal }) =>
@@ -408,16 +442,10 @@ describe("server-channels auto restart", () => {
408442await vi.advanceTimersByTimeAsync(5_000);
409443await recoveryStopTask;
410444445+const manualStopTask = manager.stopChannel("discord", DEFAULT_ACCOUNT_ID);
446+await vi.advanceTimersByTimeAsync(5_000);
447+await manualStopTask;
411448releaseFirstTask.resolve();
412-await waitForMicrotaskCondition(
413-() => hoisted.sleepWithAbort.mock.calls.length > 0,
414-"expected recovery restart backoff to be scheduled",
415-);
416-const sleepCall = firstSleepWithAbortCall();
417-expect(sleepCall[0]).toBe(10);
418-expect(sleepCall[1]).toBeInstanceOf(AbortSignal);
419-420-await manager.stopChannel("discord", DEFAULT_ACCOUNT_ID);
421449await vi.advanceTimersByTimeAsync(10);
422450await flushMicrotasks();
423451@@ -426,6 +454,7 @@ describe("server-channels auto restart", () => {
426454expect(account?.running).toBe(false);
427455expect(account?.restartPending).toBe(false);
428456expect(manager.isManuallyStopped("discord", DEFAULT_ACCOUNT_ID)).toBe(true);
457+expect(hoisted.sleepWithAbort).not.toHaveBeenCalled();
429458});
430459431460it("marks enabled/configured when account descriptors omit them", () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。