import { afterEach, describe, expect, it, vi } from 'vitest'; /** * app-version.test — Versionsstempel der Web-Oberflaeche (quick-260914-ku1). * * `appVersion` wird beim Laden des Moduls aus `NEXT_PUBLIC_APP_*` gelesen und * `loadApiVersion()` memoisiert die Antwort von `GET /health/version` — * deshalb setzt jeder Test die Module zurueck und importiert dynamisch, * nachdem Umgebung und `fetch` gestubbt sind (Muster: module-access-gate.test.tsx). */ afterEach(() => { vi.unstubAllEnvs(); vi.unstubAllGlobals(); }); async function importFresh() { vi.resetModules(); return import('./app-version'); } describe('app-version (quick-260914-ku1)', () => { it('Test 1 (Vorgaben): ohne NEXT_PUBLIC_APP_* ist appVersion dev/dev ohne Commit', async () => { vi.stubEnv('NEXT_PUBLIC_APP_VERSION', undefined); vi.stubEnv('NEXT_PUBLIC_APP_CHANNEL', undefined); vi.stubEnv('NEXT_PUBLIC_APP_COMMIT', undefined); const mod = await importFresh(); expect(mod.appVersion).toEqual({ version: 'dev', channel: 'dev', commit: '' }); }); it('Test 2 (Umgebung): NEXT_PUBLIC_APP_VERSION/_CHANNEL/_COMMIT werden woertlich durchgereicht', async () => { vi.stubEnv('NEXT_PUBLIC_APP_VERSION', 'v1.2.3'); vi.stubEnv('NEXT_PUBLIC_APP_CHANNEL', 'beta'); vi.stubEnv('NEXT_PUBLIC_APP_COMMIT', 'abc1234'); const mod = await importFresh(); expect(mod.appVersion).toEqual({ version: 'v1.2.3', channel: 'beta', commit: 'abc1234' }); }); it('Test 3 (Normalisierung): ein unbekannter Kanal wird zu dev', async () => { vi.stubEnv('NEXT_PUBLIC_APP_VERSION', 'v1.2.3'); vi.stubEnv('NEXT_PUBLIC_APP_CHANNEL', 'gamma'); vi.stubEnv('NEXT_PUBLIC_APP_COMMIT', 'abc1234'); const mod = await importFresh(); expect(mod.appVersion.channel).toBe('dev'); }); it('Test 4 (Laden, memoisiert): zwei Aufrufe liefern das Objekt, fetch laeuft genau einmal mit Cookie', async () => { const payload = { name: 'tessera', version: 'v1.2.3', channel: 'beta', commit: 'abc1234', buildTime: 'x', }; const fetchMock = vi.fn(() => Promise.resolve({ ok: true, json: () => Promise.resolve(payload) }), ); vi.stubGlobal('fetch', fetchMock); const mod = await importFresh(); const first = await mod.loadApiVersion(); const second = await mod.loadApiVersion(); expect(first).toEqual(payload); expect(second).toEqual(payload); expect(fetchMock).toHaveBeenCalledTimes(1); const [url, options] = fetchMock.mock.calls[0] as unknown as [string, RequestInit]; expect(url.endsWith('/health/version')).toBe(true); expect(options).toMatchObject({ credentials: 'include' }); }); it('Test 5 (still bei Fehler): Netzfehler und ok=false liefern null, nichts wird geworfen', async () => { vi.stubGlobal('fetch', vi.fn(() => Promise.reject(new Error('netz')))); const rejected = await importFresh(); await expect(rejected.loadApiVersion()).resolves.toBeNull(); vi.stubGlobal( 'fetch', vi.fn(() => Promise.resolve({ ok: false, json: () => Promise.resolve({}) })), ); const notOk = await importFresh(); await expect(notOk.loadApiVersion()).resolves.toBeNull(); }); });