93 lines
2.9 KiB
TypeScript
93 lines
2.9 KiB
TypeScript
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||
|
|
|
||
|
|
vi.mock('@/lib/admin/auth', () => ({
|
||
|
|
isAdminEnabled: vi.fn(() => true),
|
||
|
|
isAdminAuthenticated: vi.fn(async () => true),
|
||
|
|
COOKIE_NAME: 'folio_admin_session',
|
||
|
|
}));
|
||
|
|
|
||
|
|
vi.mock('@/lib/admin/yaml-service', () => ({
|
||
|
|
readSettingsYaml: vi.fn(async () => ({
|
||
|
|
sitePassword: 'scrypt:stored:hash',
|
||
|
|
contact: { enabled: true, notifyUrl: 'https://ntfy.sh/stored' },
|
||
|
|
})),
|
||
|
|
readSettingsYamlVersioned: vi.fn(async () => ({
|
||
|
|
data: {
|
||
|
|
sitePassword: 'scrypt:stored:hash',
|
||
|
|
contact: { enabled: true, notifyUrl: 'https://ntfy.sh/stored' },
|
||
|
|
},
|
||
|
|
version: 'v1',
|
||
|
|
})),
|
||
|
|
writeSettingsYaml: vi.fn(async () => undefined),
|
||
|
|
}));
|
||
|
|
|
||
|
|
vi.mock('@/lib/config', () => ({
|
||
|
|
invalidateConfigCache: vi.fn(),
|
||
|
|
getConfigOrNull: vi.fn(() => null),
|
||
|
|
}));
|
||
|
|
|
||
|
|
vi.mock('@/lib/immich', () => ({ immich: { invalidateAll: vi.fn() } }));
|
||
|
|
vi.mock('next/cache', () => ({ revalidatePath: vi.fn() }));
|
||
|
|
|
||
|
|
const env = vi.hoisted(() => ({
|
||
|
|
SITE_PASSWORD: 'env-secret-password' as string | undefined,
|
||
|
|
CONTACT_NOTIFY_URL: 'https://ntfy.sh/env-secret-topic' as string | undefined,
|
||
|
|
SITE_TITLE: 'Env Title',
|
||
|
|
}));
|
||
|
|
vi.mock('@/lib/env', () => ({ env }));
|
||
|
|
|
||
|
|
import { writeSettingsYaml } from '@/lib/admin/yaml-service';
|
||
|
|
import { GET, PUT } from '../settings/route';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* The panel renders these fields locked (#605). The payload carries only the
|
||
|
|
* variable names: the values are a password and an ntfy topic.
|
||
|
|
*/
|
||
|
|
describe('/api/admin/settings env locks (#605)', () => {
|
||
|
|
beforeEach(() => {
|
||
|
|
vi.clearAllMocks();
|
||
|
|
env.SITE_PASSWORD = 'env-secret-password';
|
||
|
|
env.CONTACT_NOTIFY_URL = 'https://ntfy.sh/env-secret-topic';
|
||
|
|
});
|
||
|
|
|
||
|
|
it('GET names the locking variables and never their values', async () => {
|
||
|
|
const res = await GET();
|
||
|
|
const text = await res.text();
|
||
|
|
|
||
|
|
expect(JSON.parse(text).envLocks).toEqual({
|
||
|
|
sitePassword: 'SITE_PASSWORD',
|
||
|
|
'contact.notifyUrl': 'CONTACT_NOTIFY_URL',
|
||
|
|
});
|
||
|
|
expect(text).not.toContain('env-secret-password');
|
||
|
|
expect(text).not.toContain('env-secret-topic');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('GET reports no locks when the variables are unset', async () => {
|
||
|
|
env.SITE_PASSWORD = undefined;
|
||
|
|
env.CONTACT_NOTIFY_URL = undefined;
|
||
|
|
const body = await (await GET()).json();
|
||
|
|
expect(body.envLocks).toEqual({});
|
||
|
|
});
|
||
|
|
|
||
|
|
it('PUT keeps the stored value of a locked field', async () => {
|
||
|
|
const res = await PUT(
|
||
|
|
new Request('http://localhost/api/admin/settings', {
|
||
|
|
method: 'PUT',
|
||
|
|
body: JSON.stringify({
|
||
|
|
settings: {
|
||
|
|
title: 'Folio',
|
||
|
|
sitePassword: 'typed-in-panel',
|
||
|
|
contact: { enabled: true, notifyUrl: 'https://ntfy.sh/typed' },
|
||
|
|
},
|
||
|
|
}),
|
||
|
|
}),
|
||
|
|
);
|
||
|
|
expect(res.status).toBe(200);
|
||
|
|
expect(vi.mocked(writeSettingsYaml).mock.calls[0][0]).toMatchObject({
|
||
|
|
title: 'Folio',
|
||
|
|
sitePassword: 'scrypt:stored:hash',
|
||
|
|
contact: { enabled: true, notifyUrl: 'https://ntfy.sh/stored' },
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|