import { describe, expect, it } from 'vitest'; import { intergreenDetailCsv } from '@/services/export/csv'; import { ueberfahrzeitAnsatzSatz } from '@/services/export/bewertung'; import { buildSignalPlan } from '@/domain/plan/signalPlan'; import { createStandardIntersectionProject } from '@/domain/model/factory'; import type { Project } from '@/domain/model/project'; import { UEBERFAHRZEIT_ANSATZ_LABELS } from '@/domain/rilsa/constants'; import { resolveRilsaSettings } from '@/domain/rilsa/settings'; /** * Was Tabellenausgabe und Unterlagentexte ueber die Ueberfahrzeit sagen duerfen * (Fassung 5.5.0, Befunde F5 und B5). * * F5 Die Spalte neben der Ueberfahrzeit nannte in JEDER Zeile den * Kfz-Rechenansatz - auch neben der 0,00 s eines Fussgaengerstroms und * neben den 5 s eines OePNV-Stroms aus der Vmax-Staffel. Der Ansatz * erreicht diese Stroeme nicht. * B5 Der gedruckte Absatz unter der Zwischenzeitenmatrix nannte die * Verkuerzung mit "bis zu 3 s je Uebergang" - eine Zahl, die nur fuer die * Regelwerte 3 s/2 s gilt. Beide sind Vorgabenfelder. */ const STICHTAG = new Date('2026-01-01T12:00:00Z'); /** Vorlage mit abbiegendem erstem Kfz-Strom und dem Ansatz "fest". */ function mitFesterUeberfahrzeit(): Project { const basis = createStandardIntersectionProject('Prüffall', STICHTAG); return { ...basis, signalGroups: basis.signalGroups.map((g, i) => i === 0 ? { ...g, movement: 'links' as const } : g, ), settings: { ...basis.settings, ueberfahrzeitAnsatz: 'fest' }, }; } /** Zeilen der Zwischenzeit-Tabellenausgabe als Spaltenfelder. */ function tabelle(project: Project): { kopf: string[]; zeilen: string[][] } { const csv = intergreenDetailCsv(project, buildSignalPlan(project)); const [kopfzeile, ...rest] = csv.split('\r\n'); const zerlege = (zeile: string): string[] => (zeile.match(/("([^"]|"")*"|[^;]*)(;|$)/g) ?? []).map((feld) => feld.replace(/;$/, '').replace(/^"|"$/g, '').replace(/""/g, '"'), ); return { kopf: zerlege((kopfzeile ?? '').replace(/^/, '')), zeilen: rest.filter((z) => z.length > 0).map(zerlege), }; } describe('F5 - die Ueberfahrzeit-Spalte nennt die Herkunft je Verkehrsart', () => { it('nennt den Kfz-Ansatz nur an Kfz-Raeumstroemen', () => { const project = mitFesterUeberfahrzeit(); const { kopf, zeilen } = tabelle(project); const spalte = kopf.indexOf('Überfahrzeit-Herkunft'); expect(spalte, 'Die Spalte fehlt').toBeGreaterThanOrEqual(0); let kfz = 0; let andere = 0; for (const zeile of zeilen) { const von = project.signalGroups.find((g) => g.name === zeile[0]); if (von === undefined) continue; const wert = zeile[spalte] ?? ''; if (von.mode === 'kfz') { expect(wert, von.name).toBe(UEBERFAHRZEIT_ANSATZ_LABELS.fest); kfz += 1; } else { // Der Ansatz gilt fuer sie nicht - und wird deshalb auch nicht // neben ihre Ueberfahrzeit gedruckt. expect(wert, von.name).not.toContain('feste Überfahrzeit'); expect(wert, von.name).not.toBe(''); andere += 1; } } expect(kfz, 'Keine Kfz-Zeile geprüft').toBeGreaterThan(0); expect(andere, 'Keine Zeile mit anderer Verkehrsart geprüft').toBeGreaterThan(0); }); it('nennt eine von Hand gesetzte Ueberfahrzeit als solche', () => { const basis = mitFesterUeberfahrzeit(); const project: Project = { ...basis, conflicts: basis.conflicts.map((c, i) => (i === 0 ? { ...c, crossingTimeOverride: 4 } : c)), }; const { kopf, zeilen } = tabelle(project); const spalte = kopf.indexOf('Überfahrzeit-Herkunft'); const erste = project.conflicts[0]!; const von = project.signalGroups.find((g) => g.id === erste.fromId)!; const nach = project.signalGroups.find((g) => g.id === erste.toId)!; const zeile = zeilen.find((z) => z[0] === von.name && z[1] === nach.name)!; expect(zeile[spalte]).toContain('von Hand'); }); }); describe('B5 - der Unterlagentext nennt die Groessenordnung der wirksamen Vorgaben', () => { it('rechnet die Verkuerzung aus den eingetragenen Werten', () => { const { defaults } = resolveRilsaSettings({ crossingTimeKfzGeradeaus: 4, crossingTimeKfzAbbiegend: 3, }); const satz = ueberfahrzeitAnsatzSatz('fest', defaults); expect(satz).toContain('beim Abbiegen um bis zu 2 s'); expect(satz).toContain('geradeaus um bis zu 1 s'); expect(satz).not.toContain('bis zu 3 s'); }); it('behauptet keine Verkuerzung, wo keine ist', () => { const { defaults } = resolveRilsaSettings({ crossingTimeKfzGeradeaus: 6, crossingTimeKfzAbbiegend: 6, }); const satz = ueberfahrzeitAnsatzSatz('fest', defaults); expect(satz).not.toContain('VERKÜRZT'); expect(satz).toContain('nirgends kürzer'); // Begruendungspflichtig bleibt der Ansatz gleichwohl. expect(satz).toContain('zu begründen'); }); it('haelt am Regelwertsatz die belegte Groessenordnung fest', () => { expect(ueberfahrzeitAnsatzSatz('fest')).toContain('beim Abbiegen um bis zu 3 s'); }); });