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.
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
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");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user