/** * app-config.test — endpoint + initial-admin resolution from the generated app's * config files (launchSettings / vite.config / appsettings cascade). */ import { describe, it, expect, afterEach } from 'vitest'; import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { apiUrlFromLaunchSettings, DEFAULT_ADMIN_EMAIL, pickHttpUrl, portFromViteConfig, resolveApiUrl, resolveFrontendUrl, resolveInitialAdmin, } from '../app-config.js'; const tmpDirs: string[] = []; const makeTmp = (): string => { const dir = mkdtempSync(join(tmpdir(), 'uat-appcfg-')); tmpDirs.push(dir); return dir; }; afterEach(() => { while (tmpDirs.length) rmSync(tmpDirs.pop()!, { recursive: true, force: true }); }); describe('pure parsers', () => { it('pickHttpUrl prefers http:// among ;-separated urls', () => { expect(pickHttpUrl('https://localhost:7055;http://localhost:5142')).toBe('http://localhost:5142'); expect(pickHttpUrl('https://only:1')).toBe('https://only:1'); expect(pickHttpUrl('')).toBeNull(); }); it('apiUrlFromLaunchSettings reads the first profile applicationUrl', () => { expect( apiUrlFromLaunchSettings({ profiles: { http: { applicationUrl: 'http://localhost:5142/' }, https: { applicationUrl: 'https://x' } }, }), ).toBe('http://localhost:5142'); expect(apiUrlFromLaunchSettings({})).toBeNull(); }); it('portFromViteConfig extracts the dev port', () => { expect(portFromViteConfig('export default { server: { port: 5199 } }')).toBe(5199); expect(portFromViteConfig('no port here')).toBeNull(); }); }); describe('resolveApiUrl / resolveFrontendUrl', () => { it('spec override wins; file second; default last', () => { const apiDir = makeTmp(); expect(resolveApiUrl(apiDir, 'http://spec:1/')).toEqual({ value: 'http://spec:1', source: 'spec' }); expect(resolveApiUrl(apiDir)).toEqual({ value: 'http://localhost:5142', source: 'default' }); mkdirSync(join(apiDir, 'Properties'), { recursive: true }); writeFileSync( join(apiDir, 'Properties', 'launchSettings.json'), JSON.stringify({ profiles: { http: { applicationUrl: 'http://localhost:7030' } } }), ); expect(resolveApiUrl(apiDir)).toEqual({ value: 'http://localhost:7030', source: 'Properties/launchSettings.json' }); }); it('frontend resolves vite.config port then defaults', () => { const webDir = makeTmp(); expect(resolveFrontendUrl(webDir).value).toBe('http://localhost:5173'); writeFileSync(join(webDir, 'vite.config.ts'), 'export default { server: { port: 9030 } }'); expect(resolveFrontendUrl(webDir)).toEqual({ value: 'http://localhost:9030', source: 'vite.config.ts' }); }); }); describe('resolveInitialAdmin', () => { it('cascades Local > Development > base and falls back to the seeded email', () => { const apiDir = makeTmp(); writeFileSync( join(apiDir, 'appsettings.json'), JSON.stringify({ Security: { InitialAdmin: { Email: 'base@x', Password: 'base-pw' } } }), ); writeFileSync( join(apiDir, 'appsettings.Local.json'), JSON.stringify({ Security: { InitialAdmin: { Password: 'local-pw' } } }), ); const admin = resolveInitialAdmin(apiDir); expect(admin.value.password).toBe('local-pw'); // Local wins for the password expect(admin.value.email).toBe('base@x'); // email cascades down to base expect(admin.source).toContain('appsettings.Local.json'); }); it('returns the default seeded email and a null password when nothing is configured', () => { const admin = resolveInitialAdmin(makeTmp()); expect(admin.value.email).toBe(DEFAULT_ADMIN_EMAIL); expect(admin.value.password).toBeNull(); }); it('spec overrides take precedence wholesale', () => { const admin = resolveInitialAdmin(makeTmp(), { email: 'o@x', password: 'opw' }); expect(admin).toEqual({ value: { email: 'o@x', password: 'opw' }, source: 'spec' }); }); });