lsa-planer

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

/ tests domain konfliktkennungSchreibstellen.test.ts

5,7 KB Rohdatei
tests/domain/konfliktkennungSchreibstellen.test.ts — 131 Zeilen
1 import { describe, expect, it } from 'vitest';
2 import { setConflict } from '@/app/actions';
3 import { validateProject } from '@/domain/validation/engine';
4 import { buildSignalPlan } from '@/domain/plan/signalPlan';
5 import { createStandardIntersectionProject } from '@/domain/model/factory';
6 import { parseProject } from '@/domain/model/schema';
7 import type { Conflict, Project } from '@/domain/model/project';
8
9 /*
10 * Befund 2 - die verbliebenen Schreib- und Nachschlagstellen (Fassung 5.9.0).
11 *
12 * Der Befund war: Doppelte Konfliktkennungen wurden beim Einlesen nicht
13 * bemerkt, und das Aendern einer Beziehung aenderte stumm eine zweite mit.
14 * Die erste Behebung hat die Neuvergabe beim Einlesen eingebaut - damit sind Daten aus
15 * `parseProject` entschaerft. `setConflict` sucht seine Beziehung aber ueber
16 * das Paar (fromId, toId) und schreibt danach ueber die KENNUNG: zwei
17 * Schluessel fuer dieselbe Entscheidung. Trifft die Kennung ein zweites Mal zu,
18 * schreibt die Funktion an eine Beziehung, die sie nie nachgeschlagen hat.
19 */
20
21 const DATUM = new Date('2026-01-01T00:00:00Z');
22
23 /**
24 * Zwei Beziehungen mit derselben Kennung, aber verschiedenen Paaren.
25 *
26 * Von Hand zusammengesetzt: Genau so entsteht der Zustand auch im Betrieb -
27 * aus einer Projektdatei fremder Herkunft oder einer Umwandlung, die an
28 * `parseProject` vorbeifuehrt.
29 */
30 function mitDoppelterKennung(): { projekt: Project; erste: Conflict; zweite: Conflict } {
31 const roh = createStandardIntersectionProject('Doppelte Kennung', DATUM);
32 const erste = roh.conflicts[0]!;
33 const zweite = { ...roh.conflicts[1]!, id: erste.id };
34 return {
35 projekt: { ...roh, conflicts: [erste, zweite] },
36 erste,
37 zweite,
38 };
39 }
40
41 describe('setConflict schreibt ueber das Paar, nicht ueber die Kennung', () => {
42 it('aendert nur die nachgeschlagene Beziehung', () => {
43 const { projekt, erste, zweite } = mitDoppelterKennung();
44
45 const nachher = setConflict(projekt, zweite.fromId, zweite.toId, { clearingDistance: 44 });
46
47 const geaendert = nachher.conflicts.find(
48 (c) => c.fromId === zweite.fromId && c.toId === zweite.toId,
49 );
50 const unberuehrt = nachher.conflicts.find(
51 (c) => c.fromId === erste.fromId && c.toId === erste.toId,
52 );
53 expect(geaendert?.clearingDistance).toBe(44);
54 // Bis hierher trug auch diese Beziehung 44 m - der Anwender hat einen Weg
55 // eingetragen und zwei Zwischenzeiten veraendert.
56 expect(unberuehrt?.clearingDistance).toBe(erste.clearingDistance);
57 });
58
59 it('zieht den Herkunftsnachweis nur an der nachgeschlagenen Beziehung nach', () => {
60 // Der Nachweis sagt aus, dass die Zeile im Ausdruck aus dem Lageplan
61 // stammt. An der mitgeaenderten Beziehung war er ebenfalls entwertet -
62 // ohne dass dort jemand etwas eingetragen haette.
63 const { projekt, erste, zweite } = mitDoppelterKennung();
64
65 const nachher = setConflict(projekt, zweite.fromId, zweite.toId, { clearingDistance: 44 });
66
67 const unberuehrt = nachher.conflicts.find(
68 (c) => c.fromId === erste.fromId && c.toId === erste.toId,
69 );
70 expect(unberuehrt?.wegnachweis).toBe(erste.wegnachweis);
71 });
72
73 it('legt eine noch nicht erfasste Beziehung weiterhin an', () => {
74 const roh = createStandardIntersectionProject('Neu', DATUM);
75 const k1 = roh.signalGroups.find((g) => g.name === 'K1')!;
76 const k2 = roh.signalGroups.find((g) => g.name === 'K2')!;
77
78 const nachher = setConflict(roh, k1.id, k2.id, { clearingDistance: 17 });
79
80 expect(nachher.conflicts).toHaveLength(roh.conflicts.length + 1);
81 const neu = nachher.conflicts.find((c) => c.fromId === k1.id && c.toId === k2.id);
82 expect(neu?.clearingDistance).toBe(17);
83 });
84 });
85
86 /*
87 * Die zweite Stelle des Befundes: Der Sprungknopf des Pruefberichts loest die
88 * Notiz ueber `c.id === note.konfliktId` auf. Das traegt - aber nur, solange
89 * die Kennungen eindeutig sind. Genau das ist hier festgehalten: Faellt die
90 * Neuvergabe beim Einlesen weg, ist die Aufloesung ueber die Kennung nicht mehr
91 * eindeutig, und der Bericht schickt den Anwender an die falsche Beziehung.
92 */
93 describe('Voraussetzung der Aufloesung ueber die Kennung', () => {
94 it('vergibt eine doppelte Konfliktkennung beim Einlesen neu', () => {
95 const roh = createStandardIntersectionProject('Einlesen', DATUM);
96 const rohDatei = JSON.parse(JSON.stringify(roh)) as Record<string, unknown>;
97 const konflikte = rohDatei['conflicts'] as Record<string, unknown>[];
98 konflikte[1]!['id'] = konflikte[0]!['id'];
99
100 const ergebnis = parseProject(rohDatei);
101
102 expect(ergebnis.project).not.toBeNull();
103 const kennungen = (ergebnis.project?.conflicts ?? []).map((c) => c.id);
104 expect(kennungen.length).toBeGreaterThan(1);
105 expect(new Set(kennungen).size).toBe(kennungen.length);
106 });
107
108 it('schickt den Sprungknopf an die Beziehung, aus der die Meldung stammt', () => {
109 // Eine von Hand auf 1 s gekuerzte Zwischenzeit K1 nach K3: Die Meldung
110 // traegt die Kennung dieser Beziehung, und das Sprungziel muss sie nennen.
111 const roh = createStandardIntersectionProject('Sprungziel', DATUM);
112 const k1 = roh.signalGroups.find((g) => g.name === 'K1')!;
113 const k3 = roh.signalGroups.find((g) => g.name === 'K3')!;
114 const projekt: Project = {
115 ...roh,
116 conflicts: roh.conflicts.map((c) =>
117 c.fromId === k1.id && c.toId === k3.id ? { ...c, manualIntergreen: 1 } : c,
118 ),
119 };
120 const gemeint = projekt.conflicts.find((c) => c.fromId === k1.id && c.toId === k3.id)!;
121
122 const bericht = validateProject(projekt, buildSignalPlan(projekt), DATUM);
123
124 const befund = bericht.findings.find(
125 (f) => f.rule === 'signalplan.zwischenzeit-vorgabe-unter-rechenwert',
126 );
127 expect(befund?.target?.kind).toBe('konflikt');
128 expect(befund?.target?.id).toBe(gemeint.id);
129 expect(befund?.target?.label).toBe('K1 nach K3');
130 });
131 });