real-debrid-downloader/tests/startup-health-check.test.ts
Sucukdeluxe 90f347dc2b Startup Health-Check: proaktive Warnungen bei Problem-Zustaenden
Laeuft einmal beim App-Start und warnt klar im Log, wenn etwas auffaellt
— BEVOR der Nutzer mitten im Download stolpert. Blockiert den Start
nicht, schreibt nur in rd_downloader.log + audit-log.

Pruefungen:
- Download-Ziel-Ordner fehlt / nicht beschreibbar / nicht konfiguriert
- Runtime-Ordner (%APPDATA%/runtime) fehlt oder nicht beschreibbar
- Wenig Festplattenplatz im Download-Ordner (< 5 GB)
- Kein einziger Debrid-Provider konfiguriert → Downloads koennen nicht
  funktionieren
- State-Datei > 50 MB (alte abgeschlossene Pakete sollten geprunt werden)

Listet zudem als INFO alle aktiv konfigurierten Provider auf, damit aus
dem Startup-Log klar ist was aktiv ist (Mega-Debrid X Accounts,
Debrid-Link X Keys, etc.).

Reine Funktion runStartupHealthCheck() → HealthCheckReport, 6 Unit-Tests
decken die wichtigsten Pfade ab. Wiring in AppController-Constructor ist
in try/catch — falls der Check selbst abstuerzt, stoert das den Start
nicht.
2026-04-20 20:20:25 +02:00

138 lines
4.5 KiB
TypeScript

import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { defaultSettings } from "../src/main/constants";
import { createStoragePaths } from "../src/main/storage";
import { runStartupHealthCheck } from "../src/main/startup-health-check";
const tempDirs: string[] = [];
afterEach(() => {
for (const dir of tempDirs.splice(0)) {
try {
fs.rmSync(dir, { recursive: true, force: true });
} catch {
// ignore cleanup errors
}
}
});
function makeTempBase(): { baseDir: string; outputDir: string; paths: ReturnType<typeof createStoragePaths> } {
const baseDir = fs.mkdtempSync(path.join(os.tmpdir(), "rd-health-"));
tempDirs.push(baseDir);
const outputDir = path.join(baseDir, "downloads");
fs.mkdirSync(outputDir, { recursive: true });
return {
baseDir: path.join(baseDir, "runtime"),
outputDir,
paths: createStoragePaths(path.join(baseDir, "runtime"))
};
}
describe("runStartupHealthCheck", () => {
it("flags missing download directory", () => {
const { outputDir, paths } = makeTempBase();
fs.mkdirSync(paths.baseDir, { recursive: true });
const settings = {
...defaultSettings(),
token: "rd-token",
outputDir: path.join(outputDir, "does-not-exist-subdir")
};
const report = runStartupHealthCheck(settings, paths);
const codes = report.findings.map((f) => f.code);
expect(codes).toContain("outputDir_not_found");
});
it("flags no-provider-configured when all credentials are empty", () => {
const { outputDir, paths } = makeTempBase();
fs.mkdirSync(paths.baseDir, { recursive: true });
const settings = {
...defaultSettings(),
token: "",
megaLogin: "",
megaPassword: "",
megaCredentials: "",
allDebridToken: "",
bestToken: "",
oneFichierApiKey: "",
debridLinkApiKeys: "",
outputDir
};
const report = runStartupHealthCheck(settings, paths);
const codes = report.findings.map((f) => f.code);
expect(codes).toContain("no_provider_configured");
expect(report.warnCount).toBeGreaterThanOrEqual(1);
});
it("reports configured providers when at least one credential is set", () => {
const { outputDir, paths } = makeTempBase();
fs.mkdirSync(paths.baseDir, { recursive: true });
const settings = {
...defaultSettings(),
token: "rd-token-here",
debridLinkApiKeys: "dl-key-a\ndl-key-b",
outputDir
};
const report = runStartupHealthCheck(settings, paths);
const providersFinding = report.findings.find((f) => f.code === "providers_configured");
expect(providersFinding).toBeDefined();
expect(providersFinding?.message).toContain("Real-Debrid");
expect(providersFinding?.message).toContain("Debrid-Link");
expect(providersFinding?.message).toContain("2 Keys");
});
it("flags large state files", () => {
const { outputDir, paths } = makeTempBase();
fs.mkdirSync(paths.baseDir, { recursive: true });
// 60 MB dummy state file, threshold is 50 MB
fs.writeFileSync(paths.sessionFile, Buffer.alloc(60 * 1024 * 1024, 0));
const settings = {
...defaultSettings(),
token: "rd-token",
outputDir
};
const report = runStartupHealthCheck(settings, paths);
const codes = report.findings.map((f) => f.code);
expect(codes).toContain("large_state_file");
});
it("flags missing base dir as ERROR", () => {
const { outputDir, paths } = makeTempBase();
// Intentionally DON'T create baseDir.
const settings = {
...defaultSettings(),
token: "rd-token",
outputDir
};
const report = runStartupHealthCheck(settings, paths);
const codes = report.findings.map((f) => f.code);
expect(codes).toContain("baseDir_missing");
expect(report.errorCount).toBeGreaterThanOrEqual(1);
});
it("passes cleanly when everything is healthy", () => {
const { outputDir, paths } = makeTempBase();
fs.mkdirSync(paths.baseDir, { recursive: true });
const settings = {
...defaultSettings(),
token: "rd-token-here",
outputDir
};
const report = runStartupHealthCheck(settings, paths);
expect(report.errorCount).toBe(0);
const codes = report.findings.map((f) => f.code);
expect(codes).not.toContain("outputDir_not_found");
expect(codes).not.toContain("outputDir_not_writable");
expect(codes).not.toContain("no_provider_configured");
expect(codes).not.toContain("baseDir_missing");
expect(codes).not.toContain("baseDir_not_writable");
});
});