From 664e34fc53ac037018724650c188e4e60b07649d Mon Sep 17 00:00:00 2001 From: Sucukdeluxe Date: Sat, 30 May 2026 23:44:19 +0200 Subject: [PATCH] Test: dedizierter Per-Account-Timeout-Rotationstest (acc1 haengt -> acc2 versucht) Schliesst die Coverage-Luecke zum v1.7.168-Fix (c4a49d9): der korrigierte Abort-Test prueft nur Signal-Propagation, nicht die Kernlogik des Fix. Dieser Test (Fake-Timer) faehrt den echten Rotationspfad: acc1 haengt bis sein Per-Account-Timeout feuert -> 30s-Cooldown -> Loop probiert acc2 -> Erfolg. Ohne den Fix (keine Abort-Quelle bei fehlendem globalem Signal) wuerde acc1 ewig haengen -> Test-Timeout. 642 Tests gruen. Co-Authored-By: Claude Opus 4.8 (1M context) --- tests/debrid.test.ts | 56 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/tests/debrid.test.ts b/tests/debrid.test.ts index 233d83b..c21bce0 100644 --- a/tests/debrid.test.ts +++ b/tests/debrid.test.ts @@ -1400,6 +1400,62 @@ describe("debrid service", () => { } }); + it("rotation per-account timeout: a hanging account is skipped so the next account is tried", async () => { + // Kern des v1.7.168-Fix: haengt Account 1, darf das NICHT die ganze Rotation + // fressen — der Per-Account-Timeout (PER_ACCOUNT_ATTEMPT_TIMEOUT_MS, default 25s) + // bricht NUR acc1 ab (kurzer Cooldown), der Loop probiert acc2. Ohne globales + // Signal (kein download-manager-Wrap) testet das den Per-Account-Timeout isoliert. + const settings = { + ...defaultSettings(), + token: "", + bestToken: "", + allDebridToken: "", + megaLogin: "user1", + megaPassword: "pass1", + megaCredentials: "user1:pass1\nuser2:pass2", + providerOrder: [] as const, + providerPrimary: "megadebrid" as const, + providerSecondary: "none" as const, + providerTertiary: "none" as const, + autoProviderFallback: false + }; + + // API connect schlaegt pro Account schnell fehl (500) -> Web-Fallback (megaWeb). + globalThis.fetch = (async () => new Response("error", { status: 500 })) as typeof fetch; + + let call = 0; + const megaWeb = vi.fn((_link: string, signal?: AbortSignal) => { + call += 1; + if (call === 1) { + // Account 1 haengt, bis sein eigener Per-Account-Timeout das Signal abortet. + return new Promise((_, reject) => { + if (signal?.aborted) { reject(new Error("aborted:acc1-hang")); return; } + signal?.addEventListener("abort", () => reject(new Error("aborted:acc1-hang")), { once: true }); + }); + } + // Account 2 liefert sofort. + return Promise.resolve({ + fileName: "acc2.rar", + directUrl: "https://mega-web.example/acc2.rar", + fileSize: null, + retriesUsed: 0 + }); + }); + + const service = new DebridService(settings, { megaWebUnrestrict: megaWeb }); + vi.useFakeTimers(); + try { + const pending = service.unrestrictLink("https://rapidgator.net/file/rotate-acc"); + // Per-Account-Timeout von acc1 (25s) feuern lassen -> acc1 faellt -> Loop -> acc2. + await vi.advanceTimersByTimeAsync(30000); + const result = await pending; + expect(result.directUrl).toBe("https://mega-web.example/acc2.rar"); + expect(megaWeb).toHaveBeenCalledTimes(2); + } finally { + vi.useRealTimers(); + } + }); + it("respects provider selection and does not append hidden providers", async () => { const settings = { ...defaultSettings(),