feat: add Deepbrid, daily scheduling, and notification center

Add encrypted Deepbrid API accounts with account validation, provider routing, fallback, usage tracking, safe error handling, and verified 1Fichier downloads. Restore persistent recurring daily starts with local-calendar deduplication and legacy schedule compatibility. Add durable Discord package, run, remaining-volume, stall, and recovery notifications with privacy-safe telemetry and disk-failure recovery.
This commit is contained in:
Sucukdeluxe
2026-08-24 07:37:55 +02:00
parent 06e5bf4340
commit 1b7caba2eb
83 changed files with 12769 additions and 1098 deletions
+77 -1
View File
@@ -1,9 +1,35 @@
import { describe, expect, it } from "vitest";
import { describe, expect, it, vi } from "vitest";
import { defaultSettings } from "../src/main/constants";
import { createRendererSettings } from "../src/main/renderer-state";
import { validateRendererSettingsUpdate } from "../src/main/renderer-settings";
describe("renderer settings validation", () => {
it("accepts every editable notification control while keeping the webhook write-only", () => {
const current = { ...defaultSettings(), notifyUrl: "https://notify.example.test/private-hook" };
const update = {
notifyPackageSuccessMode: "individual",
notifyOnRemainingBelow: true,
notifyRemainingThresholdGb: 25,
notifyOnDownloadStall: true,
notifyStallAfterSeconds: 120,
notifyStallCooldownMinutes: 15,
notifyOnDownloadRecovery: false
};
const projected = createRendererSettings(current);
expect(projected).not.toHaveProperty("notifyUrl");
expect(projected.notifyUrlConfigured).toBe(true);
expect(validateRendererSettingsUpdate(update, current)).toEqual(update);
});
it("rejects unsupported package success modes at the IPC boundary", () => {
const current = defaultSettings();
expect(validateRendererSettingsUpdate({ notifyPackageSuccessMode: "digest" }, current)).toEqual({ notifyPackageSuccessMode: "digest" });
expect(validateRendererSettingsUpdate({ notifyPackageSuccessMode: "individual" }, current)).toEqual({ notifyPackageSuccessMode: "individual" });
expect(() => validateRendererSettingsUpdate({ notifyPackageSuccessMode: "batched" }, current)).toThrow("Settings-Payload ist ungültig");
});
it("accepts a complete settings save when an account status has no optional email", () => {
const current = defaultSettings();
current.debridAccountStatuses = {
@@ -47,4 +73,54 @@ describe("renderer settings validation", () => {
expect(projected.archivePasswordListConfigured).toBe(true);
expect(JSON.stringify(projected)).not.toContain(password);
});
it("projects daily start state and accepts only editable calendar controls", () => {
vi.useFakeTimers();
vi.setSystemTime(new Date(2026, 7, 22, 9, 0));
try {
const current = {
...defaultSettings(),
dailyStartEnabled: true,
dailyStartMinuteOfDay: 18 * 60 + 45,
dailyStartFirstLocalDate: "2026-08-23",
dailyStartLastHandledLocalDate: "2026-08-22",
dailyStartPendingLocalDate: "",
dailyStartLastOutcome: "started" as const
};
const projected = createRendererSettings(current);
expect(projected).toMatchObject({
dailyStartEnabled: true,
dailyStartMinuteOfDay: 18 * 60 + 45,
dailyStartFirstLocalDate: "2026-08-23",
dailyStartLastHandledLocalDate: "2026-08-22",
dailyStartPendingLocalDate: "",
dailyStartLastOutcome: "started"
});
expect(projected.nextDailyStartEpochMs).toBe(new Date(2026, 7, 23, 18, 45, 0, 0).getTime());
expect(validateRendererSettingsUpdate({
dailyStartEnabled: true,
dailyStartMinuteOfDay: 7 * 60 + 30,
dailyStartFirstLocalDate: "2026-08-24",
dailyStartLastHandledLocalDate: "2026-08-23",
dailyStartPendingLocalDate: "2026-08-24",
dailyStartLastOutcome: "missed",
nextDailyStartEpochMs: 123
}, current)).toEqual({
dailyStartEnabled: true,
dailyStartMinuteOfDay: 7 * 60 + 30,
dailyStartFirstLocalDate: "2026-08-24"
});
} finally {
vi.useRealTimers();
}
});
it("rejects invalid editable daily calendar values at the renderer boundary", () => {
const current = defaultSettings();
expect(() => validateRendererSettingsUpdate({ dailyStartMinuteOfDay: 1_440 }, current)).toThrow("Settings-Payload ist ungültig");
expect(() => validateRendererSettingsUpdate({ dailyStartMinuteOfDay: 12.5 }, current)).toThrow("Settings-Payload ist ungültig");
expect(() => validateRendererSettingsUpdate({ dailyStartFirstLocalDate: "2026-02-29" }, current)).toThrow("Settings-Payload ist ungültig");
});
});