import { readFileSync } from 'node:fs'; import { describe, expect, it } from 'vitest'; import { setzeAlleWege } from '@/app/actions'; import { leiteAb, uebernimmWege, wegSchluessel } from '@/domain/geometrie/ableitung'; import { loadProject } from '@/domain/model/migrate'; import { parseProject } from '@/domain/model/schema'; import { aendereKonflikt, type Conflict, type Project } from '@/domain/model/project'; import { buildSignalPlan, intergreenKey, massgebendeRaeumbeziehung, } from '@/domain/plan/signalPlan'; import { RILSA_DEFAULTS } from '@/domain/rilsa/constants'; import { clearingSpeedFor } from '@/domain/rilsa/intergreen'; /* * Der massgebende Strom (Fassung 5.11.0). * * `uebernimmWege` verdichtet die vermessenen Fahrlinienpaare auf * Signalgruppenpaare und waehlt je Paar das unguenstigste Strompaar. Gerechnet * wird dabei mit der Fahrbeziehung der FAHRLINIE (ableitung.ts, `movement: * von.movement`), gespeichert wurde bis zu diesem Befund aber nur die * Geometrie. Der Planaufbau rechnete die Beziehung danach mit der * Fahrbeziehung der SIGNALGRUPPE neu (signalPlan.ts, `movement: * from.movement`). * * Bei einer Sammelsignalgruppe "geradeaus UND rechts" - dem Regelfall des * gemeinsamen Fahrstreifens, den das Erzeugungswerkzeug selbst baut - fielen * beide Rechnungen auseinander: Der Raeumweg des Rechtsabbiegers wurde mit * 10,0 m/s statt mit 7,0 m/s geraeumt. In der Planunterlage stand die * Zwischenzeit neben ihrem eigenen Widerspruch ("... (9 s)" bei gedruckten * 7 s), und zwei Sekunden fehlten in der gefaehrlichen Richtung. * * Diese Datei bewacht beide Seiten: dass die Uebernahme die Fahrbeziehung des * massgebenden Stroms mitschreibt und dass der Plan mit ihr rechnet - und * ebenso, dass sie es NICHT tut, wo der massgebende Strom die Fahrbeziehung * seiner Gruppe hat (sonst raeumte ein Geradeausweg mit Abbiegegeschwindigkeit * und die Zwischenzeit fiele unbegruendet zu lang aus). */ const DATEI = 'beispiele/vierarmiger-knotenpunkt.lsap'; const DATUM = new Date('2026-01-01T00:00:00Z'); /** Die mitgelieferte Altdatei, eingelesen wie beim Oeffnen. */ function beispiel(): Project { const ergebnis = parseProject(JSON.parse(readFileSync(DATEI, 'utf8')), DATUM); expect(ergebnis.issues.filter((i) => i.severity === 'fehler')).toEqual([]); return ergebnis.project; } /** Dieselbe Datei nach "Ausgewählte übernehmen" ueber alle Beziehungen. */ function nachUebernahme(): Project { const basis = beispiel(); const ableitung = leiteAb(basis, basis.lageplan); return uebernimmWege(basis, basis.lageplan, ableitung, ableitung.wege.map(wegSchluessel), DATUM) .project; } function konflikt(project: Project, fromId: string, toId: string): Conflict { const gefunden = project.conflicts.find((c) => c.fromId === fromId && c.toId === toId); expect(gefunden, `Beziehung ${fromId} -> ${toId}`).toBeDefined(); return gefunden!; } /** Die Sekundenzahl, die die Uebernahme als massgebend in die Bemerkung schrieb. */ function vermerkteZwischenzeit(note: string): number | null { const treffer = /\((\d+) s\)\.$/.exec(note); return treffer === null ? null : Number(treffer[1]); } describe('A1/1: Die Uebernahme fuehrt die Fahrbeziehung des massgebenden Stroms mit', () => { it('schreibt "rechts" an die vier Beziehungen, an denen der Rechtsabbieger den Ausschlag gibt', () => { const project = nachUebernahme(); // Die vier Kfz-Sammelgruppen tragen selbst 'geradeaus' - ihr Fahrstreifen // fuehrt geradeaus UND rechts. Massgebend ist an diesen vier Beziehungen // der Rechtsabbieger (Bemerkung: '"Nord rechts" raeumt vor ...'). for (const [von, nach] of [ ['sg-k-nord', 'sg-k-ost'], ['sg-k-ost', 'sg-k-sued'], ['sg-k-sued', 'sg-k-west'], ['sg-k-west', 'sg-k-nord'], ] as const) { const gruppe = project.signalGroups.find((g) => g.id === von); expect(gruppe?.movement, `Fahrbeziehung der Gruppe ${von}`).toBe('geradeaus'); expect(konflikt(project, von, nach).raeumbeziehung, `${von} -> ${nach}`).toBe('rechts'); } }); it('laesst das Feld weg, wo der massgebende Strom die Fahrbeziehung seiner Gruppe hat', () => { const project = nachUebernahme(); // "K Nord" -> "K West": massgebend ist "Nord geradeaus" (s0 = 52 m). Das // Feld darf hier NICHT stehen - sonst wuerde eine spaetere Berichtigung // der Fahrbeziehung an der Signalgruppe wirkungslos, und ein pauschales // 'rechts' raeumte einen Geradeausweg mit 7,0 m/s (12 s statt 9 s). const west = konflikt(project, 'sg-k-nord', 'sg-k-west'); expect(west.note).toContain('"Nord geradeaus"'); expect(west.raeumbeziehung).toBeUndefined(); expect('raeumbeziehung' in west).toBe(false); /* * Zwoelf der 72 Beziehungen tragen das Feld - an allen zwoelf gibt der * Rechtsabbieger der Sammelgruppe den Ausschlag. Acht davon aendern die * gedruckte Zwischenzeit: vier Kfz-Beziehungen (7 s -> 9 s) und seit * Fassung 5.43.0 vier Furtbeziehungen (5 s -> 6 s; bis dahin blieben sie * bei 0 s, weil der Einfahrweg des Fussgaengers ab der entfernten * Bordkante gemessen wurde). Die vier eigenen Furten stehen mit 5 s * vorher wie nachher da. * * NACHGEZOGEN AM 10.09.2026 (Fassung 5.24.0): Hier standen acht. Die * vier eigenen Furten - "K Nord" vor "F Nord" und die drei gleichartigen - * sind hinzugekommen, weil dort beide Stroeme auf dieselben 5 s * aufrunden: geradeaus 4,013 s (seit 5.43.0 mit te = 0 s: 4,100 s), * rechts 4,591 s. Bis dahin gewann der zuerst gezeichnete, also die * Reihenfolge der Linien im Lageplan; jetzt * entscheidet bei gleicher gerundeter Zwischenzeit der ungerundete Wert, * und das ist der Rechtsabbieger. Die gedruckte Zwischenzeit bleibt 5 s; * gespeichert wird die anspruchsvollere Geometrie, und die bleibt es * auch, wenn sich Gelbzeit oder zulaessige Geschwindigkeit spaeter * aendern. */ expect( project.conflicts.filter((c) => 'raeumbeziehung' in c).map((c) => `${c.fromId}->${c.toId}`), ).toEqual([ 'sg-k-nord->sg-f-nord', 'sg-k-nord->sg-k-ost', 'sg-k-nord->sg-f-west', 'sg-k-ost->sg-f-ost', 'sg-k-ost->sg-k-sued', 'sg-k-ost->sg-f-nord', 'sg-k-sued->sg-f-sued', 'sg-k-sued->sg-k-west', 'sg-k-sued->sg-f-ost', 'sg-k-west->sg-k-nord', 'sg-k-west->sg-f-west', 'sg-k-west->sg-f-sued', ]); // Die eigene Furt: Das Feld steht jetzt da, die Zahl daneben nicht anders. expect( buildSignalPlan(project).intergreens.get(intergreenKey('sg-k-nord', 'sg-f-nord'))?.value, 'die eigene Furt bleibt bei 5 s', ).toBe(5); expect(project.conflicts.every((c) => c.raeumbeziehung !== 'geradeaus')).toBe(true); expect(project.conflicts).toHaveLength(72); }); it('entfernt eine ueberholte Angabe bei der naechsten Uebernahme', () => { // Eine Beziehung, deren Siegerpaar diesmal die Fahrbeziehung seiner Gruppe // hat, darf die Angabe der vorigen Uebernahme nicht behalten: Der Plan // raeumte den neuen Weg sonst mit der Geschwindigkeit des alten Siegers. const basis = beispiel(); const veraltet: Project = { ...basis, conflicts: basis.conflicts.map((c) => c.fromId === 'sg-k-nord' && c.toId === 'sg-k-west' ? { ...c, raeumbeziehung: 'links' } : c, ), }; expect(konflikt(veraltet, 'sg-k-nord', 'sg-k-west').raeumbeziehung).toBe('links'); const ableitung = leiteAb(veraltet, veraltet.lageplan); const { project } = uebernimmWege( veraltet, veraltet.lageplan, ableitung, ableitung.wege.map(wegSchluessel), DATUM, ); const west = konflikt(project, 'sg-k-nord', 'sg-k-west'); expect('raeumbeziehung' in west).toBe(false); const aufgeloest = buildSignalPlan(project).intergreens.get( intergreenKey('sg-k-nord', 'sg-k-west'), ); expect(aufgeloest?.calculation?.clearingSpeed).toBe(RILSA_DEFAULTS.clearingSpeed.kfzStraight); expect(aufgeloest?.value).toBe(9); }); }); describe('A1/2: Der Plan raeumt mit der Geschwindigkeit des massgebenden Stroms', () => { it('rechnet "K Nord" -> "K Ost" mit 7,0 m/s und 9 s statt mit 10,0 m/s und 7 s', () => { const project = nachUebernahme(); const plan = buildSignalPlan(project); const beziehung = konflikt(project, 'sg-k-nord', 'sg-k-ost'); const aufgeloest = plan.intergreens.get(intergreenKey('sg-k-nord', 'sg-k-ost')); expect(beziehung.clearingDistance).toBeCloseTo(38.91, 2); expect(beziehung.enteringDistance).toBeCloseTo(13.89, 2); expect(aufgeloest?.calculation?.clearingSpeed).toBe(RILSA_DEFAULTS.clearingSpeed.kfzTurning); // tue 3 s + tr (38,91 + 6) / 7,0 = 6,416 s - te 13,89 / 11,1 = 1,251 s expect(aufgeloest?.calculation?.raw).toBeCloseTo(8.164, 3); expect(aufgeloest?.value).toBe(9); }); it('laesst "K Nord" -> "K West" bei 9 s - der Geradeausweg raeumt weiter mit 10,0 m/s', () => { const project = nachUebernahme(); const plan = buildSignalPlan(project); const aufgeloest = plan.intergreens.get(intergreenKey('sg-k-nord', 'sg-k-west')); expect(konflikt(project, 'sg-k-nord', 'sg-k-west').clearingDistance).toBeCloseTo(52, 2); expect(aufgeloest?.calculation?.clearingSpeed).toBe(RILSA_DEFAULTS.clearingSpeed.kfzStraight); expect(aufgeloest?.value).toBe(9); }); it('bringt Bemerkung und gedruckte Zwischenzeit an allen 72 Beziehungen zur Deckung', () => { const project = nachUebernahme(); const plan = buildSignalPlan(project); const abweichend = project.conflicts .map((c) => ({ beziehung: `${c.fromId} -> ${c.toId}`, vermerk: vermerkteZwischenzeit(c.note), plan: plan.intergreens.get(intergreenKey(c.fromId, c.toId))?.value, })) .filter((z) => z.vermerk !== z.plan); expect(abweichend).toEqual([]); }); }); describe('A1/3: Die Fahrbeziehung uebersteht Speichern und Laden', () => { it('traegt "rechts" durch die Datei und rechnet danach dieselben 9 s', () => { const project = nachUebernahme(); const geladen = loadProject(JSON.parse(JSON.stringify(project)), DATUM); expect(geladen.issues.filter((i) => i.severity === 'fehler')).toEqual([]); expect(konflikt(geladen.project, 'sg-k-nord', 'sg-k-ost').raeumbeziehung).toBe('rechts'); // Zwoelf seit 5.24.0 (siehe A1/1); durch die Datei gehen alle zwoelf. expect(geladen.project.conflicts.filter((c) => 'raeumbeziehung' in c)).toHaveLength(12); expect( buildSignalPlan(geladen.project).intergreens.get(intergreenKey('sg-k-nord', 'sg-k-ost')) ?.value, ).toBe(9); }); it('meldet eine unlesbare Angabe und faellt auf die Fahrbeziehung der Gruppe zurueck', () => { const project = nachUebernahme(); const roh = JSON.parse(JSON.stringify(project)) as { conflicts: { fromId: string; toId: string; raeumbeziehung?: unknown }[]; }; const eintrag = roh.conflicts.find((c) => c.fromId === 'sg-k-nord' && c.toId === 'sg-k-ost'); eintrag!.raeumbeziehung = 'seitwaerts'; const gelesen = parseProject(roh, DATUM); expect( gelesen.issues.some((i) => i.path.endsWith('.raeumbeziehung') && i.severity !== 'hinweis'), ).toBe(true); expect(konflikt(gelesen.project, 'sg-k-nord', 'sg-k-ost').raeumbeziehung).toBeUndefined(); }); it('rechnet eine Beziehung ohne das Feld unveraendert mit der Fahrbeziehung der Gruppe', () => { // Rueckfall fuer jede Datei aelterer Fassung: fehlt die Angabe, gilt die // Fahrbeziehung der Signalgruppe - das Verhalten bis zu diesem Befund. const project = beispiel(); expect(project.conflicts.every((c) => !('raeumbeziehung' in c))).toBe(true); const plan = buildSignalPlan(project); const aufgeloest = plan.intergreens.get(intergreenKey('sg-k-nord', 'sg-k-ost')); expect(aufgeloest?.calculation?.clearingSpeed).toBe(RILSA_DEFAULTS.clearingSpeed.kfzStraight); expect(aufgeloest?.value).toBe(5); }); }); describe('A1/4: Die Entscheidung faellt genau einmal', () => { it('raeumt an allen 72 Beziehungen mit der Fahrbeziehung, die massgebendeRaeumbeziehung nennt', () => { // Die Wache gegen einen Rueckfall auf `from.movement` im Planaufbau: Wer // die Entscheidung dort ein zweites Mal nachbaut, faellt hier auf. const project = nachUebernahme(); const plan = buildSignalPlan(project); const abweichend = project.conflicts .map((c) => { const from = project.signalGroups.find((g) => g.id === c.fromId)!; return { beziehung: `${c.fromId} -> ${c.toId}`, plan: plan.intergreens.get(intergreenKey(c.fromId, c.toId))?.calculation?.clearingSpeed, erwartet: clearingSpeedFor( { mode: from.mode, movement: massgebendeRaeumbeziehung(from, c), vZul: from.vZul, ...(from.reducedMobility ? { reducedMobility: true } : {}), ...(c.engerRadius === true ? { engerRadius: true } : {}), }, plan.defaults, ), }; }) .filter((z) => z.plan !== z.erwartet); expect(abweichend).toEqual([]); // Und an den zwoelf Beziehungen mit Feld ist es eine ANDERE Fahrbeziehung // als die der Gruppe - sonst pruefte der Vergleich oben nichts. const mitFeld = project.conflicts.filter((c) => 'raeumbeziehung' in c); expect(mitFeld).toHaveLength(12); for (const c of mitFeld) { const from = project.signalGroups.find((g) => g.id === c.fromId)!; expect(massgebendeRaeumbeziehung(from, c)).not.toBe(from.movement); } }); it('faellt ohne Feld auf die Fahrbeziehung der Signalgruppe zurueck', () => { const project = beispiel(); const gruppe = project.signalGroups.find((g) => g.id === 'sg-k-nord')!; const ohne = konflikt(project, 'sg-k-nord', 'sg-k-ost'); expect('raeumbeziehung' in ohne).toBe(false); expect(massgebendeRaeumbeziehung(gruppe, ohne)).toBe(gruppe.movement); expect(massgebendeRaeumbeziehung(gruppe, { ...ohne, raeumbeziehung: 'rechts' })).toBe('rechts'); }); }); describe('A1/5: Bleibt das Feld nach einer Handeingabe stehen, ist das nicht immer die laengere Seite', () => { /* * Der Spiegelfall zur Beispielplanung: Traegt die Sammelgruppe 'rechts' und * gibt der gebuendelte GERADEAUSstrom den Ausschlag, speichert die * Uebernahme `raeumbeziehung: 'geradeaus'`. Bleibt das Feld danach stehen, * raeumt der Plan mit 10,0 m/s, waehrend die Gruppe allein 7,0 m/s ergaebe - * die KUERZERE Seite. * * Diese Faelle belegen den Wortlaut an `Conflict.raeumbeziehung`; der * Kommentar sagte vor der Fassung 5.11.0 unbedingt "die laengere und damit * sichere Seite" zu. */ function spiegelfall(): Project { const basis = beispiel(); const gedreht: Project = { ...basis, signalGroups: basis.signalGroups.map((g) => g.id === 'sg-k-nord' ? { ...g, movement: 'rechts' as const } : g, ), }; const ableitung = leiteAb(gedreht, gedreht.lageplan); return uebernimmWege( gedreht, gedreht.lageplan, ableitung, ableitung.wege.map(wegSchluessel), DATUM, ).project; } it('speichert "geradeaus", wo der Geradeausstrom einer Rechtsabbiegergruppe gewinnt', () => { const project = spiegelfall(); const west = konflikt(project, 'sg-k-nord', 'sg-k-west'); expect(project.signalGroups.find((g) => g.id === 'sg-k-nord')?.movement).toBe('rechts'); expect(west.note).toContain('"Nord geradeaus"'); expect(west.raeumbeziehung).toBe('geradeaus'); const aufgeloest = buildSignalPlan(project).intergreens.get( intergreenKey('sg-k-nord', 'sg-k-west'), ); expect(aufgeloest?.calculation?.clearingSpeed).toBe(RILSA_DEFAULTS.clearingSpeed.kfzStraight); expect(aufgeloest?.value).toBe(9); // Ohne das Feld raeumte derselbe Weg mit der Fahrbeziehung der Gruppe, // also mit 7,0 m/s: 12 s. Die gespeicherte Fahrbeziehung ist hier die // KUERZERE - und zugleich die fachlich richtige, denn den Weg faehrt der // Geradeausstrom. const ohneFeld: Project = { ...project, conflicts: project.conflicts.map((c) => { const { raeumbeziehung: _weg, ...ohne } = c; return ohne; }), }; expect( buildSignalPlan(ohneFeld).intergreens.get(intergreenKey('sg-k-nord', 'sg-k-west'))?.value, ).toBe(12); }); it('rechnet nach einer Handeingabe am Raeumweg kuerzer als ohne das Feld', () => { const project = spiegelfall(); const vonHand = (p: Project, mitFeld: boolean): Project => ({ ...p, conflicts: p.conflicts.map((c) => { if (!(c.fromId === 'sg-k-nord' && c.toId === 'sg-k-west')) return c; const { raeumbeziehung: _weg, ...ohne } = c; return aendereKonflikt(mitFeld ? c : ohne, { clearingDistance: 60 }); }), }); const mit = buildSignalPlan(vonHand(project, true)).intergreens.get( intergreenKey('sg-k-nord', 'sg-k-west'), ); const ohne = buildSignalPlan(vonHand(project, false)).intergreens.get( intergreenKey('sg-k-nord', 'sg-k-west'), ); expect(mit?.calculation?.clearingSpeed).toBe(RILSA_DEFAULTS.clearingSpeed.kfzStraight); expect(ohne?.calculation?.clearingSpeed).toBe(RILSA_DEFAULTS.clearingSpeed.kfzTurning); expect(mit!.value).toBeLessThan(ohne!.value); expect([mit?.value, ohne?.value]).toEqual([10, 13]); }); it('laesst das Feld auch bei den Sammelwegen stehen (setzeAlleWege)', () => { // `setzeAlleWege` ueberschreibt die Wege ALLER Beziehungen ueber // `aendereKonflikt`; die Fahrbeziehung bleibt dabei ebenso stehen. const project = setzeAlleWege(spiegelfall(), 60, 12, false); const west = konflikt(project, 'sg-k-nord', 'sg-k-west'); expect(west.clearingDistance).toBe(60); expect(west.raeumbeziehung).toBe('geradeaus'); expect(west.wegnachweis.herkunft).toBe('handeingabe'); expect( buildSignalPlan(project).intergreens.get(intergreenKey('sg-k-nord', 'sg-k-west'))?.calculation ?.clearingSpeed, ).toBe(RILSA_DEFAULTS.clearingSpeed.kfzStraight); }); }); describe('A1/6: Verwirft ein aelterer Programmstand das Feld, bleibt der Vermerk', () => { /* * Beleg fuer den Absatz "KEIN SCHEMAAUFSTIEG" an `Conflict.raeumbeziehung`: * Der Verlust ist weder unwiederbringlich noch spurlos. Nachgestellt wird * ein aelterer Programmstand, indem der Schluessel aus der Datei entfernt * wird - genau das tut er beim Einlesen und beim naechsten Speichern. */ function ohneFeld(project: Project): Project { return { ...project, conflicts: project.conflicts.map((c) => { const { raeumbeziehung: _weg, ...ohne } = c; return ohne; }), }; } it('nennt die Bemerkung weiter 9 s, wo die verkuerzte Zwischenzeit 7 s druckt', () => { const alt = ohneFeld(nachUebernahme()); const plan = buildSignalPlan(alt); const ost = konflikt(alt, 'sg-k-nord', 'sg-k-ost'); expect(vermerkteZwischenzeit(ost.note)).toBe(9); expect(plan.intergreens.get(intergreenKey('sg-k-nord', 'sg-k-ost'))?.value).toBe(7); // Der Widerspruch steht an genau den Beziehungen, an denen der // Rechtsabbieger den Ausschlag gibt und die Zahl aendert - sichtbar in // derselben gedruckten Zeile, in der die Bemerkung neben der Zwischenzeit // steht. // // NACHGEZOGEN MIT FASSUNG 5.43.0 (Gruppe Furt): Hier standen vier. Die // vier Beziehungen "Kfz raeumt vor der Furt, die der Rechtsabbieger beim // Ausfahren quert" (K Nord -> F West und die drei gleichartigen) blieben // bis dahin mit und ohne Feld bei 0 s - der Einfahrweg des Fussgaengers // wurde ab der entfernten Bordkante gemessen (11 m, te 7,3 s). Seit er // vom Fahrbahnrand aus mit 0 m gilt, raeumt der Rechtsabbieger dort mit // 6 s (3 + (9,95 + 6)/7,0 = 5,28 s), geradeaus gerechnet mit 5 s // (3 + 15,95/10,0 = 4,60 s) - der Widerspruch steht jetzt auch dort. expect( alt.conflicts .filter( (c) => vermerkteZwischenzeit(c.note) !== plan.intergreens.get(intergreenKey(c.fromId, c.toId))?.value, ) .map((c) => `${c.fromId}->${c.toId}`), ).toEqual([ 'sg-k-nord->sg-k-ost', 'sg-k-nord->sg-f-west', 'sg-k-ost->sg-k-sued', 'sg-k-ost->sg-f-nord', 'sg-k-sued->sg-k-west', 'sg-k-sued->sg-f-ost', 'sg-k-west->sg-k-nord', 'sg-k-west->sg-f-sued', ]); }); it('setzt die naechste Uebernahme aus demselben Lageplan wieder her', () => { const alt = ohneFeld(nachUebernahme()); expect(alt.conflicts.some((c) => 'raeumbeziehung' in c)).toBe(false); const ableitung = leiteAb(alt, alt.lageplan); const { project } = uebernimmWege( alt, alt.lageplan, ableitung, ableitung.wege.map(wegSchluessel), DATUM, ); expect(project.conflicts.filter((c) => 'raeumbeziehung' in c)).toHaveLength(12); expect(konflikt(project, 'sg-k-nord', 'sg-k-ost').raeumbeziehung).toBe('rechts'); expect( buildSignalPlan(project).intergreens.get(intergreenKey('sg-k-nord', 'sg-k-ost'))?.value, ).toBe(9); }); }); describe('A1/S1: Die Beispieldatei bleibt das Pruefstueck fuer den Altdatei-Pfad', () => { it('traegt weiterhin schemaVersion 7 und laeuft durch die ganze Migrationskette', () => { const roh = JSON.parse(readFileSync(DATEI, 'utf8')) as { schemaVersion?: number }; expect(roh.schemaVersion).toBe(7); const geladen = loadProject(roh, DATUM); expect(geladen.migrated).toBe(false); expect(geladen.sourceVersion).toBe('7'); expect(geladen.issues.filter((i) => i.severity === 'fehler')).toEqual([]); // Der Aufstieg 7 -> aktuell meldet den fehlenden Wegnachweis als Hinweis. expect(geladen.issues.some((i) => i.path === 'conflicts' && i.severity === 'hinweis')).toBe( true, ); expect(geladen.project.conflicts).toHaveLength(72); }); });