From 03c908bd3018fffcf4e4dcb7028366bc94597eab Mon Sep 17 00:00:00 2001 From: Sucukdeluxe Date: Wed, 17 Jun 2026 06:31:37 +0200 Subject: [PATCH] Test: No-Stranding-Gate fuer Provider-Fallback-Routing + Doku-Praezisierung MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit findFallbackProviderNotInCooldown-Charakterisierungstest sichert den Invariant ab, auf dem der Routing-Fix (986fbab) seine Sicherheit aufbaut: bei kein Cooldown den ersten Provider, bei einem Cooldown den naechsten gesunden, und NULL erst wenn ALLE Provider im Cooldown sind. Dieses null ist der Punkt, an dem der Aufrufer queueRetry statt Reorder macht — wuerde der Helper hier je einen abgekuehlten Provider als letzten Notnagel zurueckgeben, liefe der Reorder am Sicherheitsnetz vorbei. Der Test friert das ein. Zusaetzlich: entscheidungen-offen.md praezisiert, dass Option A des Failover- Timeouts ein Drehregler ueber den Pro-Provider-Wert ist (30s/90s, 60s/180s), kein fixer Wert. --- tasks/entscheidungen-offen.md | 8 +++++--- tests/download-manager.test.ts | 28 ++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/tasks/entscheidungen-offen.md b/tasks/entscheidungen-offen.md index 7a72ceb..3982693 100644 --- a/tasks/entscheidungen-offen.md +++ b/tasks/entscheidungen-offen.md @@ -26,10 +26,12 @@ trifft in der Produktion derzeit selten. Deshalb dokumentiert statt dringend gef **Deine Entscheidung — zwei Richtungen (gleiche Spannung, andere Seite):** -- **A) Pro-Provider-Timeout:** Jeder Provider bekommt sein eigenes frisches 60s-Budget. +- **A) Pro-Provider-Timeout:** Jeder Provider bekommt sein eigenes frisches Budget. Failover bekommt IMMER einen echten Versuch. - *Kosten:* Worst-Case-Wartezeit pro Item steigt (3 Provider × 60s = bis zu 180s, bevor ein - Item aufgibt). Du akzeptierst langsameres Worst-Case-pro-Item für vollständigeres Failover. + *Kosten:* Worst-Case-Wartezeit pro Item steigt. Das Budget ist dabei ein DREHregler, kein + fixer Wert: 60s/Provider = bis 180s Worst-Case (3 Provider), 30s/Provider = bis 90s usw. + Du akzeptierst langsameres Worst-Case-pro-Item für vollständigeres Failover — und stellst + über den Pro-Provider-Wert ein, wie viel langsamer. - **B) Globales Budget behalten, aber Slice für Failover reservieren:** Provider 1 wird auf z.B. 35s gedeckelt, damit garantiert Zeit für Provider 2 bleibt. diff --git a/tests/download-manager.test.ts b/tests/download-manager.test.ts index 4caa7fb..a8fbb8f 100644 --- a/tests/download-manager.test.ts +++ b/tests/download-manager.test.ts @@ -229,6 +229,34 @@ describe("download manager", () => { expect(historyEntries[0]?.downloadedBytes).toBe(90 * 1024 * 1024); }); + it("findFallbackProviderNotInCooldown routes around cooled providers and returns null only when all are cooled", () => { + const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-fallback-route-")); + tempDirs.push(root); + const manager = new DownloadManager( + { + ...defaultSettings(), + token: "rd-token", + debridLinkApiKeys: "dl-token", + providerOrder: ["realdebrid", "debridlink"], + providerPrimary: "realdebrid", + providerSecondary: "debridlink", + providerTertiary: "none", + autoProviderFallback: true + }, + emptySession(), + createStoragePaths(path.join(root, "state")) + ); + const item = { id: "fb-item", url: "https://hoster.example/file.bin", provider: "realdebrid" } as any; + + expect((manager as any).findFallbackProviderNotInCooldown(item)).toBe("realdebrid"); + + (manager as any).applyProviderBusyBackoff("realdebrid", 60000); + expect((manager as any).findFallbackProviderNotInCooldown(item)).toBe("debridlink"); + + (manager as any).applyProviderBusyBackoff("debridlink", 60000); + expect((manager as any).findFallbackProviderNotInCooldown(item)).toBeNull(); + }); + it("keeps the quick post-process requeue once the final package items are finished", () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), "rd-postprocess-final-")); tempDirs.push(root);