import { readFileSync } from 'node:fs'; import { describe, expect, it } from 'vitest'; /* * Befunde 33, 34, 61 und 62 zur Fassung 5.10.0. * * Vier Aussagen des Pruefstuecks, die nicht stimmten. Drei davon stehen in der * ausgelieferten Projektdatei bzw. in der ausgegebenen Planunterlage, eine im * Quelltext des Werkzeugs. * * WARUM DIESE DATEI DAS WERKZEUG NICHT AUSFUEHRT: `tools/vierarmigerKnotenpunkt.ts` * schreibt beim blossen Einbinden `beispiele/vierarmiger-knotenpunkt.svg` und * `beispiele/vierarmiger-knotenpunkt.lsap`. Geprueft wird deshalb der Quelltext * gegen die eingecheckten Erzeugnisse - dieselbe Vorsicht, aus der * tests/tools/winkelangabe.test.ts `node:fs` stellt. * * DIE PROJEKTDATEI TRAEGT WEITERHIN schemaVersion 7. Sie ist zugleich * Pruefstueck fuer den Altdatei-Pfad (bewusst so); ein Neuerzeugen * haette sie auf 13 gehoben. Berichtigt sind an ihr nur die beiden Angaben, um * die es hier geht. * * GEGEN DEN ALTSTAND schlagen alle Faelle bis auf die drei Vorbedingungen fehl. */ const WERKZEUG = readFileSync('tools/vierarmigerKnotenpunkt.ts', 'utf8'); const PROJEKTDATEI = JSON.parse(readFileSync('beispiele/vierarmiger-knotenpunkt.lsap', 'utf8')) as { schemaVersion: number; meta: { description: string }; lageplan: { bild: { herkunft: string }; kalibrierung: { von: { x: number; y: number }; bis: { x: number; y: number }; laengeMeter: number; herkunft: string; }; haltlinien: { id: string; von: { x: number; y: number }; bis: { x: number; y: number } }[]; }; }; const SVG = readFileSync('beispiele/vierarmiger-knotenpunkt.svg', 'utf8'); /** Eine Zahl aus dem Werkzeug, damit die Wache nicht selbst rechnet. */ function konstante(name: string): number { const treffer = new RegExp(`const ${name} = (-?[0-9.]+);`).exec(WERKZEUG); expect(treffer, `Konstante ${name} im Werkzeug nicht gefunden`).not.toBeNull(); return Number(treffer![1]); } describe('Befund 33 - die Projektbeschreibung nennt die Lage der Haltlinie', () => { const HALTLINIE_VOR = konstante('HALTLINIE_VOR'); const FURT_VOR = konstante('FURT_VOR'); const PX_JE_METER = konstante('PX_JE_METER'); it('ist die Haltlinie im Lageplan wirklich HALTLINIE_VOR Meter vor der Mitte', () => { // Vorbedingung: Sie stimmt schon im Altstand - nur der Satz wich ab. const nord = PROJEKTDATEI.lageplan.haltlinien.find((h) => h.id === 'hl-arm-nord'); expect(nord, 'Haltlinie "hl-arm-nord"').toBeDefined(); const mitteY = konstante('KANTE') * PX_JE_METER * 0.5; expect((mitteY - nord!.von.y) / PX_JE_METER).toBe(HALTLINIE_VOR); }); it('nennt die Beschreibung der Projektdatei dieselbe Zahl', () => { const treffer = /Haltlinie ([0-9]+) m vor Knotenpunktmitte/.exec(PROJEKTDATEI.meta.description); expect(treffer, 'Satz zur Haltlinienlage in meta.description').not.toBeNull(); expect(Number(treffer![1]), 'Haltlinienlage in der Projektbeschreibung').toBe(HALTLINIE_VOR); }); it('nennt die Beschreibung die Furt gesondert, statt ihre Zahl der Haltlinie zu geben', () => { expect(PROJEKTDATEI.meta.description).toContain(`Furt ${FURT_VOR} m davor`); }); it('bildet das Werkzeug den Satz aus den Konstanten', () => { // Sonst laufen Text und Geometrie beim naechsten Massaenderung erneut // auseinander - genau so ist der Fehler entstanden. const satz = /description:\s*((?:`[^`]*`\s*\+\s*)*`[^`]*`),/.exec(WERKZEUG); expect(satz, 'Beschreibung als Schablone (Backtick) im Werkzeug').not.toBeNull(); expect(satz![1]).toContain('${HALTLINIE_VOR}'); expect(satz![1]).toContain('${FURT_VOR}'); expect(satz![1], 'die Haltlinienlage steht nicht mehr als Zahl im Satz').not.toMatch( /Haltlinie [0-9]/, ); }); }); describe('Befund 34 - die Kalibrierung des Pruefstuecks behauptet keinen Kartendienst', () => { it('zeichnet das Werkzeug sein Hintergrundbild selbst', () => { // Vorbedingung: Kein Kartendienst ist beteiligt - das einzige `fetch` des // Vorhabens liegt in electron/karte.ts und wird hier nie beruehrt. expect(PROJEKTDATEI.lageplan.bild.herkunft).toBe( 'Schematischer vierarmiger Knotenpunkt (Pruefstueck)', ); expect(WERKZEUG).toContain('datenUrl: baueBild()'); }); it('weist die Projektdatei den Massstab als gemessen aus', () => { // 'georeferenziert' heisst: Das Programm hat den Ausschnitt selbst // angefordert und kennt seine Kantenlaenge deshalb exakt. // Der Ausdruck druckt daraus "aus dem Kartendienst übernommen ..., nicht // nachgemessen", die Seitenleiste des Lageplans "Er muss nicht nachgemessen // werden" - beides falsch fuer ein selbst gezeichnetes Bild. expect(PROJEKTDATEI.lageplan.kalibrierung.herkunft).toBe('gemessen'); }); it('setzt das Werkzeug denselben Wert', () => { const block = /kalibrierung: \{[\s\S]*?herkunft: '([a-z]+)'/.exec(WERKZEUG); expect(block, 'Kalibrierungsblock im Werkzeug').not.toBeNull(); expect(block![1], 'herkunft der Kalibrierung im Werkzeug').toBe('gemessen'); }); it('ist die Strecke bekannter Laenge nachpruefbar', () => { // Der Zweig 'gemessen' druckt "an einer Strecke bekannter Länge // abgegriffen: 90,0 m auf 900 Bildpunkten". Beide Zahlen muessen in der // Datei stehen, sonst waere auch diese Aussage nicht belegt. const k = PROJEKTDATEI.lageplan.kalibrierung; const laengeBildpunkte = Math.hypot(k.bis.x - k.von.x, k.bis.y - k.von.y); expect(laengeBildpunkte).toBeGreaterThan(0); expect(k.laengeMeter).toBe(konstante('KANTE')); expect(laengeBildpunkte).toBe(konstante('KANTE') * konstante('PX_JE_METER')); }); }); describe('Befund 61 - keine angekuendigte Darstellung ohne Ausgabe', () => { it('kuendigt das Werkzeug im SVG nichts zu feindlichen Beruehrungen an', () => { // Der Abschnitt lief ueber alle feindlichen Paare, suchte beide Planlinien // heraus und erzeugte danach nichts - kein `teile.push`. Entweder zeichnen // oder die Ankuendigung fallen lassen; gewaehlt ist das Zweite, weil der // Kopfkommentar von `baueSvg` nur zusagt, die Punkte sichtbar zu machen. // Gesucht wird die Abschnittsueberschrift im Rumpf, nicht das Wort: Der // Kopfkommentar von `baueSvg` nennt den behobenen Befund und darf das. const ueberschrift = WERKZEUG.split('\n').filter( (z) => z.trim() === '// Feindliche Beruehrungen', ); expect(ueberschrift, 'Abschnittsueberschrift im Rumpf von baueSvg').toHaveLength(0); expect(WERKZEUG).not.toContain('vertraeglichkeit.filter((v) => v.feindlich)'); }); it('rechnet das Werkzeug die Ableitung nicht ein drittes Mal', () => { // Der dritte Aufruf haengt allein an der leeren Schleife: Der zweite // Parameter von `baueSvg` wurde nirgends sonst gebraucht. const aufrufe = WERKZEUG.match(/\bleiteAb\(/g) ?? []; expect(aufrufe, 'Aufrufe von leiteAb im Werkzeug').toHaveLength(2); expect(WERKZEUG).toContain('baueSvg(fertig)'); }); it('traegt das erzeugte Bild folgerichtig kein Merkmal zu Konflikten', () => { // Gegenprobe zur Ankuendigung: Was im SVG steht, sind Fahrbahnen, // Korridore, Mittellinien, Haltlinien und deren Beschriftung. expect(SVG).toContain(' { it('spannt die Zeichenflaeche die Kantenlaenge in Metern auf', () => { // Vorbedingung und zugleich der Beweis: KANTE ist eine Laenge in Metern, // die viewBox fuehrt dieselbe Zahl. Waere eine Einheit ein Dezimeter, // stuenden dort 900. const kante = konstante('KANTE'); expect(SVG).toContain(`viewBox="0 0 ${kante} ${kante}"`); }); it('liegt die Haltlinie Nord HALTLINIE_VOR Einheiten vor der Mitte', () => { const mitte = konstante('KANTE') / 2; const treffer = / { const zeile = WERKZEUG.split('\n').find((z) => z.includes('const s = (KANTE * PX_JE_METER)')); expect(zeile, 'Zeile mit dem SVG-Massstab').toBeDefined(); expect(zeile).toContain('1 Einheit = 1 m'); expect(zeile).not.toContain('dm'); }); });