lsa-planer

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

/ tests domain migrate.test.ts

6,5 KB Rohdatei
tests/domain/migrate.test.ts — 192 Zeilen
1 import { describe, expect, it } from 'vitest';
2 import { isLegacyProject, loadProject } from '@/domain/model/migrate';
3 import { createStandardIntersectionProject } from '@/domain/model/factory';
4
5 const FIXED_DATE = new Date('2026-01-01T00:00:00Z');
6
7 /** Ein Projekt im Format der Version 4.x, wie es der Altbestand gespeichert hat. */
8 const legacyProject = {
9 metadata: {
10 id: 'LSA-1731600000000-abc123def',
11 version: '4.3.3',
12 createdAt: '2025-11-14T10:00:00.000Z',
13 modifiedAt: '2025-11-15T12:30:00.000Z',
14 },
15 projektdaten: {
16 projektNummer: 'VP-2025-018',
17 projektBezeichnung: 'Hauptstrasse / Bahnhofstrasse',
18 kreuzungstyp: 'Vierarmiger Knotenpunkt',
19 steuerungstyp: 'Festzeitsteuerung',
20 strasse: 'Hauptstrasse',
21 plz: '12345',
22 ort: 'Musterstadt',
23 auftraggeber: 'Stadt Musterstadt',
24 bearbeiter: 'O. Willerding',
25 bemerkungen: 'Bestandsaufnahme 2025',
26 anordnungsnummer: 'A-2025-77',
27 },
28 signalgruppen: [
29 {
30 id: 'SG-1',
31 nummer: 'K1',
32 typ: 'kfz',
33 richtung: 'geradeaus',
34 minGruen: 5,
35 maxGruen: 40,
36 gelb: 3,
37 rotGelb: 2,
38 geschwindigkeit: 50,
39 },
40 {
41 id: 'SG-2',
42 nummer: 'K2',
43 typ: 'kfz',
44 richtung: 'links',
45 minGruen: 5,
46 maxGruen: 30,
47 gelb: 3,
48 rotGelb: 2,
49 geschwindigkeit: 50,
50 },
51 {
52 id: 'SG-3',
53 nummer: 'F1',
54 typ: 'fussgaenger',
55 richtung: '',
56 minGruen: 6,
57 maxGruen: 20,
58 gelb: 3,
59 rotGelb: 2,
60 geschwindigkeit: 50,
61 },
62 ],
63 sperrmatrix: {
64 matrix: [
65 [false, false, true],
66 [false, false, true],
67 [true, true, false],
68 ],
69 autoDetect: false,
70 },
71 zwischenzeiten: {
72 parameter: {
73 berechnungsmethode: 'rilsa2015',
74 vRaeumKfz: 50,
75 vRaeumRad: 20,
76 vRaeumFuss: 1.2,
77 vEinfahrKfz: 40,
78 raeumweg: 24,
79 einfahrweg: 9,
80 },
81 matrix: [
82 [0, 0, 4],
83 [0, 0, 4],
84 [7, 7, 0],
85 ],
86 },
87 phasen: [
88 { id: 'PH-1', nummer: 1, bezeichnung: 'Phase 1', signalgruppen: ['SG-1', 'SG-2'], dauer: 35 },
89 { id: 'PH-2', nummer: 2, bezeichnung: 'Phase 2', signalgruppen: ['SG-3'], dauer: 15 },
90 ],
91 signalplan: { umlaufzeit: 70, signalbilder: [] },
92 umlaufzeit: { verfahren: 'webster', auslastung: 85, verlustzeiten: 12, eingabe: 70 },
93 lageplan: null,
94 pruefungen: {},
95 };
96
97 describe('Uebernahme von Altprojekten', () => {
98 it('erkennt das Altformat', () => {
99 expect(isLegacyProject(legacyProject)).toBe(true);
100 expect(isLegacyProject(createStandardIntersectionProject('x', FIXED_DATE))).toBe(false);
101 });
102
103 it('uebernimmt Projektdaten, Signalgruppen und Phasen', () => {
104 const result = loadProject(legacyProject, FIXED_DATE);
105
106 expect(result.migrated).toBe(true);
107 expect(result.sourceVersion).toBe('4.3.3');
108 expect(result.project.meta.name).toBe('Hauptstrasse / Bahnhofstrasse');
109 expect(result.project.meta.projectNumber).toBe('VP-2025-018');
110 expect(result.project.meta.client).toBe('Stadt Musterstadt');
111 expect(result.project.meta.location).toBe('Hauptstrasse, 12345 Musterstadt');
112 expect(result.project.meta.planner).toBe('O. Willerding');
113 expect(result.project.meta.description).toContain('Anordnungsnummer: A-2025-77');
114
115 expect(result.project.signalGroups.map((g) => g.name)).toEqual(['K1', 'K2', 'F1']);
116 expect(result.project.signalGroups[1]!.movement).toBe('links');
117 expect(result.project.signalGroups[2]!.mode).toBe('fuss');
118 expect(result.project.signalGroups[2]!.movement).toBe('querung');
119
120 expect(result.project.phases.map((p) => p.name)).toEqual(['Phase 1', 'Phase 2']);
121 expect(result.project.phases[0]!.manualGreen).toBe(35);
122 });
123
124 it('bildet die Phasenzuordnung ueber die neuen Kennungen ab', () => {
125 const result = loadProject(legacyProject, FIXED_DATE);
126 const k1 = result.project.signalGroups.find((g) => g.name === 'K1')!;
127 const k2 = result.project.signalGroups.find((g) => g.name === 'K2')!;
128 const f1 = result.project.signalGroups.find((g) => g.name === 'F1')!;
129
130 expect(result.project.phases[0]!.signalGroupIds).toEqual([k1.id, k2.id]);
131 expect(result.project.phases[1]!.signalGroupIds).toEqual([f1.id]);
132 });
133
134 it('leitet die Konfliktbeziehungen aus der Sperrmatrix ab', () => {
135 const result = loadProject(legacyProject, FIXED_DATE);
136 const names = result.project.conflicts.map((c) => {
137 const from = result.project.signalGroups.find((g) => g.id === c.fromId)!.name;
138 const to = result.project.signalGroups.find((g) => g.id === c.toId)!.name;
139 return `${from}->${to}`;
140 });
141
142 expect(names).toContain('K1->F1');
143 expect(names).toContain('F1->K1');
144 expect(names).toContain('K2->F1');
145 expect(names).not.toContain('K1->K2');
146 });
147
148 it('uebernimmt die alten Zwischenzeiten NICHT, sondern rechnet neu', () => {
149 const result = loadProject(legacyProject, FIXED_DATE);
150
151 for (const conflict of result.project.conflicts) {
152 expect(conflict.manualIntergreen).toBeNull();
153 expect(conflict.clearingDistance).toBe(24);
154 expect(conflict.enteringDistance).toBe(9);
155 }
156
157 expect(result.intergreenComparison.length).toBeGreaterThan(0);
158 expect(result.issues.some((i) => i.path === 'zwischenzeiten')).toBe(true);
159 expect(result.issues.some((i) => i.path === 'conflicts')).toBe(true);
160 });
161
162 it('uebernimmt das Verfahren der Umlaufzeitermittlung', () => {
163 const result = loadProject(legacyProject, FIXED_DATE);
164 expect(result.project.program.method).toBe('webster');
165 expect(result.project.program.phaseOrder).toHaveLength(2);
166 });
167
168 it('uebernimmt eine feste Umlaufzeit, wenn sie im Altprojekt gewaehlt war', () => {
169 const result = loadProject(
170 { ...legacyProject, umlaufzeit: { ...legacyProject.umlaufzeit, verfahren: 'manuell' } },
171 FIXED_DATE,
172 );
173 expect(result.project.program.method).toBe('manuell');
174 expect(result.project.program.manualCycleTime).toBe(70);
175 });
176
177 it('kommt mit einer luecken- und fehlerhaften Altdatei zurecht', () => {
178 const result = loadProject(
179 { projektdaten: {}, signalgruppen: null, phasen: 'kaputt' },
180 FIXED_DATE,
181 );
182 expect(result.migrated).toBe(true);
183 expect(result.project.signalGroups).toEqual([]);
184 expect(result.project.phases).toEqual([]);
185 });
186
187 it('warnt bei einer Datei aus einer neueren Programmversion', () => {
188 const result = loadProject({ schemaVersion: 99, signalGroups: [] }, FIXED_DATE);
189 expect(result.migrated).toBe(false);
190 expect(result.issues.some((i) => i.path === 'schemaVersion')).toBe(true);
191 });
192 });