waffensachkunde

Waffensachkunde – Lernsoftware für die Sachkundeprüfung nach § 7 WaffG. Barrierefrei, offline, EUPL-1.2.

/ app tests drittlizenzen.test.ts

4,4 KB Rohdatei
app/tests/drittlizenzen.test.ts — 123 Zeilen
1 // @vitest-environment node
2 /**
3 * Was ausgeliefert wird, muss auch genannt werden.
4 *
5 * MIT, BSD, Apache und ISC verlangen sämtlich, dass Lizenztext und
6 * Urheberrechtsvermerk mitgeliefert werden. Die Anzeige unter „Über diese
7 * Software" speist sich aus `content/drittlizenzen.json`, erzeugt von
8 * `tools/lizenzen_erzeugen.py`.
9 *
10 * Diese Liste war einmal fest verdrahtet und lief prompt auseinander:
11 * better-sqlite3 zieht node-addon-api nach, das mit ausgeliefert wurde, in der
12 * Anzeige aber fehlte. Der Test vergleicht deshalb bei jedem Lauf gegen den
13 * tatsächlichen Abhängigkeitsbaum – so fällt eine Verschiebung beim nächsten
14 * `npm install` auf, statt still zu bleiben.
15 */
16
17 import { readFileSync } from 'node:fs';
18 import { join } from 'node:path';
19 import { fileURLToPath } from 'node:url';
20
21 import { describe, expect, it } from 'vitest';
22
23 const wurzel = fileURLToPath(new URL('..', import.meta.url));
24
25 interface Sperreintrag {
26 readonly version?: string;
27 readonly dev?: boolean;
28 readonly devOptional?: boolean;
29 }
30
31 interface Paketsperre {
32 readonly packages?: Record<string, Sperreintrag>;
33 }
34
35 interface Drittlizenz {
36 readonly name: string;
37 readonly version: string | null;
38 readonly lizenz: string;
39 readonly lizenztext: string | null;
40 readonly hinweis?: string;
41 }
42
43 function lies(pfad: string): unknown {
44 return JSON.parse(readFileSync(join(wurzel, pfad), 'utf8'));
45 }
46
47 const sperre = lies('package-lock.json') as Paketsperre;
48 const uebersicht = lies('../content/drittlizenzen.json') as {
49 pakete: Drittlizenz[];
50 stand: string;
51 anwendungsversion: string;
52 };
53
54 /** Pakete, die electron-builder in das Installationspaket kopiert. */
55 function produktionsabhaengigkeiten(): string[] {
56 return Object.entries(sperre.packages ?? {})
57 .filter(([pfad, eintrag]) => {
58 if (!pfad.startsWith('node_modules/')) {
59 return false;
60 }
61 return eintrag.dev !== true && eintrag.devOptional !== true;
62 })
63 .map(([pfad]) => pfad.slice('node_modules/'.length))
64 .sort();
65 }
66
67 describe('Übersicht der Fremdkomponenten', () => {
68 const genannt = new Map(uebersicht.pakete.map((p) => [p.name, p]));
69
70 it('nennt jede Produktionsabhängigkeit', () => {
71 const fehlend = produktionsabhaengigkeiten().filter((name) => !genannt.has(name));
72
73 expect(
74 fehlend,
75 'Diese Pakete werden ausgeliefert, stehen aber nicht in der Übersicht. ' +
76 'Erneuern mit: python tools/lizenzen_erzeugen.py',
77 ).toEqual([]);
78 });
79
80 it('nennt zu jeder Produktionsabhängigkeit die ausgelieferte Version', () => {
81 for (const [pfad, eintrag] of Object.entries(sperre.packages ?? {})) {
82 if (!pfad.startsWith('node_modules/') || eintrag.dev === true) {
83 continue;
84 }
85 const name = pfad.slice('node_modules/'.length);
86 const paket = genannt.get(name);
87 if (paket === undefined) {
88 continue; // vom vorigen Test abgedeckt
89 }
90 expect(paket.version, `Version von ${name}`).toBe(eintrag.version);
91 }
92 });
93
94 it('liefert zu jedem echten Paket auch den Lizenztext mit', () => {
95 /* Sammelposten (Chromium, Node.js, SQLite) haben keinen eigenen Text im
96 Baum – sie tragen stattdessen einen Hinweis, wo er zu finden ist. */
97 for (const paket of uebersicht.pakete) {
98 if (paket.hinweis !== undefined) {
99 continue;
100 }
101 expect(paket.lizenztext, `Lizenztext von ${paket.name}`).toBeTruthy();
102 expect((paket.lizenztext ?? '').length).toBeGreaterThan(200);
103 }
104 });
105
106 it('trägt ein echtes Datum, nicht den Vermerk „automatisch erzeugt“', () => {
107 // Nur mit Datum lässt sich der Stand der Datei einem Paket zuordnen.
108 expect(uebersicht.stand).toMatch(/^\d{4}-\d{2}-\d{2}$/u);
109 });
110
111 it('nennt die Fassung, zu der sie gehört', () => {
112 /* Die Datei stempelt sich selbst mit der Fassung aus `package.json`.
113 Geprüft hat das bis 0.26.1 niemand – der Test darüber sah nur nach,
114 ob im Feld `stand` ein Datum steht, nicht ob es das richtige ist. Die
115 Übersicht wurde zuletzt zu 0.8.0 erzeugt und reiste seitdem in jedem
116 Paket mit: achtzehn Fassungen lang mit einer Herkunftsangabe, die
117 nicht stimmte. Wer wissen will, ob die Liste zum Paket gehört, das er
118 vor sich hat, kann sich nur auf diese beiden Felder stützen. */
119 const { version } = lies('package.json') as { version: string };
120
121 expect(uebersicht.anwendungsversion).toBe(version);
122 });
123 });