lsa-planer

LSA-Planer Professional – Planungssoftware für Lichtsignalanlagen nach RiLSA 2015 und § 45 StVO. EUPL-1.2.

/ tests tools pruefsummendatum.test.ts

4,3 KB Rohdatei
tests/tools/pruefsummendatum.test.ts — 95 Zeilen
1 import { copyFileSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
2 import { spawnSync } from 'node:child_process';
3 import os from 'node:os';
4 import path from 'node:path';
5 import { afterAll, beforeAll, describe, expect, it } from 'vitest';
6
7 /*
8 * Befund 67 zur Fassung 5.9.0.
9 *
10 * `scripts/pruefsummen.mjs` schrieb `new Date().toISOString().slice(0, 10)` als
11 * "Erstellt am" in die Pruefsummendatei. `toISOString` liefert Weltzeit, und
12 * `.slice(0, 10)` schneidet das 'Z' ab, das den Wert als Weltzeit auswiese.
13 * Uebrig bleibt ein nackter Kalendertag unter einer deutschen Beschriftung -
14 * nach ISO 8601 als Ortszeit zu lesen. Deutschland liegt oestlich von
15 * Greenwich; zwischen 00:00 und 02:00 Ortszeit (Sommerzeit) beziehungsweise
16 * 00:00 und 01:00 (Winterzeit) traegt die Datei damit den Vortag, waehrend die
17 * Pakete daneben den laufenden Tag nennen. Die Datei ist ausdruecklich als Nachweis gedacht:
18 * "Ein Pruefer muss sagen koennen, mit welchem Programmstand eine Unterlage
19 * erstellt wurde."
20 *
21 * WARUM DER LAUF IN EINEM EIGENEN VERZEICHNIS STATTFINDET: Das Skript ist ein
22 * ausfuehrbares Modul ohne Ausfuhren - es liest `package.json` und `release/`
23 * ueber seinen eigenen Ort und schreibt die Datei dorthin. Geprueft wird
24 * deshalb das Skript selbst, mit einer Nachbildung des Projektordners im
25 * Kratzbereich; der Projektbaum wird nicht angefasst.
26 *
27 * WARUM ZWEI ZEITZONEN: Zu jedem Zeitpunkt weicht mindestens eine der beiden
28 * vom Weltzeit-Tag ab - UTC+14 ab 10:00 UTC, UTC-12 bis 12:00 UTC. Damit faellt
29 * der Fall gegen den Altstand unabhaengig davon, wann er laeuft und in welcher
30 * Zeitzone der Rechner steht.
31 */
32
33 const VERSION = '9.9.9';
34 const PAKETNAME = `LSA-Planer-Setup-${VERSION}.exe`;
35 /** Zeitzonen, deren Kalendertag zu keiner Stunde beide mit dem der Weltzeit uebereinstimmt. */
36 const ZONEN = ['Etc/GMT-14', 'Etc/GMT+12'] as const;
37
38 let wurzel = '';
39
40 /** Kalendertag, den eine Uhr in dieser Zeitzone gerade zeigt. */
41 function tagIn(zone: string): string {
42 return new Intl.DateTimeFormat('en-CA', {
43 timeZone: zone,
44 year: 'numeric',
45 month: '2-digit',
46 day: '2-digit',
47 }).format(new Date());
48 }
49
50 beforeAll(() => {
51 wurzel = mkdtempSync(path.join(os.tmpdir(), 'lsa-pruefsummen-'));
52 mkdirSync(path.join(wurzel, 'scripts'));
53 mkdirSync(path.join(wurzel, 'release'));
54 writeFileSync(path.join(wurzel, 'package.json'), JSON.stringify({ version: VERSION }), 'utf8');
55 writeFileSync(path.join(wurzel, 'release', PAKETNAME), 'Platzhalter statt eines Pakets', 'utf8');
56 copyFileSync('scripts/pruefsummen.mjs', path.join(wurzel, 'scripts', 'pruefsummen.mjs'));
57 });
58
59 afterAll(() => {
60 if (wurzel !== '') rmSync(wurzel, { recursive: true, force: true });
61 });
62
63 /** Laesst das Skript in der angegebenen Zeitzone laufen und gibt die Datei zurueck. */
64 function laufIn(zone: string): string {
65 const lauf = spawnSync(process.execPath, [path.join(wurzel, 'scripts', 'pruefsummen.mjs')], {
66 env: { ...process.env, TZ: zone },
67 encoding: 'utf8',
68 });
69 expect(lauf.status, `Lauf in ${zone}: ${lauf.stderr}`).toBe(0);
70 return readFileSync(path.join(wurzel, 'release', `PRUEFSUMMEN-${VERSION}.txt`), 'utf8');
71 }
72
73 describe('Pruefsummendatei - das Erstellungsdatum', () => {
74 it.each(ZONEN)('nennt in %s den oertlichen Kalendertag, nicht den der Weltzeit', (zone) => {
75 const vorher = tagIn(zone);
76 const datei = laufIn(zone);
77 const nachher = tagIn(zone);
78
79 const treffer = /Erstellt am (\d{4}-\d{2}-\d{2})/.exec(datei);
80 expect(treffer, 'Zeile "Erstellt am" nicht gefunden').not.toBeNull();
81 // Zwei Werte, falls der Lauf ueber Mitternacht dieser Zone hinweg faellt.
82 expect([vorher, nachher], `Erstellungsdatum in ${zone}`).toContain(treffer![1]);
83 });
84
85 it('schreibt die uebrigen Angaben der Nachweisdatei unveraendert', () => {
86 // Gegenprobe: Die Aenderung betrifft nur das Datum.
87 const datei = laufIn('Etc/GMT-14');
88 expect(datei, 'Kopfzeile').toContain(`LSA-Planer Professional ${VERSION} – Prüfsummen`);
89 expect(datei, 'Name des Pakets').toContain(PAKETNAME);
90 // Als Zeichennummer geschrieben, nicht als Zeichen: Die Bytefolgemarke
91 // selbst im Quelltext stehen zu lassen, beanstandet der Linter zu Recht.
92 expect(datei.charCodeAt(0), 'Bytefolgemarke').toBe(0xfeff);
93 expect(datei, 'Hinweis zur Aussagekraft').toContain('Was eine Prüfsumme leistet');
94 });
95 });