import { describe, expect, it } from 'vitest' import { buildApp } from '../src/app.js' import { loadConfig } from '../src/config.js' describe('app-switcher API', () => { it('GET /api/v1/app-switcher is public and returns defaults', async () => { const config = loadConfig({ ...process.env, JWT_SECRET: 'test-secret-at-least-8', ADMIN_PASSWORD: 'admin', DATABASE_URL: 'sqlite::memory:', NODE_ENV: 'test', }) const app = await buildApp({ config, databaseUrl: 'sqlite::memory:' }) const res = await app.inject({ method: 'GET', url: '/api/v1/app-switcher' }) expect(res.statusCode).toBe(200) const body = res.json() as { menuLabel: string; apps: { id: string }[] } expect(body.menuLabel).toBeTruthy() expect(body.apps.map((a) => a.id).sort()).toEqual([ 'bgp', 'cdn', 'cfdm', 'dns', 'fw', 'mm', 'vps', ]) expect(body.apps.find((a) => a.id === 'cdn')).toMatchObject({ authMode: 'jwt', }) expect(body.apps.find((a) => a.id === 'mm')).toMatchObject({ authMode: 'jwt', }) expect(body.apps.find((a) => a.id === 'dns')).toMatchObject({ authMode: 'oidc', }) await app.close() }) it('PUT /api/v1/admin/app-switcher requires admin and persists', async () => { const config = loadConfig({ ...process.env, JWT_SECRET: 'test-secret-at-least-8', ADMIN_EMAIL: 'admin@test.local', ADMIN_PASSWORD: 'adminpass', DATABASE_URL: 'sqlite::memory:', NODE_ENV: 'test', }) const app = await buildApp({ config, databaseUrl: 'sqlite::memory:' }) const denied = await app.inject({ method: 'PUT', url: '/api/v1/admin/app-switcher', payload: { menuLabel: 'Apps', apps: [] }, }) expect(denied.statusCode).toBe(401) const login = await app.inject({ method: 'POST', url: '/api/v1/auth/login', payload: { email: 'admin@test.local', password: 'adminpass' }, }) expect(login.statusCode).toBe(200) const token = (login.json() as { access_token: string }).access_token const getBefore = await app.inject({ method: 'GET', url: '/api/v1/app-switcher', }) const before = getBefore.json() as { menuLabel: string apps: { id: string name: string url: string icon: string enabled: boolean }[] } const updated = { menuLabel: 'Сервисы', apps: before.apps.map((a) => a.id === 'cfdm' ? { ...a, url: 'https://cfdm.example.test', name: 'CFDM Test' } : a, ), } const put = await app.inject({ method: 'PUT', url: '/api/v1/admin/app-switcher', headers: { authorization: `Bearer ${token}` }, payload: updated, }) expect(put.statusCode).toBe(200) expect(put.json()).toMatchObject({ menuLabel: 'Сервисы' }) const getAfter = await app.inject({ method: 'GET', url: '/api/v1/app-switcher', }) const after = getAfter.json() as typeof before expect(after.menuLabel).toBe('Сервисы') expect(after.apps.find((a) => a.id === 'cfdm')?.url).toBe( 'https://cfdm.example.test', ) await app.close() }) })