real-debrid-downloader/tests/dead-link.test.ts
Sucukdeluxe 938b84392d Fix: Tote Mega-Debrid-Links (frz. "Fichier supprimé") vergiften nicht mehr die ganze Queue
Mega-Debrid liefert bei geloeschten Hoster-Dateien franzoesische Fehlertexte
("Fichier supprime chez l'hebergeur"). Diese wurden weder in classifyAccountFailure
(debrid.ts) noch in isPermanentLinkError (download-manager.ts) erkannt und landeten
im generischen "temporaer"-Zweig: 30s Account-Cooldown + (bei retryLimit 0) endlose
Wiederholung. Mit nur einem Account (als API UND Web) blockierte ein einziger toter
Link ueber den Cooldown auch alle gesunden Links (SKIP_COOLDOWN) -- die Queue stand.
Beleg aus dem Support-Bundle: 479 von 983 Zeilen im Rotations-Log waren dieser Fehler.

- shared/dead-link.ts: zentrale Tot-Link-Erkennung (frz./dt./engl.), eine Quelle fuer
  beide Schichten, damit die Muster nicht auseinanderdriften.
- classifyAccountFailure: toter Link -> fatal/skip, KEIN Account-Cooldown (Account ist
  gesund, nur die Datei ist weg).
- isPermanentLinkError: toter Link -> Item sofort als gescheitert markiert, kein
  Endlos-Retry. Greift auch im aggregierten Provider-Ketten-Fehlerstring.
- Provider-Kette + interner Web-Fallback: toter Link -> kein Fallback auf weitere
  Provider/Modi (spart den 60s-Web-Timeout; saubere Meldung erreicht isPermanentLinkError
  unveraendert).
- Anzeige: deutsche Klartext-Meldung "Link tot - Datei beim Hoster geloescht" statt frz.
- Account-Liste: Hinweis, dass Mega-Debrid API + Web derselbe Account in zwei Modi sind.

Tests: tests/dead-link.test.ts (inkl. aggregierter Provider-Ketten-String + akzentfreie
Variante). Volle Suite 823/823 gruen.
2026-06-17 00:23:50 +02:00

53 lines
2.4 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { isDeadLinkErrorText, germanDeadLinkReason } from "../src/shared/dead-link";
describe("isDeadLinkErrorText", () => {
it("matches the real Mega-Debrid French dead-link phrase", () => {
expect(isDeadLinkErrorText("Mega-Debrid API: Fichier supprimé chez l'hébergeur")).toBe(true);
});
it("matches inside the aggregated provider-chain error (api dead | web timeout)", () => {
const aggregated = "Unrestrict fehlgeschlagen: Mega-Debrid API: Mega-Debrid (API): Fichier supprimé chez l'hébergeur | Mega-Debrid Web: Mega-Debrid (Web): Abbruch/Timeout nach 60s";
expect(isDeadLinkErrorText(aggregated)).toBe(true);
});
it("matches the de-accented variant (encoding may strip accents)", () => {
expect(isDeadLinkErrorText("Fichier supprime chez l'hebergeur")).toBe(true);
});
it("matches other clearly-dead French phrases", () => {
expect(isDeadLinkErrorText("Fichier introuvable")).toBe(true);
expect(isDeadLinkErrorText("Le fichier n'existe plus")).toBe(true);
});
it("still matches the existing English dead-link phrases", () => {
expect(isDeadLinkErrorText("file not found")).toBe(true);
expect(isDeadLinkErrorText("File has been deleted")).toBe(true);
expect(isDeadLinkErrorText("file is no longer available")).toBe(true);
expect(isDeadLinkErrorText("link is dead")).toBe(true);
});
it("does NOT match temporary/transient failures", () => {
expect(isDeadLinkErrorText("Abbruch/Timeout nach 60s")).toBe(false);
expect(isDeadLinkErrorText("Quota/Limit erreicht")).toBe(false);
expect(isDeadLinkErrorText("Rate-Limit (429)")).toBe(false);
expect(isDeadLinkErrorText("Kein Server fuer diesen Hoster")).toBe(false);
expect(isDeadLinkErrorText("queue-timeout")).toBe(false);
expect(isDeadLinkErrorText("hosternotavailable")).toBe(false);
});
});
describe("germanDeadLinkReason", () => {
it("renders the deleted-at-host phrase in German", () => {
expect(germanDeadLinkReason("Mega-Debrid API: Fichier supprimé chez l'hébergeur")).toBe("Datei beim Hoster gelöscht");
});
it("renders a plain deletion in German", () => {
expect(germanDeadLinkReason("fichier supprimé")).toBe("Datei gelöscht");
});
it("renders not-found in German", () => {
expect(germanDeadLinkReason("Fichier introuvable")).toBe("Datei beim Hoster nicht gefunden");
});
});