Files
MajoorWaldi-ComfyUI-Majoor-…/ui/tests/settings_security.vitest.ts
T

226 lines
8.0 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
const saveMajoorSettings = vi.fn();
const applySettingsToConfig = vi.fn();
const syncBackendSecuritySettings = vi.fn();
const setSecuritySettings = vi.fn();
const setRuntimeSecurityToken = vi.fn();
const comfyToast = vi.fn();
const t = vi.fn((key, fallback, vars) => {
if (!fallback) return key;
if (!vars || typeof vars !== "object") return fallback;
return String(fallback).replace(/\{(\w+)\}/g, (_m, name) => String(vars[name] ?? `{${name}}`));
});
vi.mock("../app/i18n.js", () => ({
t,
}));
vi.mock("../app/toast.js", () => ({
comfyToast,
}));
vi.mock("../api/client.js", () => ({
setSecuritySettings,
setRuntimeSecurityToken,
}));
vi.mock("../app/settings/settingsCore.js", () => ({
saveMajoorSettings,
applySettingsToConfig,
syncBackendSecuritySettings,
}));
describe("settingsSecurity recommended remote LAN preset", () => {
beforeEach(() => {
vi.resetModules();
vi.unstubAllGlobals();
vi.clearAllMocks();
globalThis.location = {
protocol: "http:",
hostname: "192.168.1.10",
};
vi.stubGlobal("crypto", {
getRandomValues: vi.fn((buffer) => {
for (let index = 0; index < buffer.length; index += 1) {
buffer[index] = (index * 17 + 23) & 0xff;
}
return buffer;
}),
});
setSecuritySettings.mockResolvedValue({ ok: true, data: { prefs: {} } });
});
it("builds the recommended preset with a generated token on HTTP LAN sessions", async () => {
const mod = await import("../app/settings/settingsSecurity.js");
const patch = mod.buildRecommendedRemoteLanSecuritySettings({});
expect(patch.allowWrite).toBe(true);
expect(patch.requireAuth).toBe(true);
expect(patch.allowRemoteWrite).toBe(false);
expect(patch.allowInsecureTokenTransport).toBe(true);
expect(String(patch.apiToken)).toMatch(/^mjr_[0-9a-f]+$/);
expect(String(patch.apiToken)).toHaveLength(40);
});
it("rejects token generation when secure randomness is unavailable", async () => {
vi.stubGlobal("crypto", undefined);
const mod = await import("../app/settings/settingsSecurity.js");
expect(() => mod.generateRecommendedApiToken()).toThrow(
"Secure token generation requires crypto.getRandomValues().",
);
});
it("applies the preset through the settings toggle and syncs backend security", async () => {
const mod = await import("../app/settings/settingsSecurity.js");
const defs = [];
const settings = {
safety: {},
security: {
allowWrite: false,
requireAuth: false,
allowRemoteWrite: false,
allowInsecureTokenTransport: false,
},
};
const notifyApplied = vi.fn();
mod.registerSecuritySettings((def) => defs.push(def), settings, notifyApplied);
const presetDef = defs.find((def) => def.id === "Majoor.Security.RemoteLanPreset");
expect(presetDef).toBeTruthy();
presetDef.onChange(true);
await Promise.resolve();
await Promise.resolve();
expect(saveMajoorSettings).toHaveBeenCalled();
expect(setRuntimeSecurityToken).toHaveBeenCalledTimes(1);
expect(setSecuritySettings).toHaveBeenCalledTimes(1);
expect(setSecuritySettings).toHaveBeenCalledWith(
expect.objectContaining({
allow_write: true,
require_auth: true,
allow_remote_write: false,
allow_insecure_token_transport: true,
}),
);
expect(String(settings.security.apiToken)).toMatch(/^mjr_[0-9a-f]+$/);
expect(notifyApplied).toHaveBeenCalledWith("security.remoteLanPreset");
expect(notifyApplied).toHaveBeenCalledWith("security.apiToken");
expect(syncBackendSecuritySettings).toHaveBeenCalledTimes(1);
expect(comfyToast).toHaveBeenCalledWith(
"Recommended remote LAN setup applied. This browser session is now authorized for Majoor write operations.",
"success",
);
});
it("persists a manually entered API token into the runtime auth session", async () => {
const mod = await import("../app/settings/settingsSecurity.js");
const defs = [];
const settings = {
safety: {},
security: {},
};
mod.registerSecuritySettings((def) => defs.push(def), settings, vi.fn());
const tokenDef = defs.find((def) => def.id === "Majoor.Security.ApiToken");
expect(tokenDef).toBeTruthy();
tokenDef.onChange("mjr_manual_token_1234567890");
await Promise.resolve();
expect(setRuntimeSecurityToken).toHaveBeenCalledWith("mjr_manual_token_1234567890");
expect(setSecuritySettings).toHaveBeenCalledWith({
api_token: "mjr_manual_token_1234567890",
});
});
it("does not push unchanged security toggle values during initial settings hydration", async () => {
const mod = await import("../app/settings/settingsSecurity.js");
const defs = [];
const settings = {
safety: {
confirmDeletion: true,
},
security: {
allowWrite: true,
requireAuth: true,
allowRemoteWrite: false,
allowInsecureTokenTransport: false,
},
};
mod.registerSecuritySettings((def) => defs.push(def), settings, vi.fn());
const allowWriteDef = defs.find((def) => def.id === "Majoor.Security.allowWrite");
expect(allowWriteDef).toBeTruthy();
allowWriteDef.onChange(true);
await Promise.resolve();
expect(saveMajoorSettings).not.toHaveBeenCalled();
expect(setSecuritySettings).not.toHaveBeenCalled();
expect(syncBackendSecuritySettings).not.toHaveBeenCalled();
});
it("uses permissive local defaults except safe mode when security settings are missing", async () => {
const mod = await import("../app/settings/settingsSecurity.js");
const defs = [];
const settings = {
safety: {},
security: {},
};
mod.registerSecuritySettings((def) => defs.push(def), settings, vi.fn());
expect(defs.find((def) => def.id === "Majoor.Safety.ConfirmDeletion")?.defaultValue).toBe(
true,
);
expect(defs.find((def) => def.id === "Majoor.Security.safeMode")?.defaultValue).toBe(
false,
);
expect(defs.find((def) => def.id === "Majoor.Security.allowWrite")?.defaultValue).toBe(
true,
);
expect(defs.find((def) => def.id === "Majoor.Security.allowDelete")?.defaultValue).toBe(
true,
);
expect(defs.find((def) => def.id === "Majoor.Security.allowRename")?.defaultValue).toBe(
true,
);
expect(
defs.find((def) => def.id === "Majoor.Security.allowOpenInFolder")?.defaultValue,
).toBe(true);
expect(defs.find((def) => def.id === "Majoor.Security.allowResetIndex")?.defaultValue).toBe(
true,
);
});
it("does not push an unchanged API token during initial settings hydration", async () => {
const mod = await import("../app/settings/settingsSecurity.js");
const defs = [];
const settings = {
safety: {},
security: {
apiToken: "mjr_same_token_1234567890",
},
};
mod.registerSecuritySettings((def) => defs.push(def), settings, vi.fn());
const tokenDef = defs.find((def) => def.id === "Majoor.Security.ApiToken");
expect(tokenDef).toBeTruthy();
tokenDef.onChange("mjr_same_token_1234567890");
await Promise.resolve();
expect(saveMajoorSettings).not.toHaveBeenCalled();
expect(setRuntimeSecurityToken).not.toHaveBeenCalled();
expect(setSecuritySettings).not.toHaveBeenCalled();
});
});