Files
Multi-Debrid-Downloader/tests/history-reveal.test.ts
T
Sucukdeluxe 069babfd54 feat: deliver the redesigned desktop workspace
Rebuild downloads, link collection, settings, history, and statistics around a responsive desktop shell with compact account and queue tables, contextual navigation, persistent update affordances, unified overlays, and accessible keyboard interactions.

Add safe history-folder reveal IPC, responsive 2560/1920/1366/1120 coverage, deterministic visual fixtures, focused component regressions, and release-tree exclusions for internal working files. Bump the public application version to 2.0.13.
2026-08-10 14:15:57 +02:00

143 lines
6.1 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("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" });
});
});