Files
SnowLuma/packages/core/tests/webui-system-settings.test.ts
TanTan fa9c73363d
Dev Build / cleanup (push) Waiting to run
Dev Build / typecheck (push) Waiting to run
Publish MCP to npm / Build, test, and publish MCP (push) Waiting to run
Publish SDK to npm / Build, test, and publish SDK (push) Waiting to run
Sync action catalog to docs / sync (push) Waiting to run
Publish UI to npm / Build and publish UI (push) Waiting to run
Build and Release / preflight (push) Waiting to run
SnowLuma v2.4.2 source snapshot
2026-11-13 09:02:00 +08:00

117 lines
3.9 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import {
coerceSettingsPatch,
evaluateSettingsSave,
tlsCertDeletionBlocked,
} from '../src/webui/system-settings';
import type { RuntimeConfig } from '@snowluma/common/runtime';
describe('coerceSettingsPatch', () => {
it('accepts a full valid body and maps tlsEnabled → webuiTls.enabled', () => {
const r = coerceSettingsPatch({
webuiPort: 8080,
webuiHost: '127.0.0.1',
tlsEnabled: true,
trustProxy: '1',
});
expect(r).toEqual({
ok: true,
patch: {
webuiPort: 8080,
webuiHost: '127.0.0.1',
webuiTls: { enabled: true },
trustProxy: '1',
},
});
});
it('only includes provided keys (partial patch)', () => {
const r = coerceSettingsPatch({ webuiHost: '0.0.0.0' });
expect(r).toEqual({ ok: true, patch: { webuiHost: '0.0.0.0' } });
});
it('rejects a non-object body', () => {
expect(coerceSettingsPatch(null).ok).toBe(false);
expect(coerceSettingsPatch('x').ok).toBe(false);
});
it('rejects an out-of-range or non-integer port', () => {
expect(coerceSettingsPatch({ webuiPort: 0 }).ok).toBe(false);
expect(coerceSettingsPatch({ webuiPort: 70000 }).ok).toBe(false);
expect(coerceSettingsPatch({ webuiPort: 12.5 }).ok).toBe(false);
});
it('rejects an empty/blank host', () => {
expect(coerceSettingsPatch({ webuiHost: ' ' }).ok).toBe(false);
expect(coerceSettingsPatch({ webuiHost: 123 }).ok).toBe(false);
});
it('rejects a non-boolean tlsEnabled and non-string trustProxy', () => {
expect(coerceSettingsPatch({ tlsEnabled: 'yes' }).ok).toBe(false);
expect(coerceSettingsPatch({ trustProxy: 1 }).ok).toBe(false);
});
it('accepts an empty-string trustProxy (trust nobody)', () => {
expect(coerceSettingsPatch({ trustProxy: '' })).toEqual({
ok: true,
patch: { trustProxy: '' },
});
});
});
describe('evaluateSettingsSave', () => {
const current: RuntimeConfig = {
webuiPort: 5099,
webuiHost: '127.0.0.1',
webuiTls: { enabled: false },
};
it('rejects an invalid bind host on the patch', () => {
const r = evaluateSettingsSave(current, { webuiHost: 'not_a_host' }, true);
expect(r.ok).toBe(false);
if (!r.ok) {
expect(r.error).toMatch(/TCP/);
}
});
it('rejects saving an unrelated field when the persisted host is invalid', () => {
const r = evaluateSettingsSave(
{ ...current, webuiHost: 'not_a_host' },
{ webuiPort: 8080 },
true,
);
expect(r.ok).toBe(false);
});
it('rejects enabling TLS without a usable certificate pair', () => {
const r = evaluateSettingsSave(current, { webuiTls: { enabled: true } }, false);
expect(r).toEqual({ ok: false, error: '启用 TLS 前请先上传有效的证书与私钥' });
});
it('rejects leaving TLS enabled when the pair is already missing', () => {
const r = evaluateSettingsSave(
{ ...current, webuiTls: { enabled: true } },
{ webuiPort: 8080 },
false,
);
expect(r.ok).toBe(false);
});
it('allows disabling TLS even without a pair', () => {
const r = evaluateSettingsSave(
{ ...current, webuiTls: { enabled: true } },
{ webuiTls: { enabled: false } },
false,
);
expect(r).toEqual({ ok: true, patch: { webuiTls: { enabled: false } } });
});
});
describe('tlsCertDeletionBlocked', () => {
it('blocks deletion while TLS is enabled and allows it when TLS is off', () => {
expect(tlsCertDeletionBlocked(true)).toBe(true);
expect(tlsCertDeletionBlocked(false)).toBe(false);
});
});