Files
Multi-Debrid-Downloader/tests/account-preload.test.ts
T
Sucukdeluxe 14525fa4c3 Restore package-based link collector without blocking DLC drops
Reintroduce expandable collector packages, background metadata enrichment, filters, selection, and controlled queue transfer. Keep DLC files dropped outside the collector on the direct addContainers path without settings persistence or metadata waits. Protect collector state with stable URL identities, non-degrading metadata merges, and per-URL generations so stale enrichment responses cannot overwrite newer data or resurrect removed links.
2026-08-26 13:52:49 +02:00

138 lines
5.5 KiB
TypeScript

import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { IPC_CHANNELS } from "../src/shared/ipc";
import type { ElectronApi } from "../src/shared/preload-api";
const electron = vi.hoisted(() => ({
api: undefined as ElectronApi | undefined,
invoke: vi.fn<(...args: unknown[]) => Promise<unknown>>(async () => undefined),
getPathForFile: vi.fn(() => "C:\\Imports\\dropped.dlc")
}));
vi.mock("electron", () => ({
contextBridge: {
exposeInMainWorld: (_name: string, api: ElectronApi) => {
electron.api = api;
}
},
ipcRenderer: {
invoke: electron.invoke,
on: vi.fn(),
removeListener: vi.fn(),
send: vi.fn()
},
webUtils: { getPathForFile: electron.getPathForFile }
}));
describe("account preload contract", () => {
beforeAll(async () => {
await import("../src/preload/preload");
});
beforeEach(() => {
electron.invoke.mockClear();
});
it("forwards submitted secrets only in write-only account commands", async () => {
const secret = "fixture-preload-secret-5zK1";
electron.invoke.mockResolvedValueOnce({ accountId: "mda_fixture", settings: { language: "de" }, accounts: [] });
const result = await electron.api?.createAccount({
action: "create",
kind: "megadebrid-api",
identity: "preload-account@example.test",
secret,
dailyLimitBytes: 0
});
expect(electron.invoke).toHaveBeenCalledWith(
IPC_CHANNELS.CREATE_ACCOUNT,
expect.objectContaining({ secret })
);
expect(JSON.stringify(result)).not.toContain(secret);
});
it("exposes separate replace, update-secret and delete channels", async () => {
electron.invoke.mockResolvedValue({ accountId: "mda_fixture", settings: { language: "de" }, accounts: [] });
await electron.api?.replaceAccount({ action: "replace", kind: "megadebrid-api", accountId: "mda_fixture", identity: "account@example.test", secret: "", dailyLimitBytes: 0 });
await electron.api?.updateAccountSecret({ action: "update-secret", kind: "megadebrid-api", accountId: "mda_fixture", secret: "fixture-new-secret-8sP4" });
await electron.api?.deleteAccount({ action: "delete", kind: "megadebrid-api", accountId: "mda_fixture" });
expect(electron.invoke.mock.calls.map((call) => call[0])).toEqual([
IPC_CHANNELS.REPLACE_ACCOUNT,
IPC_CHANNELS.UPDATE_ACCOUNT_SECRET,
IPC_CHANNELS.DELETE_ACCOUNT
]);
});
it("forwards the selected bulk account-check scope", async () => {
electron.invoke.mockResolvedValue([]);
await electron.api?.checkDebridAccounts("active");
await electron.api?.checkDebridAccounts("all");
expect(electron.invoke.mock.calls).toEqual([
[IPC_CHANNELS.CHECK_DEBRID_ACCOUNTS, "active"],
[IPC_CHANNELS.CHECK_DEBRID_ACCOUNTS, "all"]
]);
});
it("forwards account-bound existing and create browser logins", async () => {
await electron.api?.openRealDebridLogin({ accountId: "rdw_existing" });
await electron.api?.openRealDebridLogin({ accountId: "rdw_reserved", create: true, dailyLimitBytes: 10_000 });
expect(electron.invoke.mock.calls).toEqual([
[IPC_CHANNELS.OPEN_REALDEBRID_LOGIN, { accountId: "rdw_existing" }],
[IPC_CHANNELS.OPEN_REALDEBRID_LOGIN, { accountId: "rdw_reserved", create: true, dailyLimitBytes: 10_000 }]
]);
});
it("reveals a stored secret only through the explicit account channel", async () => {
electron.invoke.mockResolvedValueOnce({ secret: "fixture-revealed-secret-7gH8" });
const result = await (electron.api as ElectronApi & {
revealAccountSecret: (request: { kind: "realdebrid-api"; accountId: string }) => Promise<{ secret: string }>;
}).revealAccountSecret({ kind: "realdebrid-api", accountId: "svc-realdebrid" });
expect(electron.invoke).toHaveBeenCalledWith(
IPC_CHANNELS.REVEAL_ACCOUNT_SECRET,
{ kind: "realdebrid-api", accountId: "svc-realdebrid" }
);
expect(result).toEqual({ secret: "fixture-revealed-secret-7gH8" });
});
it("loads the stored archive password list only through its dedicated channel", async () => {
const passwords = "fixture-archive-one\nfixture-archive-two";
electron.invoke.mockResolvedValueOnce({ passwords });
const result = await (electron.api as ElectronApi & {
getArchivePasswordList: () => Promise<{ passwords: string }>;
}).getArchivePasswordList();
expect(electron.invoke).toHaveBeenCalledWith(IPC_CHANNELS.GET_ARCHIVE_PASSWORD_LIST);
expect(result).toEqual({ passwords });
});
it("exposes separate collector preparation and enrichment channels", async () => {
const textRequest = { rawText: "https://example.com/file", addedAt: 1234 };
const packages = [{ id: "package", name: "Paket", nameSource: "inferred" as const, addedAt: 1234, links: [] }];
await electron.api?.prepareCollectorText(textRequest);
await electron.api?.prepareCollectorContainers(["C:\\Imports\\sample.dlc"], 2345);
await electron.api?.enrichCollectorPackages({ packages });
expect(electron.invoke.mock.calls).toEqual([
[IPC_CHANNELS.PREPARE_COLLECTOR_TEXT, textRequest],
[IPC_CHANNELS.PREPARE_COLLECTOR_CONTAINERS, ["C:\\Imports\\sample.dlc"], 2345],
[IPC_CHANNELS.ENRICH_COLLECTOR_PACKAGES, { packages }]
]);
});
it("resolves dropped files through Electron webUtils without IPC", () => {
const file = { name: "dropped.dlc" } as File;
expect(electron.api?.getPathForDroppedFile(file)).toBe("C:\\Imports\\dropped.dlc");
expect(electron.getPathForFile).toHaveBeenCalledWith(file);
expect(electron.invoke).not.toHaveBeenCalled();
});
});