import { describe, expect, it } from 'vitest'; import { readFileSync } from 'node:fs'; import path from 'node:path'; /** * Befund B5: die Bruecke meldete eine erfundene Version. * * In electron/preload.ts stand acht Fassungen lang * * appVersion: process.env['LSA_APP_VERSION'] ?? '5.0.0', * * Die Umgebungsvariable wird nirgends gesetzt - nicht im Hauptprozess, nicht in * einem npm-Skript, nicht bei electron-builder. Der Rueckfall griff also immer, * und die Bruecke gab noch in 5.7.0 "5.0.0" heraus. Aufgefallen ist das nicht, * weil den Wert kein Aufrufer las: Die Versionsangabe der Statusleiste kommt aus * `__APP_VERSION__`, das vite.config.ts aus package.json einsetzt. * * Deshalb wurde das Feld ENTFERNT und nicht aus `app.getVersion()` gespeist: Ein * zweiter Weg zur selben Angabe ist eine zweite Gelegenheit, eine andere Zahl zu * melden - genau das, was vite.config.ts unter "Programmversion - eine Quelle, * nicht zwei" schon einmal abgestellt hat. * * Diese Pruefung faellt gegen den Stand von 5.7.0: Dort steht die * Umgebungsvariable samt fester Zahl noch in der Datei. * * OFFEN und ausserhalb dieser Datei: src/platform/bridge.ts erklaert in * `DesktopBridge` weiterhin ein `readonly appVersion: string`, und * tests/ui/luftbildKacheln.test.ts fuellt es in seinem Ersatzobjekt. Solange * beides steht, verspricht der Typ ein Feld, das die Bruecke nicht mehr liefert. * Beide Zeilen gehoeren mit weg; hier wird das nicht geprueft, weil diese * Pruefung sonst gegen den heutigen Stand faellt, ohne dass etwas kaputt waere. */ const WURZEL = process.cwd(); function lies(...teile: readonly string[]): string { return readFileSync(path.join(WURZEL, ...teile), 'utf8'); } /** * Kommentare heraus, bevor gesucht wird. * * Der Kopf von preload.ts erklaert in Worten, warum es `appVersion` nicht mehr * gibt - eine Suche ueber den rohen Text fiele genau darueber und meldete das * entfernte Feld als vorhanden. */ function ohneKommentare(quelltext: string): string { return quelltext.replace(/\/\*[\s\S]*?\*\//g, '').replace(/(^|[^:])\/\/.*$/gm, '$1'); } const PRELOAD = lies('electron', 'preload.ts'); const PRELOAD_CODE = ohneKommentare(PRELOAD); describe('Bruecke: Programmversion', () => { it('bietet dem Renderer kein appVersion-Feld an', () => { expect(PRELOAD_CODE).not.toMatch(/\bappVersion\b/); }); it('liest keine Umgebungsvariable LSA_APP_VERSION', () => { expect(PRELOAD_CODE).not.toMatch(/LSA_APP_VERSION/); }); it('trägt überhaupt keine fest eingetragene Versionsnummer', () => { // Auch nicht als Rueckfall: Eine feste Zahl im Quelltext wird still falsch, // sobald package.json weiterzaehlt - und niemand merkt es. expect(PRELOAD_CODE).not.toMatch(/['"]\d+\.\d+\.\d+/); }); it('setzt die Umgebungsvariable auch sonst nirgends im Quellstand', () => { // Sie war nie gesetzt - das war der Kern des Befundes. Wer sie wieder // einfuehrt, soll hier stolpern und stattdessen app.getVersion() nehmen. for (const datei of [ ['package.json'], ['vite.config.ts'], ['electron', 'main.ts'], ['electron', 'preload.ts'], ['src', 'main.ts'], ]) { // Ohne Kommentare: preload.ts nennt den Namen im Kopf, um zu erklaeren, // warum es ihn nicht mehr auswertet. Gesucht ist Quelltext, nicht Prosa. expect( ohneKommentare(lies(...datei)), `${datei.join('/')} nennt LSA_APP_VERSION`, ).not.toMatch(/LSA_APP_VERSION/); } }); }); describe('Programmversion: eine Quelle', () => { it('kommt aus package.json und wird von vite als __APP_VERSION__ eingesetzt', () => { const vite = lies('vite.config.ts'); expect(vite).toMatch(/readFileSync\([^)]*package\.json/); expect(vite).toMatch(/__APP_VERSION__:\s*JSON\.stringify\(VERSION\)/); }); it('wird im Renderer aus genau dieser Konstanten genommen', () => { /* * NACHGEZOGEN AM 10.09.2026 (Fassung 5.26.0): Die Aufloesung stand bis * dahin in src/main.ts, weil allein das "Ueber"-Fenster sie brauchte. Seit * die gedruckte Planunterlage die Fassung nennt, braucht sie ein zweiter * Aufrufer, und zwei Aufloesungen waeren zwei Stellen fuer dieselbe Zahl. * Sie steht jetzt in src/fassung.ts; main.ts holt sie von dort. */ const fassung = ohneKommentare(lies('src', 'fassung.ts')); expect(fassung).toMatch(/APP_VERSION =[^;]*__APP_VERSION__/); const rendererCode = ohneKommentare(lies('src', 'main.ts')); expect(rendererCode, 'main.ts loest die Nummer nicht mehr selbst auf').not.toMatch( /const APP_VERSION = [^\n]*__APP_VERSION__/, ); expect(rendererCode).toMatch(/import \{ APP_VERSION \} from '\.\/fassung'/); // Die Statusleiste bekommt sie ueber die Shell - nicht ueber die Bruecke. expect(rendererCode).toMatch(/new Shell\([^)]*APP_VERSION/); }); it('nimmt die Planunterlage sie aus derselben Stelle', () => { /* * Die eigentliche Zusage dieser Aenderung: Die gedruckte Unterlage nennt die * Fassung, und zwar dieselbe, die im "Ueber"-Fenster steht. Ein zweiter * Rueckfallwert im Ausdruck waere genau der Widerspruch, den diese Datei * verhindern soll. */ const ausdruck = ohneKommentare(lies('src', 'services', 'export', 'pdf.ts')); expect(ausdruck).toMatch(/import \{ FASSUNGSANGABE \} from '@\/fassung'/); expect(ausdruck, 'die Zeile auf dem Deckblatt').toMatch(/'Programmfassung', FASSUNGSANGABE/); expect(ausdruck, 'kein eigener Rueckfall im Ausdruck').not.toMatch(/__APP_VERSION__/); }); it('steht in package.json als vollständige Versionsnummer', () => { const paket = JSON.parse(lies('package.json')) as { version?: unknown }; expect(typeof paket.version).toBe('string'); expect(paket.version).toMatch(/^\d+\.\d+\.\d+/); }); });