163 lines
8.7 KiB
TypeScript
163 lines
8.7 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import crypto from "node:crypto";
|
|
import { defaultSettings } from "../src/main/constants";
|
|
import { createRendererState } from "../src/main/renderer-state";
|
|
import { getDebridLinkApiKeyId } from "../src/shared/debrid-link-keys";
|
|
import { getMegaDebridAccountId } from "../src/shared/mega-debrid-accounts";
|
|
import { serializeRealDebridApiAccounts } from "../src/shared/real-debrid-accounts";
|
|
import type { AppSettings, RendererAccountKind } from "../src/shared/types";
|
|
|
|
const SECRETS = {
|
|
token: "fixture-rd-token-7vQ2",
|
|
megaPassword: "fixture-mega-password-8kM3",
|
|
bestToken: "fixture-best-token-5xL9",
|
|
allDebridToken: "fixture-ad-token-4pR6",
|
|
ddownloadPassword: "fixture-dd-password-2tN8",
|
|
oneFichierApiKey: "fixture-onefichier-key-6cW1",
|
|
debridLinkApiKey: "fixture-debridlink-key-9hS4",
|
|
linkSnappyPassword: "fixture-linksnappy-password-3mB7",
|
|
archivePassword: "fixture-archive-password-1jD5",
|
|
notifyUrl: "https://notify.example.test/hooks/fixture-notify-secret-0fA2"
|
|
} as const;
|
|
|
|
const ACCOUNT_FIXTURES: Array<{
|
|
kind: RendererAccountKind;
|
|
secret: string;
|
|
settings: Partial<AppSettings>;
|
|
}> = [
|
|
{ kind: "realdebrid-api", secret: "fixture-rd-api-secret-1aK4", settings: { token: "fixture-rd-api-secret-1aK4" } },
|
|
{ kind: "realdebrid-web", secret: "fixture-rd-web-session-2bL5", settings: { token: "fixture-rd-web-session-2bL5", realDebridUseWebLogin: true } },
|
|
{ kind: "megadebrid-api", secret: "fixture-mega-api-secret-3cM6", settings: { megaCredentials: "mega-api@example.test:fixture-mega-api-secret-3cM6", megaDebridApiCredentials: "mega-api@example.test:fixture-mega-api-secret-3cM6", megaDebridApiEnabled: true } },
|
|
{ kind: "megadebrid-web", secret: "fixture-mega-web-secret-4dN7", settings: { megaCredentials: "mega-web@example.test:fixture-mega-web-secret-4dN7", megaDebridWebCredentials: "mega-web@example.test:fixture-mega-web-secret-4dN7", megaDebridWebEnabled: true } },
|
|
{ kind: "bestdebrid-api", secret: "fixture-best-api-secret-5eP8", settings: { bestToken: "fixture-best-api-secret-5eP8" } },
|
|
{ kind: "bestdebrid-web", secret: "fixture-best-web-session-6fQ9", settings: { bestToken: "fixture-best-web-session-6fQ9", bestDebridUseWebLogin: true } },
|
|
{ kind: "alldebrid-api", secret: "fixture-all-api-secret-7gR1", settings: { allDebridToken: "fixture-all-api-secret-7gR1" } },
|
|
{ kind: "alldebrid-web", secret: "fixture-all-web-session-8hS2", settings: { allDebridToken: "fixture-all-web-session-8hS2", allDebridUseWebLogin: true } },
|
|
{ kind: "ddownload-login", secret: "fixture-dd-secret-9jT3", settings: { ddownloadLogin: "dd@example.test", ddownloadPassword: "fixture-dd-secret-9jT3" } },
|
|
{ kind: "onefichier-api", secret: "fixture-one-secret-0kU4", settings: { oneFichierApiKey: "fixture-one-secret-0kU4" } },
|
|
{ kind: "debridlink-api", secret: "fixture-dl-secret-1mV5", settings: { debridLinkApiKeys: "fixture-dl-secret-1mV5" } },
|
|
{ kind: "linksnappy-login", secret: "fixture-ls-secret-2nW6", settings: { linkSnappyLogin: "ls@example.test", linkSnappyPassword: "fixture-ls-secret-2nW6" } }
|
|
];
|
|
|
|
describe("renderer state serialization", () => {
|
|
it("projects distinct Real-Debrid API and Web rows with per-account state", () => {
|
|
const firstToken = "fixture-renderer-rd-first-1aB2";
|
|
const secondToken = "fixture-renderer-rd-second-3cD4";
|
|
const firstId = "rda_rendererFirst";
|
|
const secondId = "rda_rendererSecond";
|
|
const settings = {
|
|
...defaultSettings(),
|
|
realDebridApiTokens: serializeRealDebridApiAccounts([{ id: firstId, token: firstToken }, { id: secondId, token: secondToken }]),
|
|
realDebridWebAccountIds: ["rdw_first"],
|
|
realDebridDisabledAccountIds: [secondId],
|
|
realDebridAccountDailyLimitBytes: { [firstId]: 100 },
|
|
realDebridAccountDailyUsageBytes: { [firstId]: 25 },
|
|
realDebridAccountTotalUsageBytes: { [firstId]: 500 },
|
|
debridAccountStatuses: {
|
|
[firstId]: { accountId: firstId, provider: "realdebrid" as const, label: "API-Token 1", maskedLogin: "Geschützter API-Token", valid: true, isPremium: true, premiumUntilMs: null, message: "Premium aktiv", checkedAt: 1 }
|
|
}
|
|
};
|
|
const state = createRendererState(settings);
|
|
const realDebridRows = state.accounts.filter((account) => account.provider === "realdebrid");
|
|
|
|
expect(realDebridRows).toEqual([
|
|
expect.objectContaining({ accountId: firstId, kind: "realdebrid-api", enabled: true, dailyLimitBytes: 100, dailyUsageBytes: 25, totalUsageBytes: 500, status: expect.objectContaining({ accountId: firstId }) }),
|
|
expect.objectContaining({ accountId: secondId, kind: "realdebrid-api", enabled: false }),
|
|
expect.objectContaining({ accountId: "rdw_first", kind: "realdebrid-web", enabled: true })
|
|
]);
|
|
expect(JSON.stringify(state)).not.toContain(firstToken);
|
|
expect(JSON.stringify(state)).not.toContain(secondToken);
|
|
expect(realDebridRows[0].accountId).not.toBe(`rda_${crypto.createHash("sha256").update(firstToken).digest("hex").slice(0, 32)}`);
|
|
});
|
|
it.each(ACCOUNT_FIXTURES)("serializes $kind without its representative secret", ({ kind, secret, settings }) => {
|
|
const state = createRendererState({ ...defaultSettings(), ...settings });
|
|
|
|
expect(state.accounts).toEqual(expect.arrayContaining([expect.objectContaining({ kind, hasSecret: true })]));
|
|
expect(JSON.stringify(state)).not.toContain(secret);
|
|
expect(JSON.stringify(state.settings)).not.toContain(secret);
|
|
});
|
|
|
|
it("excludes every provider and settings secret while preserving safe account metadata", () => {
|
|
const megaLogin = "renderer-fixture@example.test";
|
|
const settings = {
|
|
...defaultSettings(),
|
|
token: SECRETS.token,
|
|
megaLogin,
|
|
megaPassword: SECRETS.megaPassword,
|
|
megaCredentials: `${megaLogin}:${SECRETS.megaPassword}`,
|
|
megaDebridApiCredentials: `${megaLogin}:${SECRETS.megaPassword}`,
|
|
megaDebridApiEnabled: true,
|
|
bestToken: SECRETS.bestToken,
|
|
allDebridToken: SECRETS.allDebridToken,
|
|
ddownloadLogin: "renderer-dd@example.test",
|
|
ddownloadPassword: SECRETS.ddownloadPassword,
|
|
oneFichierApiKey: SECRETS.oneFichierApiKey,
|
|
debridLinkApiKeys: SECRETS.debridLinkApiKey,
|
|
linkSnappyLogin: "renderer-linksnappy@example.test",
|
|
linkSnappyPassword: SECRETS.linkSnappyPassword,
|
|
archivePasswordList: SECRETS.archivePassword,
|
|
notifyUrl: SECRETS.notifyUrl
|
|
};
|
|
|
|
const state = createRendererState(settings);
|
|
const serialized = JSON.stringify(state);
|
|
|
|
for (const secret of Object.values(SECRETS)) {
|
|
expect(serialized).not.toContain(secret);
|
|
}
|
|
expect(state.settings.archivePasswordListConfigured).toBe(true);
|
|
expect(state.settings.notifyUrlConfigured).toBe(true);
|
|
expect(state.accounts).toEqual(expect.arrayContaining([
|
|
expect.objectContaining({ kind: "realdebrid-api", hasSecret: true }),
|
|
expect.objectContaining({ kind: "megadebrid-api", identity: megaLogin, hasSecret: true }),
|
|
expect.objectContaining({ kind: "debridlink-api", hasSecret: true })
|
|
]));
|
|
});
|
|
|
|
it("redacts individual secrets embedded in status metadata for multi-account pools", () => {
|
|
const firstMegaSecret = "fixture-first-mega-pool-secret-3pX7";
|
|
const secondMegaSecret = "fixture-second-mega-pool-secret-4qY8";
|
|
const firstKey = "fixture-first-debridlink-pool-secret-5rZ9";
|
|
const secondKey = "fixture-second-debridlink-pool-secret-6sA1";
|
|
const secondMegaId = getMegaDebridAccountId("second-pool@example.test");
|
|
const secondKeyId = getDebridLinkApiKeyId(secondKey);
|
|
const settings = {
|
|
...defaultSettings(),
|
|
megaCredentials: `first-pool@example.test:${firstMegaSecret}\nsecond-pool@example.test:${secondMegaSecret}`,
|
|
megaDebridApiCredentials: `first-pool@example.test:${firstMegaSecret}\nsecond-pool@example.test:${secondMegaSecret}`,
|
|
megaDebridApiEnabled: true,
|
|
debridLinkApiKeys: `${firstKey}\n${secondKey}`,
|
|
debridAccountStatuses: {
|
|
[secondMegaId]: {
|
|
accountId: secondMegaId,
|
|
provider: "megadebrid" as const,
|
|
label: "Account 2",
|
|
maskedLogin: "se***st",
|
|
valid: false,
|
|
isPremium: false,
|
|
premiumUntilMs: null,
|
|
message: `Rejected ${secondMegaSecret}`,
|
|
checkedAt: 1
|
|
},
|
|
[secondKeyId]: {
|
|
accountId: secondKeyId,
|
|
provider: "debridlink" as const,
|
|
label: "Key 2",
|
|
maskedLogin: "fi***A1",
|
|
valid: false,
|
|
isPremium: false,
|
|
premiumUntilMs: null,
|
|
message: `Rejected ${secondKey}`,
|
|
checkedAt: 1
|
|
}
|
|
}
|
|
};
|
|
|
|
const serialized = JSON.stringify(createRendererState(settings));
|
|
|
|
for (const secret of [firstMegaSecret, secondMegaSecret, firstKey, secondKey]) {
|
|
expect(serialized).not.toContain(secret);
|
|
}
|
|
});
|
|
});
|