Compare commits
No commits in common. "bc50e4285dd069017246602d75419361d546d16b" and "68f50eaa5ee41b2d27b735ad615586cfdf4e5ee3" have entirely different histories.
bc50e4285d
...
68f50eaa5e
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "real-debrid-downloader",
|
||||
"version": "1.7.217",
|
||||
"version": "1.7.216",
|
||||
"description": "Desktop downloader",
|
||||
"main": "build/main/main/main.js",
|
||||
"author": "Sucukdeluxe",
|
||||
|
||||
@ -38,7 +38,6 @@ import { initSessionLog, getSessionLogPath, shutdownSessionLog } from "./session
|
||||
import { MegaWebFallback } from "./mega-web-fallback";
|
||||
import { addHistoryEntry, addHistoryEntryForRetention, cancelPendingAsyncSaves, clearHistory, createStoragePaths, loadHistory, loadHistoryForRetention, loadSession, loadSettings, normalizeHistoryEntry, normalizeLoadedSession, normalizeLoadedSessionTransientFields, normalizeSettings, removeHistoryEntry, resetHistoryForRetention, saveHistory, saveSession, saveSettings } from "./storage";
|
||||
import { abortActiveUpdateDownload, checkGitHubUpdate, installLatestUpdate } from "./update";
|
||||
import { runInstallWithResume } from "./update-install-flow";
|
||||
import { rotateDebugToken, startDebugServer, stopDebugServer } from "./debug-server";
|
||||
import { encryptBackup, decryptBackup } from "./backup-crypto";
|
||||
import { buildBackupPayload, planBackupImport } from "./backup-payload";
|
||||
@ -460,14 +459,16 @@ public async checkDebridAccounts(): Promise<DebridAccountStatus[]> {
|
||||
}
|
||||
|
||||
public async installUpdate(onProgress?: (progress: UpdateInstallProgress) => void): Promise<UpdateInstallResult> {
|
||||
if (this.manager.isSessionRunning()) {
|
||||
this.manager.stop({ parkForRestart: true });
|
||||
}
|
||||
this.manager.persistNowSync();
|
||||
|
||||
const cacheAgeMs = Date.now() - this.lastUpdateCheckAt;
|
||||
const cached = this.lastUpdateCheck && !this.lastUpdateCheck.error && cacheAgeMs <= 10 * 60 * 1000
|
||||
? this.lastUpdateCheck
|
||||
: undefined;
|
||||
const result = await runInstallWithResume(
|
||||
this.manager,
|
||||
() => installLatestUpdate(this.settings.updateRepo, cached, onProgress)
|
||||
);
|
||||
const result = await installLatestUpdate(this.settings.updateRepo, cached, onProgress);
|
||||
if (result.started) {
|
||||
this.lastUpdateCheck = null;
|
||||
this.lastUpdateCheckAt = 0;
|
||||
|
||||
@ -349,26 +349,14 @@ export function primeMegaDebridRuntimeCooldownForTests(accountId: string, cooldo
|
||||
setMegaDebridAccountCooldownState(accountId, cooldownMs, message, "temporary");
|
||||
}
|
||||
|
||||
export function primeMegaDebridUntilRestartForTests(accountId: string, message = "Tageslimit (Test) — bis zum Tagesreset gesperrt"): void {
|
||||
export function primeMegaDebridUntilRestartForTests(accountId: string, message = "Tageslimit (Test) — bis Neustart gesperrt"): void {
|
||||
setMegaDebridAccountCooldownState(accountId, 0, message, "quota", true);
|
||||
}
|
||||
|
||||
export function classifyMegaDebridAccountFailureForTests(
|
||||
error: unknown
|
||||
): { fatal: boolean; cooldownMs: number; message: string; category: MegaDebridCooldownCategory; limitSignal?: boolean } {
|
||||
return MegaDebridClient.classifyAccountFailure(error);
|
||||
}
|
||||
|
||||
function clearMegaDebridAccountCooldownState(accountId: string): void {
|
||||
megaDebridAccountCooldowns.delete(accountId);
|
||||
}
|
||||
|
||||
function megaDebridDailyParkExpiry(now: number): number {
|
||||
const midnight = new Date(now);
|
||||
midnight.setHours(24, 0, 0, 0);
|
||||
return Math.max(midnight.getTime(), now + MEGA_DEBRID_ACCOUNT_COOLDOWN_MS);
|
||||
}
|
||||
|
||||
function setMegaDebridAccountCooldownState(
|
||||
accountId: string,
|
||||
cooldownMs: number,
|
||||
@ -378,7 +366,7 @@ function setMegaDebridAccountCooldownState(
|
||||
): void {
|
||||
if (untilRestart) {
|
||||
megaDebridAccountCooldowns.set(accountId, {
|
||||
until: megaDebridDailyParkExpiry(Date.now()),
|
||||
until: Number.MAX_SAFE_INTEGER,
|
||||
message,
|
||||
category,
|
||||
untilRestart: true
|
||||
@ -1999,7 +1987,7 @@ class MegaDebridClient {
|
||||
? "Neustart"
|
||||
: new Date(accountCooldownState.until).toLocaleTimeString();
|
||||
const reasonText = accountCooldownState.untilRestart
|
||||
? "Tageslimit erreicht — bis zum Tagesreset gesperrt"
|
||||
? "Tageslimit erreicht — bis Neustart gesperrt"
|
||||
: `Cooldown bis ${untilStr}`;
|
||||
logger.info(`Mega-Debrid${accountLabel}: uebersprungen (${reasonText}), pruefe naechsten Account`);
|
||||
logAccountRotation("INFO", providerName, rotationLabel, "SKIP_COOLDOWN", {
|
||||
@ -2098,7 +2086,7 @@ class MegaDebridClient {
|
||||
const streak = recordMegaDebridEmptyResponseStreak(cooldownKey);
|
||||
if (streak >= MEGA_DEBRID_EMPTY_STREAK_UNTIL_RESTART) {
|
||||
parkUntilRestart = true;
|
||||
parkMessage = `Tageslimit erreicht (${streak}x kein Server/leere Antwort) — bis zum Tagesreset gesperrt`;
|
||||
parkMessage = `Tageslimit erreicht (${streak}x kein Server/leere Antwort) — bis Neustart gesperrt`;
|
||||
}
|
||||
} else {
|
||||
clearMegaDebridEmptyResponseStreak(cooldownKey);
|
||||
@ -2121,7 +2109,7 @@ class MegaDebridClient {
|
||||
throw new Error(`Mega-Debrid${accountLabel}: ${failure.message}`);
|
||||
}
|
||||
const cooldownInfo = parkUntilRestart
|
||||
? ", bis zum Tagesreset gesperrt"
|
||||
? ", bis Neustart gesperrt"
|
||||
: failure.cooldownMs > 0
|
||||
? `, Cooldown ${Math.ceil(failure.cooldownMs / 1000)}s`
|
||||
: "";
|
||||
@ -2158,14 +2146,14 @@ class MegaDebridClient {
|
||||
throw new Error(`mega_debrid_cooldown:${retryMs}:${cooldownFailures.join(" | ")}`);
|
||||
}
|
||||
if (parkedUntilRestartSeen) {
|
||||
throw new Error(`Mega-Debrid: Alle Accounts am Tageslimit (bis zum Tagesreset gesperrt)${cooldownFailures.length > 0 ? ` | ${cooldownFailures.join(" | ")}` : ""}`);
|
||||
throw new Error(`Mega-Debrid: Alle Accounts am Tageslimit (bis Neustart gesperrt)${cooldownFailures.length > 0 ? ` | ${cooldownFailures.join(" | ")}` : ""}`);
|
||||
}
|
||||
throw new Error("Mega-Debrid: Kein aktiver Account verfuegbar");
|
||||
}
|
||||
throw new Error(failures.join(" | ") || "Mega-Debrid: Kein aktiver Account verfuegbar");
|
||||
}
|
||||
|
||||
static classifyAccountFailure(
|
||||
private static classifyAccountFailure(
|
||||
error: unknown
|
||||
): { fatal: boolean; cooldownMs: number; message: string; category: MegaDebridCooldownCategory; limitSignal?: boolean } {
|
||||
const errorText = compactErrorText(error).replace(/^Error:\s*/i, "");
|
||||
@ -2218,7 +2206,8 @@ class MegaDebridClient {
|
||||
fatal: false,
|
||||
cooldownMs: MEGA_DEBRID_ACCOUNT_COOLDOWN_MS,
|
||||
message: "Kein Server fuer diesen Hoster (Tageslimit/Hoster nicht verfuegbar)",
|
||||
category: "quota"
|
||||
category: "quota",
|
||||
limitSignal: true
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,34 +0,0 @@
|
||||
export interface InstallResumeManager {
|
||||
isSessionRunning(): boolean;
|
||||
stop(options: { parkForRestart: boolean }): void;
|
||||
persistNowSync(): void;
|
||||
start(): Promise<void> | void;
|
||||
}
|
||||
|
||||
export async function runInstallWithResume<T extends { started: boolean }>(
|
||||
manager: InstallResumeManager,
|
||||
doInstall: () => Promise<T>
|
||||
): Promise<T> {
|
||||
const wasRunning = manager.isSessionRunning();
|
||||
if (wasRunning) {
|
||||
manager.stop({ parkForRestart: true });
|
||||
}
|
||||
manager.persistNowSync();
|
||||
|
||||
const resumeIfParked = async (): Promise<void> => {
|
||||
if (wasRunning && !manager.isSessionRunning()) {
|
||||
await manager.start();
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const result = await doInstall();
|
||||
if (!result.started) {
|
||||
await resumeIfParked();
|
||||
}
|
||||
return result;
|
||||
} catch (error) {
|
||||
await resumeIfParked();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@ -109,7 +109,7 @@ interface MegaDialogAccount {
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface AccountDialogState {
|
||||
interface AccountDialogState {
|
||||
mode: "create" | "edit";
|
||||
kind: AccountKind | null;
|
||||
token: string;
|
||||
@ -647,7 +647,7 @@ function createAccountDialogState(mode: "create" | "edit", kind: AccountKind | n
|
||||
}
|
||||
}
|
||||
|
||||
export function applyAccountDialogToSettings(settings: AppSettings, dialog: AccountDialogState): AppSettings {
|
||||
function applyAccountDialogToSettings(settings: AppSettings, dialog: AccountDialogState): AppSettings {
|
||||
if (!dialog.kind) {
|
||||
return settings;
|
||||
}
|
||||
@ -682,7 +682,7 @@ export function applyAccountDialogToSettings(settings: AppSettings, dialog: Acco
|
||||
const firstPassword = megaParsed.length > 0 ? megaParsed[0].password : "";
|
||||
const validIds = new Set(megaParsed.map((a) => a.id));
|
||||
const megaDebridDisabledAccountIds = (dialog.megaDisabledIds || []).filter((id) => validIds.has(id));
|
||||
return { ...settings, megaCredentials: megaSerialized, megaLogin: firstLogin, megaPassword: firstPassword, megaDebridApiEnabled: true, megaDebridDisabledAccountIds, providerDailyLimitBytes: nextProviderDailyLimitBytes };
|
||||
return { ...settings, megaCredentials: megaSerialized, megaLogin: firstLogin, megaPassword: firstPassword, megaDebridApiEnabled: true, megaDebridPreferApi: true, megaDebridDisabledAccountIds, providerDailyLimitBytes: nextProviderDailyLimitBytes };
|
||||
}
|
||||
case "megadebrid-web": {
|
||||
const megaSerialized = serializeMegaDebridAccounts(dialog.megaAccounts);
|
||||
@ -691,7 +691,7 @@ export function applyAccountDialogToSettings(settings: AppSettings, dialog: Acco
|
||||
const firstPassword = megaParsed.length > 0 ? megaParsed[0].password : "";
|
||||
const validIds = new Set(megaParsed.map((a) => a.id));
|
||||
const megaDebridDisabledAccountIds = (dialog.megaDisabledIds || []).filter((id) => validIds.has(id));
|
||||
return { ...settings, megaCredentials: megaSerialized, megaLogin: firstLogin, megaPassword: firstPassword, megaDebridWebEnabled: true, megaDebridDisabledAccountIds, providerDailyLimitBytes: nextProviderDailyLimitBytes };
|
||||
return { ...settings, megaCredentials: megaSerialized, megaLogin: firstLogin, megaPassword: firstPassword, megaDebridWebEnabled: true, megaDebridPreferApi: false, megaDebridDisabledAccountIds, providerDailyLimitBytes: nextProviderDailyLimitBytes };
|
||||
}
|
||||
case "bestdebrid-api":
|
||||
return { ...settings, bestToken: token, bestDebridUseWebLogin: false, providerDailyLimitBytes: nextProviderDailyLimitBytes };
|
||||
@ -1159,13 +1159,13 @@ function formatCheckedAgo(checkedAt: number): string {
|
||||
}
|
||||
|
||||
function rotationEventText(ev: { event: string; cooldownSec?: number; next?: string; reason?: string }): string {
|
||||
const untilRestart = /bis zum Tagesreset gesperrt/i.test(ev.reason || "");
|
||||
const untilRestart = /bis Neustart gesperrt/i.test(ev.reason || "");
|
||||
switch (ev.event) {
|
||||
case "OK": return "erfolgreich";
|
||||
case "FAILED": {
|
||||
if (untilRestart) {
|
||||
const nx = ev.next && ev.next !== "ENDE" ? ` → ${ev.next}` : "";
|
||||
return `Tageslimit erreicht, bis zum Tagesreset gesperrt${nx}`;
|
||||
return `Tageslimit erreicht, bis Neustart gesperrt${nx}`;
|
||||
}
|
||||
const cd = ev.cooldownSec ? `, Cooldown ${ev.cooldownSec}s` : "";
|
||||
const nx = ev.next && ev.next !== "ENDE" ? ` → ${ev.next}` : "";
|
||||
@ -1176,7 +1176,7 @@ function rotationEventText(ev: { event: string; cooldownSec?: number; next?: str
|
||||
const cd = ev.cooldownSec ? `, Cooldown ${ev.cooldownSec}s` : "";
|
||||
return `Timeout/Abbruch${cd} → nächster Account beim Retry`;
|
||||
}
|
||||
case "SKIP_COOLDOWN": return untilRestart ? "übersprungen (bis zum Tagesreset gesperrt)" : "übersprungen (Cooldown aktiv)";
|
||||
case "SKIP_COOLDOWN": return untilRestart ? "übersprungen (bis Neustart gesperrt)" : "übersprungen (Cooldown aktiv)";
|
||||
case "SKIP_DISABLED": return "übersprungen (deaktiviert)";
|
||||
case "SKIP_DAILY_LIMIT": return "übersprungen (Tageslimit erreicht)";
|
||||
case "SKIP_HOST_COOLDOWN": return "übersprungen (Host-Cooldown)";
|
||||
|
||||
@ -76,36 +76,6 @@ daily-limit aggregate early-exit.
|
||||
braucht workMs-Threading → groesserer Eingriff, nicht LOW-billig.
|
||||
- Vor Runde 4-5: SYNTHESE-Pass — ist Retry/Cooldown/Rotation END-TO-END kohaerent selbstheilend?
|
||||
|
||||
## Runde 3 (Failover/Reconnect/Cooldown-Lifecycle/Scheduler/IPC-Toggle/Updater) — Workflow wcwztx7e9
|
||||
7 confirmed / 1 refuted. provider-failover-Finder crashte (Socket) → diese Dimension via MEINER
|
||||
unabhaengigen Code-Verifikation abgedeckt (60s-Timeout kappt Failover, debrid.ts 3845 + dl-mgr 8814).
|
||||
|
||||
### Batch 3 → v1.7.217 (GEFIXT, je rot-bewiesener Test)
|
||||
- [x] #R3-1 HIGH (3/3) Self-Cooldown bis Neustart — DER vom Nutzer gemeldete „Tool sperrt sich selbst".
|
||||
(a) limitSignal aus MEGA_DEBRID_NO_SERVER_RE-Zweig entfernt (Hoster-Problem != Account-Limit),
|
||||
(b) until-restart-Park laeuft jetzt zum Tagesreset (lokale Mitternacht) ab statt MAX_SAFE_INTEGER →
|
||||
heilt <=24h selbst. Texte „bis Neustart"→„bis zum Tagesreset". Commit 76b3f99.
|
||||
- [x] #R3-5 HIGH (3/3) Fehlgeschlagenes Update → Queue-Stillstand bis Neustart. runInstallWithResume()
|
||||
(neue reine Funktion) resumt bei started:false UND throw. Commit dfd1926.
|
||||
- [x] #R3-3 MED (3/3) Account-Edit ueberschreibt megaDebridPreferApi. Hardcode entfernt → ...settings
|
||||
reicht Nutzerwahl durch. Commit 1e04b7b.
|
||||
|
||||
### Dokumentiert / NICHT autonom gefixt (Advisor-Disziplin)
|
||||
- #R3-2 HIGH (2/3, UMSTRITTEN) Mega API/Web-Account-Zeilen teilen EINE login-only Enable-Flag →
|
||||
Toggle spiegelt sich (= Nutzer-Report „API aus → Web an"). KEIN Auto-Fix: Daten-Modell-Fix braucht
|
||||
Settings-Migration (kann deaktivierte Accounts re-aktivieren), UI-Collapse = Layout-Redesign (Nutzer
|
||||
UI-Geschmack-sensibel). → DEM NUTZER vorlegen: gemeinsamer Schalter vs. unabhaengige pro-Modus-Flags.
|
||||
- 60s-Failover-Kappung (HIGH, mein Fund, Finder gecrasht) — debrid.ts 3845 wertet JEDEN combined-signal-
|
||||
Abort (cancel ODER 60s-Timeout) als kein-Failover; langsamer Provider1 hungert Provider2 aus, auch ueber
|
||||
Retries. Post-214 (API-first) groesstenteils latent. → eigene Runde: gecrashten Finder ERST neu laufen
|
||||
lassen (unabhaengige Verifikation fehlt), dann per-Provider-Timeout-Design mit Advisor. NICHT in 217.
|
||||
- #R3-6 MED (3/3) Update-Mirror-Failover feuert nie (nur Gitea). NICHT fixen: aendert den Update-Fetch-Pfad
|
||||
= der Kanal, ueber den jeder Fix den Nutzer erreicht; faellt heute sicher aus (App behaelt alte Version).
|
||||
- #R3-4 LOW (2/3) providerPrimary kann auf disabled Mega normalisieren — self-heilt zur Laufzeit. Belassen
|
||||
(Refuter: Fix riskanter als Bug — schreibt persistierte Absicht um).
|
||||
- #R3-7 LOW (3/3) Update-Integritaet hash-only, kein Authenticode — ehrliche Grenze, faellt sicher aus.
|
||||
- REFUTIERT (0/3): all-accounts-parked wirft plain error ohne cooldown-retry-Token.
|
||||
|
||||
## Runde 2 (Download-Ausfuehrung: stream/resume/disk/integrity/extract/persist) — Workflow whspc8ddv
|
||||
14 confirmed / 7 refuted (>=2/3 adversarisch). Alle HIGH/MED unten unabhaengig am echten Code
|
||||
verifiziert (Zeilen zitiert) bevor gefixt. Jeder Fix mit rot-bewiesenem Test, tsc bleibt 6.
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { applyAccountDialogToSettings, AccountDialogState } from "../src/renderer/App";
|
||||
import { defaultSettings } from "../src/main/constants";
|
||||
|
||||
function megaDialog(kind: "megadebrid-api" | "megadebrid-web"): AccountDialogState {
|
||||
return {
|
||||
mode: "edit",
|
||||
kind,
|
||||
token: "",
|
||||
login: "",
|
||||
password: "",
|
||||
dailyLimitGb: "",
|
||||
keyDailyLimitGbById: {},
|
||||
megaAccounts: [{ login: "user@x", password: "pw" }],
|
||||
megaNewLogin: "",
|
||||
megaNewPassword: "",
|
||||
megaDisabledIds: []
|
||||
};
|
||||
}
|
||||
|
||||
describe("applyAccountDialogToSettings — keeps the user's Mega preferApi choice", () => {
|
||||
it("does not flip megaDebridPreferApi to true when editing the API account", () => {
|
||||
const settings = { ...defaultSettings(), megaDebridApiEnabled: true, megaDebridWebEnabled: true, megaDebridPreferApi: false };
|
||||
const next = applyAccountDialogToSettings(settings, megaDialog("megadebrid-api"));
|
||||
expect(next.megaDebridApiEnabled).toBe(true);
|
||||
expect(next.megaDebridPreferApi).toBe(false);
|
||||
});
|
||||
|
||||
it("does not flip megaDebridPreferApi to false when editing the Web account", () => {
|
||||
const settings = { ...defaultSettings(), megaDebridApiEnabled: true, megaDebridWebEnabled: true, megaDebridPreferApi: true };
|
||||
const next = applyAccountDialogToSettings(settings, megaDialog("megadebrid-web"));
|
||||
expect(next.megaDebridWebEnabled).toBe(true);
|
||||
expect(next.megaDebridPreferApi).toBe(true);
|
||||
});
|
||||
});
|
||||
@ -3,7 +3,7 @@ import { defaultSettings, REQUEST_RETRIES } from "../src/main/constants";
|
||||
import { parseDebridLinkApiKeys } from "../src/shared/debrid-link-keys";
|
||||
import { getMegaDebridAccountId } from "../src/shared/mega-debrid-accounts";
|
||||
import { getProviderUsageDayKey } from "../src/shared/provider-daily-limits";
|
||||
import { classifyMegaDebridAccountFailureForTests, clearMegaDebridEmptyResponseStreak, DebridService, extractRapidgatorFilenameFromHtml, fetchAllDebridHostInfo, fetchDebridLinkHostLimits, filenameFromRapidgatorUrlPath, getDebridLinkKeyRuntimeStateForTests, getMegaDebridAccountCooldownState, MEGA_DEBRID_EMPTY_STREAK_UNTIL_RESTART, MEGA_DEBRID_STICKY_LINKS, normalizeResolvedFilename, primeMegaDebridUntilRestartForTests, recordMegaDebridEmptyResponseStreak, resetDebridLinkRuntimeStateForTests, resetMegaDebridRuntimeStateForTests } from "../src/main/debrid";
|
||||
import { clearMegaDebridEmptyResponseStreak, DebridService, extractRapidgatorFilenameFromHtml, fetchAllDebridHostInfo, fetchDebridLinkHostLimits, filenameFromRapidgatorUrlPath, getDebridLinkKeyRuntimeStateForTests, getMegaDebridAccountCooldownState, MEGA_DEBRID_EMPTY_STREAK_UNTIL_RESTART, MEGA_DEBRID_STICKY_LINKS, normalizeResolvedFilename, primeMegaDebridUntilRestartForTests, recordMegaDebridEmptyResponseStreak, resetDebridLinkRuntimeStateForTests, resetMegaDebridRuntimeStateForTests } from "../src/main/debrid";
|
||||
|
||||
const originalFetch = globalThis.fetch;
|
||||
|
||||
@ -1918,24 +1918,13 @@ describe("debrid service", () => {
|
||||
expect(recordMegaDebridEmptyResponseStreak(key)).toBe(1);
|
||||
});
|
||||
|
||||
it("self-heals a daily-limit park after the daily reset instead of staying locked until process restart", () => {
|
||||
it("keeps an 'until restart' park active forever (never expires until process restart)", () => {
|
||||
const key = `${getMegaDebridAccountId("user1")}:api`;
|
||||
primeMegaDebridUntilRestartForTests(key);
|
||||
const active = getMegaDebridAccountCooldownState(key);
|
||||
expect(active?.untilRestart).toBe(true);
|
||||
expect(getMegaDebridAccountCooldownState(key, Date.now() + 60_000)?.untilRestart).toBe(true);
|
||||
const afterReset = Date.now() + 25 * 60 * 60 * 1000;
|
||||
expect(getMegaDebridAccountCooldownState(key, afterReset)).toBeNull();
|
||||
});
|
||||
|
||||
it("does NOT treat a per-hoster 'no server' failure as an account daily-limit signal (no until-restart park)", () => {
|
||||
const noServer = classifyMegaDebridAccountFailureForTests(new Error("no server available for this host"));
|
||||
expect(noServer.limitSignal).toBeFalsy();
|
||||
expect(noServer.category).toBe("quota");
|
||||
expect(noServer.cooldownMs).toBeGreaterThan(0);
|
||||
|
||||
const genuineEmpty = classifyMegaDebridAccountFailureForTests(new Error("Antwort leer"));
|
||||
expect(genuineEmpty.limitSignal).toBe(true);
|
||||
const now = getMegaDebridAccountCooldownState(key);
|
||||
expect(now?.untilRestart).toBe(true);
|
||||
const farFuture = Date.now() + 100 * 24 * 60 * 60 * 1000;
|
||||
expect(getMegaDebridAccountCooldownState(key, farFuture)?.untilRestart).toBe(true);
|
||||
});
|
||||
|
||||
it("skips a Mega-Debrid account parked until restart and rotates to the next, without re-testing it", async () => {
|
||||
@ -2001,7 +1990,7 @@ describe("debrid service", () => {
|
||||
const megaWeb = vi.fn(async () => ({ fileName: "x.rar", directUrl: "https://mega-web.example/x.rar", fileSize: null, retriesUsed: 0 }));
|
||||
const service = new DebridService(settings, { megaWebUnrestrict: megaWeb });
|
||||
|
||||
await expect(service.unrestrictLink("https://rapidgator.net/file/all-parked-test")).rejects.toThrow(/bis zum Tagesreset gesperrt/i);
|
||||
await expect(service.unrestrictLink("https://rapidgator.net/file/all-parked-test")).rejects.toThrow(/bis Neustart gesperrt/i);
|
||||
expect(megaWeb).not.toHaveBeenCalled();
|
||||
}, 20000);
|
||||
|
||||
|
||||
@ -1,63 +0,0 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { runInstallWithResume, InstallResumeManager } from "../src/main/update-install-flow";
|
||||
|
||||
function makeManager(running: boolean): InstallResumeManager & { startCalls: number; stopCalls: number; persistCalls: number; sessionRunning: boolean } {
|
||||
return {
|
||||
sessionRunning: running,
|
||||
startCalls: 0,
|
||||
stopCalls: 0,
|
||||
persistCalls: 0,
|
||||
isSessionRunning() {
|
||||
return this.sessionRunning;
|
||||
},
|
||||
stop() {
|
||||
this.stopCalls += 1;
|
||||
this.sessionRunning = false;
|
||||
},
|
||||
persistNowSync() {
|
||||
this.persistCalls += 1;
|
||||
},
|
||||
async start() {
|
||||
this.startCalls += 1;
|
||||
this.sessionRunning = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
describe("runInstallWithResume", () => {
|
||||
it("resumes a running session when the install returns started:false", async () => {
|
||||
const m = makeManager(true);
|
||||
const result = await runInstallWithResume(m, async () => ({ started: false }));
|
||||
expect(result.started).toBe(false);
|
||||
expect(m.stopCalls).toBe(1);
|
||||
expect(m.startCalls).toBe(1);
|
||||
expect(m.isSessionRunning()).toBe(true);
|
||||
});
|
||||
|
||||
it("resumes a running session when the install THROWS, then rethrows", async () => {
|
||||
const m = makeManager(true);
|
||||
await expect(
|
||||
runInstallWithResume(m, async () => {
|
||||
throw new Error("network down");
|
||||
})
|
||||
).rejects.toThrow("network down");
|
||||
expect(m.stopCalls).toBe(1);
|
||||
expect(m.startCalls).toBe(1);
|
||||
expect(m.isSessionRunning()).toBe(true);
|
||||
});
|
||||
|
||||
it("does NOT resume when the install succeeds (started:true) — the app is about to quit", async () => {
|
||||
const m = makeManager(true);
|
||||
const result = await runInstallWithResume(m, async () => ({ started: true }));
|
||||
expect(result.started).toBe(true);
|
||||
expect(m.startCalls).toBe(0);
|
||||
expect(m.isSessionRunning()).toBe(false);
|
||||
});
|
||||
|
||||
it("does NOT resume when no session was running before the install", async () => {
|
||||
const m = makeManager(false);
|
||||
await runInstallWithResume(m, async () => ({ started: false }));
|
||||
expect(m.stopCalls).toBe(0);
|
||||
expect(m.startCalls).toBe(0);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user