import { describe, expect, it } from 'vitest'; import { ProjectStore } from '@/app/store'; import * as actions from '@/app/actions'; import { createStandardIntersectionProject } from '@/domain/model/factory'; /** * Eine Aenderung, die nichts aendert, ist keine Aenderung (Befund 59). * * Der Waechter in `update()` prueft nur, ob der Aenderungsschritt dasselbe * Objekt zurueckgibt. Die Aenderungsfunktionen in `actions.ts` liefern aber * immer ein neues Objekt - `updateSignalGroup` etwa baut Projekt und * Signalgruppenliste in jedem Fall neu auf. Kommt aus der Oberflaeche ein * bereits eingetragener Wert an - der Zahleneingabe passiert das bei jeder * begrenzten Eingabe -, entstand daraus ein Rueckgaengig-Schritt, der nichts * rueckgaengig macht, der Aenderungsmerker wurde gesetzt, und `touch()` schrieb * `meta.modifiedAt` hoch. * * Der letzte Punkt ist der schwerste: Das Aenderungsdatum wird als "Zuletzt * geändert" in die Planunterlage gedruckt. Der Quelltext nennt genau diese * Verfaelschung als Grund, `markDirty()` nicht ueber `update()` zu fuehren - * derselbe Massstab gilt fuer den Bedienweg. * * GEGEN DEN ALTSTAND schlagen die ersten drei Faelle fehl: der * Rueckgaengig-Schritt samt Merker, das hochgeschriebene `meta.modifiedAt` und * die eine Benachrichtigung, die keine sein duerfte. Die letzten drei sind die * Gegenprobe: Der Vergleich darf keine echte Aenderung verschlucken, auch keine * tief im Baum und keine, die nur ein Feld entfernt. */ const FESTES_DATUM = new Date('2026-01-01T00:00:00Z'); function store(): ProjectStore { return new ProjectStore(createStandardIntersectionProject('Prueffall', FESTES_DATUM)); } describe('Wertgleiche Aenderung', () => { it('erzeugt weder Rueckgaengig-Schritt noch Aenderungsmerker', () => { const s = store(); const gruppe = s.getProject().signalGroups[0]!; const vorher = s.getProject(); // Genau das, was die Signalgruppenansicht nach einer begrenzten Eingabe im // Feld "Fahrstreifen" schickt: Der Wert 0 wird auf die Untergrenze 1 // gezogen - und 1 steht dort bereits. s.update((p) => actions.updateSignalGroup(p, gruppe.id, { lanes: gruppe.lanes }), { label: 'Fahrstreifenzahl geändert', }); expect(s.getState().canUndo).toBe(false); expect(s.getState().undoLabel).toBe(null); expect(s.getState().dirty).toBe(false); // Der Stand bleibt derselbe - auch als Objekt, sonst zeichnete jede // Ansicht ohne Grund neu. expect(s.getProject()).toBe(vorher); }); it('schreibt das Aenderungsdatum des Projekts nicht hoch', () => { const s = store(); const gruppe = s.getProject().signalGroups[0]!; const vorher = s.getProject().meta.modifiedAt; s.update((p) => actions.updateSignalGroup(p, gruppe.id, { vZul: gruppe.vZul }), { label: 'Höchstgeschwindigkeit geändert', }); // Die Zahl geht als "Zuletzt geändert" in die Planunterlage. expect(s.getProject().meta.modifiedAt).toBe(vorher); }); it('benachrichtigt die Empfaenger nicht', () => { const s = store(); const gruppe = s.getProject().signalGroups[0]!; let gemeldet = 0; s.subscribe(() => { gemeldet += 1; }); s.update((p) => actions.updateSignalGroup(p, gruppe.id, { name: gruppe.name }), { label: 'Signalgruppe geaendert', }); expect(gemeldet).toBe(0); }); it('laesst eine echte Aenderung unveraendert durch', () => { const s = store(); const gruppe = s.getProject().signalGroups[0]!; s.update((p) => actions.updateSignalGroup(p, gruppe.id, { lanes: gruppe.lanes + 1 }), { label: 'Fahrstreifenzahl geändert', }); expect(s.getState().canUndo).toBe(true); expect(s.getState().dirty).toBe(true); expect(s.getProject().signalGroups[0]!.lanes).toBe(gruppe.lanes + 1); }); it('erkennt einen Unterschied tief im Baum', () => { const s = store(); const gruppe = s.getProject().signalGroups[0]!; const vorher = s.getProject().demands.find((d) => d.signalGroupId === gruppe.id)!; s.update((p) => actions.setDemand(p, gruppe.id, { volume: vorher.volume + 1 }), { label: 'Verkehrsstärke geändert', }); expect(s.getState().dirty).toBe(true); expect(s.getProject().demands.find((d) => d.signalGroupId === gruppe.id)!.volume).toBe( vorher.volume + 1, ); }); it('erkennt eine entfernte Angabe', () => { const s = store(); const gruppe = s.getProject().signalGroups[0]!; expect(s.getProject().demands.some((d) => d.signalGroupId === gruppe.id)).toBe(true); s.update((p) => actions.removeDemand(p, gruppe.id), { label: 'Verkehrsstärke entfernt' }); expect(s.getState().dirty).toBe(true); expect(s.getProject().demands.some((d) => d.signalGroupId === gruppe.id)).toBe(false); }); });