lsa-planer
LSA-Planer Professional – Planungssoftware für Lichtsignalanlagen nach RiLSA 2015 und § 45 StVO. EUPL-1.2.
/ tests tools aufraeumen.test.ts
| 1 | import { |
| 2 | copyFileSync, |
| 3 | existsSync, |
| 4 | mkdirSync, |
| 5 | mkdtempSync, |
| 6 | readFileSync, |
| 7 | rmSync, |
| 8 | writeFileSync, |
| 9 | } from 'node:fs'; |
| 10 | import { spawnSync } from 'node:child_process'; |
| 11 | import os from 'node:os'; |
| 12 | import path from 'node:path'; |
| 13 | import { afterEach, beforeEach, describe, expect, it } from 'vitest'; |
| 14 | |
| 15 | /* |
| 16 | * Befund 13 zur Fassung 5.10.0. |
| 17 | * |
| 18 | * `npm run clean` entfernte neben dem jederzeit neu baubaren `build/` auch |
| 19 | * `release/` - rekursiv, mit force, ohne Rueckfrage und ohne Ausnahme fuer die |
| 20 | * Dateien PRUEFSUMMEN-<Fassung>.txt. |
| 21 | * |
| 22 | * `release/` steht in .gitignore und ist damit die einzige Kopie. Die |
| 23 | * Pruefsummendateien sind ausdruecklich als Nachweis gedacht: "Ein Pruefer muss |
| 24 | * sagen koennen, mit welchem Programmstand eine Unterlage erstellt wurde" |
| 25 | * (scripts/pruefsummen.mjs). Wiederherstellbar sind sie nicht: Ein Neubau aus |
| 26 | * derselben Marke ergibt zwar denselben Quelltext, aber eine andere Pruefsumme. |
| 27 | * Und nach dem Loeschen kann `npm run pruefsummen` gar nichts mehr schreiben: |
| 28 | * Es braucht das Paket, an das es die Summe haengt. |
| 29 | * |
| 30 | * Die beiden Skripte widersprachen sich damit: pruefsummen.mjs behandelt |
| 31 | * `release/` als Archiv ("Im Ordner liegen erfahrungsgemaess auch aeltere |
| 32 | * Staende"), clean.mjs behandelte denselben Ordner als Zwischenstand. |
| 33 | * |
| 34 | * WARUM DER LAUF IN EINEM EIGENEN VERZEICHNIS STATTFINDET: Das Skript ist ein |
| 35 | * ausfuehrbares Modul ohne Ausfuhren und bildet seine Ziele aus dem eigenen Ort |
| 36 | * (`new URL('../build', import.meta.url)`). Geprueft wird deshalb das Skript |
| 37 | * selbst, mit einer Nachbildung des Projektordners im Kratzbereich - der |
| 38 | * Projektbaum und sein echtes `release/` werden nicht angefasst. |
| 39 | * |
| 40 | * WARUM DER AUFRUF MIT DOPPELTEM STRICH GENANNT SEIN MUSS: Der dokumentierte |
| 41 | * Einstiegspunkt ist `npm run clean` (package.json). npm nimmt ein |
| 42 | * unbekanntes Argument als eigene Einstellung und reicht es nicht weiter: |
| 43 | * `npm run clean --auch-release` erreicht das Skript nicht und laesst |
| 44 | * `release/` vollstaendig liegen, erst `npm run clean -- --auch-release` wirkt. |
| 45 | * Ausgabe und Kopfkommentar muessen deshalb den Aufruf nennen, der wirkt - |
| 46 | * sonst tippt den Befehl zweimal vergeblich, wer der eigenen Ausgabe folgt. |
| 47 | */ |
| 48 | |
| 49 | /** Der wirksame Aufruf ueber den dokumentierten Einstiegspunkt. */ |
| 50 | const AUFRUF = 'npm run clean -- --auch-release'; |
| 51 | |
| 52 | const PRUEFSUMMEN = 'PRUEFSUMMEN-9.9.9.txt'; |
| 53 | const PAKET = 'LSA-Planer-Setup-9.9.9.exe'; |
| 54 | |
| 55 | let wurzel = ''; |
| 56 | |
| 57 | beforeEach(() => { |
| 58 | wurzel = mkdtempSync(path.join(os.tmpdir(), 'lsa-clean-')); |
| 59 | mkdirSync(path.join(wurzel, 'scripts')); |
| 60 | mkdirSync(path.join(wurzel, 'build', 'assets'), { recursive: true }); |
| 61 | mkdirSync(path.join(wurzel, 'release')); |
| 62 | writeFileSync(path.join(wurzel, 'build', 'index.html'), 'Platzhalter statt eines Baus', 'utf8'); |
| 63 | writeFileSync(path.join(wurzel, 'build', 'assets', 'haupt.js'), 'Platzhalter', 'utf8'); |
| 64 | writeFileSync(path.join(wurzel, 'release', PAKET), 'Platzhalter statt eines Pakets', 'utf8'); |
| 65 | writeFileSync(path.join(wurzel, 'release', PRUEFSUMMEN), 'Platzhalter statt Summen', 'utf8'); |
| 66 | copyFileSync('scripts/clean.mjs', path.join(wurzel, 'scripts', 'clean.mjs')); |
| 67 | }); |
| 68 | |
| 69 | afterEach(() => { |
| 70 | if (wurzel !== '') rmSync(wurzel, { recursive: true, force: true }); |
| 71 | wurzel = ''; |
| 72 | }); |
| 73 | |
| 74 | /** Laesst das Skript in der Nachbildung laufen und gibt seine Ausgabe zurueck. */ |
| 75 | function lauf(...argumente: string[]): string { |
| 76 | const ergebnis = spawnSync( |
| 77 | process.execPath, |
| 78 | [path.join(wurzel, 'scripts', 'clean.mjs'), ...argumente], |
| 79 | { encoding: 'utf8' }, |
| 80 | ); |
| 81 | expect(ergebnis.status, `Lauf mit [${argumente.join(' ')}]: ${ergebnis.stderr}`).toBe(0); |
| 82 | return ergebnis.stdout; |
| 83 | } |
| 84 | |
| 85 | /** Liegt die Datei noch im nachgebildeten release-Ordner? */ |
| 86 | function imArchiv(name: string): boolean { |
| 87 | return existsSync(path.join(wurzel, 'release', name)); |
| 88 | } |
| 89 | |
| 90 | describe('npm run clean - was es entfernen darf', () => { |
| 91 | it('entfernt den Bauordner', () => { |
| 92 | lauf(); |
| 93 | expect(existsSync(path.join(wurzel, 'build')), 'build/ nach dem Lauf').toBe(false); |
| 94 | }); |
| 95 | |
| 96 | it('laesst den Auslieferungsordner ohne ausdrueckliche Ansage unberuehrt', () => { |
| 97 | lauf(); |
| 98 | expect(imArchiv(PAKET), 'Paket nach dem Lauf').toBe(true); |
| 99 | expect(imArchiv(PRUEFSUMMEN), 'Pruefsummendatei nach dem Lauf').toBe(true); |
| 100 | }); |
| 101 | |
| 102 | it('sagt in der Ausgabe, dass der Auslieferungsordner liegen bleibt', () => { |
| 103 | // Wer aufraeumen wollte, soll nicht raten muessen, was liegen blieb - und |
| 104 | // erfahren, wie er ihn doch loswird. Genannt sein muss der Aufruf, der |
| 105 | // wirklich wirkt (siehe Kopf dieser Datei), nicht nur der blosse Schalter. |
| 106 | const ausgabe = lauf(); |
| 107 | expect(ausgabe, 'Ausgabe des Laufs').toContain('behalten: release'); |
| 108 | expect(ausgabe, 'Hinweis auf den wirksamen Aufruf').toContain(AUFRUF); |
| 109 | }); |
| 110 | |
| 111 | it('nennt im Kopfkommentar denselben Aufruf wie in der Ausgabe', () => { |
| 112 | // Zusagen in Kommentaren sind Teil des Programms: Der Kopf darf nicht den |
| 113 | // Aufruf nennen, der ueber `npm run` verpufft. |
| 114 | const kopf = readFileSync('scripts/clean.mjs', 'utf8').replace(/\s+/g, ' '); |
| 115 | expect(kopf, 'Kopfkommentar von scripts/clean.mjs').toContain(AUFRUF); |
| 116 | }); |
| 117 | |
| 118 | it('entfernt mit --auch-release die Pakete, aber nie die Pruefsummen', () => { |
| 119 | lauf('--auch-release'); |
| 120 | expect(imArchiv(PAKET), 'Paket nach dem Lauf mit --auch-release').toBe(false); |
| 121 | expect(imArchiv(PRUEFSUMMEN), 'Pruefsummendatei nach dem Lauf mit --auch-release').toBe(true); |
| 122 | }); |
| 123 | |
| 124 | it('kommt ohne vorhandenen Auslieferungsordner zurecht', () => { |
| 125 | // Ein frischer Klon hat kein release/ - der Lauf darf daran nicht brechen. |
| 126 | rmSync(path.join(wurzel, 'release'), { recursive: true, force: true }); |
| 127 | lauf('--auch-release'); |
| 128 | expect(existsSync(path.join(wurzel, 'build')), 'build/ nach dem Lauf').toBe(false); |
| 129 | }); |
| 130 | }); |