Files
Multi-Debrid-Downloader/tests/history-reveal.test.ts
T
Sucukdeluxe 1b7caba2eb 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.
2026-08-24 07:37:55 +02:00

150 lines
6.5 KiB
TypeScript

import { win32 } from "node:path";
import { describe, expect, it, vi } from "vitest";
import type { HistoryEntry } from "../src/shared/types";
import {
revealHistoryEntry,
type HistoryRevealDependencies
} from "../src/main/history-reveal";
function historyEntry(overrides: Partial<HistoryEntry> = {}): HistoryEntry {
return {
id: "known-id",
name: "Paket",
totalBytes: 1,
downloadedBytes: 1,
fileCount: 1,
provider: "realdebrid",
completedAt: 1,
durationSeconds: 1,
status: "completed",
outputDir: "C:\\Downloads\\Paket",
urls: [],
...overrides
};
}
function dependencies(overrides: Partial<HistoryRevealDependencies> = {}): HistoryRevealDependencies {
return {
loadHistory: () => [historyEntry()],
stat: vi.fn(async () => ({ isDirectory: () => true })),
openPath: vi.fn(async () => ""),
...overrides
};
}
describe("revealHistoryEntry", () => {
it.each(["", " ", " padded", "padded ", "x".repeat(257)])("rejects malformed entry id %j before loading history", async (entryId) => {
const loadHistory = vi.fn(() => [historyEntry()]);
const deps = dependencies({ loadHistory });
await expect(revealHistoryEntry({ entryId }, deps)).resolves.toEqual({ ok: false, reason: "entry-not-found" });
expect(loadHistory).not.toHaveBeenCalled();
expect(deps.stat).not.toHaveBeenCalled();
expect(deps.openPath).not.toHaveBeenCalled();
});
it("resolves a known case-sensitive id to the authoritative directory and opens it exactly once", async () => {
const deps = dependencies();
await expect(revealHistoryEntry({ entryId: "known-id" }, deps)).resolves.toEqual({ ok: true });
expect(deps.stat).toHaveBeenCalledTimes(1);
expect(deps.stat).toHaveBeenCalledWith("C:\\Downloads\\Paket");
expect(deps.openPath).toHaveBeenCalledTimes(1);
expect(deps.openPath).toHaveBeenCalledWith("C:\\Downloads\\Paket");
});
it.each(["partial", "failed", "cancelled"] as const)("opens a %s history result from the same authoritative directory", async (status) => {
const deps = dependencies({ loadHistory: () => [historyEntry({ status })] });
await expect(revealHistoryEntry({ entryId: "known-id" }, deps)).resolves.toEqual({ ok: true });
expect(deps.openPath).toHaveBeenCalledWith("C:\\Downloads\\Paket");
});
it("ignores every renderer-supplied field except entryId", async () => {
const deps = dependencies();
await expect(revealHistoryEntry({ entryId: "known-id", outputDir: "C:\\Angriff" } as never, deps)).resolves.toEqual({ ok: true });
expect(deps.openPath).toHaveBeenCalledWith("C:\\Downloads\\Paket");
});
it("rejects unknown and differently cased ids before path inspection", async () => {
const deps = dependencies();
await expect(revealHistoryEntry({ entryId: "KNOWN-ID" }, deps)).resolves.toEqual({ ok: false, reason: "entry-not-found" });
expect(deps.stat).not.toHaveBeenCalled();
expect(deps.openPath).not.toHaveBeenCalled();
});
it.each([
"relative\\folder",
"C:relative\\folder",
"\\current-drive-rooted",
"/current-drive-rooted",
"\\\\server",
"\\\\server\\",
"\\\\..\\share\\folder",
"\\\\server\\.\\folder",
"\\\\server\\..\\folder",
"\\\\.\\C:\\folder",
"\\\\?\\C:\\folder",
"\\\\?\\GLOBALROOT\\Device\\HarddiskVolumeShadowCopy1",
"C:\\folder:stream",
"\\\\server\\share\\folder:stream",
"C:\\folder\0bad",
"C:\\folder\nbad",
"C:\\bad?name"
])("rejects unsafe or non-absolute Windows path %s", async (outputDir) => {
const deps = dependencies({ loadHistory: () => [historyEntry({ outputDir })] });
await expect(revealHistoryEntry({ entryId: "known-id" }, deps)).resolves.toEqual({ ok: false, reason: "invalid-output-dir" });
expect(deps.stat).not.toHaveBeenCalled();
expect(deps.openPath).not.toHaveBeenCalled();
});
it.each([
["C:/Media/Folder//Child", win32.normalize("C:/Media/Folder//Child")],
["\\\\server\\share\\Folder\\Child\\", win32.normalize("\\\\server\\share\\Folder\\Child\\")]
])("normalizes valid drive and UNC paths before stat and openPath", async (outputDir, normalized) => {
const deps = dependencies({ loadHistory: () => [historyEntry({ outputDir })] });
await expect(revealHistoryEntry({ entryId: "known-id" }, deps)).resolves.toEqual({ ok: true });
expect(deps.stat).toHaveBeenCalledWith(normalized);
expect(deps.openPath).toHaveBeenCalledWith(normalized);
});
it("maps a missing directory and a file target without calling openPath", async () => {
const missing = dependencies({ stat: vi.fn(async () => { throw Object.assign(new Error("missing"), { code: "ENOENT" }); }) });
const file = dependencies({ stat: vi.fn(async () => ({ isDirectory: () => false })) });
await expect(revealHistoryEntry({ entryId: "known-id" }, missing)).resolves.toEqual({ ok: false, reason: "output-dir-missing" });
await expect(revealHistoryEntry({ entryId: "known-id" }, file)).resolves.toEqual({ ok: false, reason: "output-dir-not-directory" });
expect(missing.openPath).not.toHaveBeenCalled();
expect(file.openPath).not.toHaveBeenCalled();
});
it.each([
Object.assign(new Error("denied"), { code: "EACCES" }),
new Error("network unavailable")
])("maps non-missing stat failures to open-failed without calling openPath", async (error) => {
const deps = dependencies({ stat: vi.fn(async () => { throw error; }) });
await expect(revealHistoryEntry({ entryId: "known-id" }, deps)).resolves.toEqual({ ok: false, reason: "open-failed" });
expect(deps.openPath).not.toHaveBeenCalled();
});
it("accepts followed junction or symlink stats when the resolved target is a directory", async () => {
const deps = dependencies({ stat: vi.fn(async () => ({ isDirectory: () => true, isSymbolicLink: () => true })) });
await expect(revealHistoryEntry({ entryId: "known-id" }, deps)).resolves.toEqual({ ok: true });
expect(deps.openPath).toHaveBeenCalledTimes(1);
});
it("treats a non-empty shell result and a rejected shell promise as open failures", async () => {
const returnedError = dependencies({ openPath: vi.fn(async () => "Zugriff verweigert") });
const rejected = dependencies({ openPath: vi.fn(async () => { throw new Error("shell failed"); }) });
await expect(revealHistoryEntry({ entryId: "known-id" }, returnedError)).resolves.toEqual({ ok: false, reason: "open-failed" });
await expect(revealHistoryEntry({ entryId: "known-id" }, rejected)).resolves.toEqual({ ok: false, reason: "open-failed" });
});
});