lsa-planer

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

/ tests domain festeFreigabezeitNull.test.ts

4,2 KB Rohdatei
tests/domain/festeFreigabezeitNull.test.ts — 104 Zeilen
1 import { describe, expect, it } from 'vitest';
2 import { distributeGreenTimes } from '@/domain/rilsa/greenSplit';
3 import { buildSignalPlan } from '@/domain/plan/signalPlan';
4 import { createStandardIntersectionProject } from '@/domain/model/factory';
5 import * as actions from '@/app/actions';
6 import type { Project } from '@/domain/model/project';
7
8 /*
9 * Befund 41, behoben in Fassung 5.10.0 - eine Obergrenze von 0 s galt als
10 * "keine Obergrenze".
11 *
12 * `safeMax` gab fuer jeden Wert, der nicht groesser als 0 ist,
13 * Number.POSITIVE_INFINITY zurueck. Eine Phase mit maxGreen = 0 galt damit als
14 * Phase OHNE Obergrenze - das Gegenteil dessen, was eingetragen wurde. Der
15 * Weg dahin: Das Feld "Feste Freigabezeit der N. Phase" laesst 0 s
16 * ausdruecklich zu (FREIGABE_GRENZEN min 0, unterschieden von "leer =
17 * verteilen"), und an einer Phase ohne Signalgruppe ist die
18 * Mindestfreigabezeit 0, die Vorgabe wird also nicht als zu kurz abgelehnt;
19 * `buildSignalPlan` setzt dann minGreen = maxGreen = 0.
20 *
21 * Folge: Die Phase zog die gesamte Zeit an sich, die die uebrigen Phasen wegen
22 * ihrer Hoechstfreigabezeit nicht aufnehmen konnten - waehrend der Hinweis
23 * 'freigabezeit-rest-umgelegt' woertlich zusagt, "eine von Hand vorgegebene
24 * Freigabezeit bleibt dabei unangetastet".
25 *
26 * Unbegrenzt heisst `undefined` (so der Typ: "Optionale Obergrenze"), nicht 0.
27 * `safeMin` daneben behandelt 0 bereits richtig.
28 *
29 * GEGEN DEN ALTSTAND schlagen die ersten drei Faelle fehl. Der vierte ist die
30 * Gegenprobe fuer die weggelassene Obergrenze und besteht auch dort.
31 */
32
33 const DATUM = new Date('2026-01-01T00:00:00Z');
34
35 /** Standardknotenpunkt mit fester Umlaufzeit und einer leeren Phase mit 0 s. */
36 function leerePhaseMitNullVorgabe(): Project {
37 const roh = createStandardIntersectionProject('Feste Null', DATUM);
38 const mitPhase = actions.addPhase(roh);
39 const leer = mitPhase.phases[2]!;
40 const gesetzt = actions.updatePhase(mitPhase, leer.id, { manualGreen: 0 });
41 return {
42 ...gesetzt,
43 program: { ...gesetzt.program, method: 'manuell', manualCycleTime: 300 },
44 };
45 }
46
47 describe('Befund 41 - Hoechstfreigabezeit 0 s ist eine Obergrenze', () => {
48 it('haelt eine Phase mit der Obergrenze 0 s bei 0 s fest', () => {
49 const ergebnis = distributeGreenTimes(
50 300,
51 [
52 { id: 'a', weight: 0.5, minGreen: 5, maxGreen: 90 },
53 { id: 'leer', weight: 0, minGreen: 0, maxGreen: 0 },
54 ],
55 [0, 0],
56 );
57
58 expect(ergebnis.phases.find((p) => p.id === 'leer')?.green).toBe(0);
59 // Die nicht unterzubringende Zeit laeuft in den bereits vorhandenen Zweig
60 // mit eigener Warnung - sie verschwindet nicht.
61 expect(ergebnis.notes.some((n) => n.code === 'freigabezeit-rest')).toBe(true);
62 expect(ergebnis.phases.reduce((s, p) => s + p.green, 0)).toBe(300);
63 });
64
65 it('gibt ihr auch ohne Verkehrsstaerken nichts ab', () => {
66 // Ohne Gewichtung werden alle Gewichte auf 1 gesetzt; bis dahin bekam die
67 // auf 0 s gesetzte Phase die Haelfte der Umlaufzeit.
68 const ergebnis = distributeGreenTimes(
69 60,
70 [
71 { id: 'a', weight: 0, minGreen: 5, maxGreen: 120 },
72 { id: 'leer', weight: 0, minGreen: 0, maxGreen: 0 },
73 ],
74 [0, 0],
75 );
76
77 expect(ergebnis.phases.find((p) => p.id === 'leer')?.green).toBe(0);
78 expect(ergebnis.phases.find((p) => p.id === 'a')?.green).toBe(60);
79 });
80
81 it('haelt die Vorgabe auch im vollstaendigen Signalzeitenplan ein', () => {
82 const plan = buildSignalPlan(leerePhaseMitNullVorgabe());
83
84 expect(plan.phases[2]?.duration).toBe(0);
85 // Der Hinweis, der "unangetastet" zusagt, entsteht damit gar nicht mehr:
86 // Es gibt keine Phase mit Spielraum, auf die umgelegt werden koennte.
87 expect(plan.notes.some((n) => n.code === 'freigabezeit-rest-umgelegt')).toBe(false);
88 });
89
90 it('laesst eine weggelassene Obergrenze weiterhin unbegrenzt', () => {
91 // Gegenprobe: "unbegrenzt" wird durch das Weglassen des Feldes
92 // ausgedrueckt, nicht durch die Null.
93 const ergebnis = distributeGreenTimes(
94 300,
95 [
96 { id: 'a', weight: 0.5, minGreen: 5, maxGreen: 90 },
97 { id: 'offen', weight: 0.1, minGreen: 5 },
98 ],
99 [0, 0],
100 );
101
102 expect(ergebnis.phases.find((p) => p.id === 'offen')?.green).toBe(210);
103 });
104 });