fix(settings): accept omitted optional values after upgrades
Restore the v2.0.35 renderer settings validation behavior that was lost when the download path returned to the v2.0.28 baseline. Ignore undefined optional values at top level and inside structured settings while continuing to reject unknown concrete keys and invalid types. Cover a full settings save containing a Real-Debrid account status without the optional email field, plus obsolete optional-field handling.
This commit is contained in:
@@ -30,7 +30,11 @@ function validateJsonValue(value: unknown, depth = 0): void {
|
|||||||
}
|
}
|
||||||
const entries = Object.entries(value as Record<string, unknown>);
|
const entries = Object.entries(value as Record<string, unknown>);
|
||||||
if (entries.length > 10_000) invalid();
|
if (entries.length > 10_000) invalid();
|
||||||
entries.forEach(([, entry]) => validateJsonValue(entry, depth + 1));
|
entries.forEach(([, entry]) => {
|
||||||
|
if (entry !== undefined) {
|
||||||
|
validateJsonValue(entry, depth + 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateTopLevelType(value: unknown, expected: unknown): void {
|
function validateTopLevelType(value: unknown, expected: unknown): void {
|
||||||
@@ -64,6 +68,9 @@ export function validateRendererSettingsUpdate(value: unknown, current: AppSetti
|
|||||||
const input = value as Record<string, unknown>;
|
const input = value as Record<string, unknown>;
|
||||||
const output: Record<string, unknown> = {};
|
const output: Record<string, unknown> = {};
|
||||||
for (const [key, entry] of Object.entries(input)) {
|
for (const [key, entry] of Object.entries(input)) {
|
||||||
|
if (entry === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (DERIVED_KEYS.has(key)) {
|
if (DERIVED_KEYS.has(key)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { describe, expect, it } 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 a complete settings save when an account status has no optional email", () => {
|
||||||
|
const current = defaultSettings();
|
||||||
|
current.debridAccountStatuses = {
|
||||||
|
"svc-realdebrid": {
|
||||||
|
accountId: "svc-realdebrid",
|
||||||
|
provider: "realdebrid",
|
||||||
|
label: "Real-Debrid",
|
||||||
|
maskedLogin: "user",
|
||||||
|
valid: true,
|
||||||
|
isPremium: true,
|
||||||
|
premiumUntilMs: null,
|
||||||
|
message: "Premium aktiv",
|
||||||
|
checkedAt: 1_700_000_000_000
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const rendererSettings = createRendererSettings(current);
|
||||||
|
|
||||||
|
expect(rendererSettings.debridAccountStatuses["svc-realdebrid"]).toHaveProperty("email", undefined);
|
||||||
|
const validated = validateRendererSettingsUpdate(rendererSettings, current);
|
||||||
|
|
||||||
|
expect(validated.debridAccountStatuses).toEqual(rendererSettings.debridAccountStatuses);
|
||||||
|
expect(validated.animatePackageDisclosure).toBe(true);
|
||||||
|
expect(validated).not.toHaveProperty("configuredProviders");
|
||||||
|
expect(validated).not.toHaveProperty("archivePasswordListConfigured");
|
||||||
|
expect(validated).not.toHaveProperty("notifyUrlConfigured");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("ignores omitted optional top-level values but still rejects unknown concrete settings", () => {
|
||||||
|
const current = defaultSettings();
|
||||||
|
|
||||||
|
expect(validateRendererSettingsUpdate({ columnOrderVersion: undefined }, current)).toEqual({});
|
||||||
|
expect(() => validateRendererSettingsUpdate({ obsoleteSetting: true }, current)).toThrow("Settings-Payload ist ungültig");
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user