using LehrerApp.Core.Models; using LehrerApp.Desktop.ViewModels.Groups; using Xunit; namespace LehrerApp.Desktop.Tests; public sealed class LessonDialogViewModelTests { private static LessonDialogViewModel BuildVm(Guid unitId, Guid groupId, Lesson? editing = null) => new(new FakeLessons(), new FakeShorthandCodes([]), new FakeAlternativeLessonPaths([]), unitId, groupId, [], [], editing); [Fact] public void AddPhase_FuegtZeileMitStandardwertenHinzu() { var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid()); vm.AddPhaseCommand.Execute(null); Assert.Single(vm.Phases); Assert.Equal(5, vm.Phases[0].DurationMinutes); Assert.False(vm.Phases[0].HasAlternativePath); } [Fact] public void RemovePhase_EntferntZeileUndAktualisiertGesamtdauer() { var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid()); vm.AddPhaseCommand.Execute(null); vm.Phases[0].DurationMinutes = 20; vm.Phases[0].RemoveCommand.Execute(null); Assert.Empty(vm.Phases); Assert.Equal("0 Minuten gesamt", vm.TotalDurationDisplay); } [Fact] public void MoveDown_VertauschtReihenfolgeDerPhasen() { var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid()); vm.AddPhaseCommand.Execute(null); vm.Phases[0].Name = "Einstieg"; vm.AddPhaseCommand.Execute(null); vm.Phases[1].Name = "Erarbeitung"; vm.Phases[0].MoveDownCommand.Execute(null); Assert.Equal("Erarbeitung", vm.Phases[0].Name); Assert.Equal("Einstieg", vm.Phases[1].Name); } [Fact] public void Shorthand_IstFreitextOhneErzwungeneVonNachStruktur() { // Kurzsymbol muss nicht immer ein Materialfluss-Pfeil sein — manchmal nur eine // Sozialform ohne Pfeil (z.B. "Plenum", "LDE"). var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid()); vm.AddPhaseCommand.Execute(null); var phase = vm.Phases[0]; phase.Shorthand = "Plenum"; Assert.Equal("Plenum", phase.Shorthand); phase.Shorthand = "AB001->S"; Assert.Equal("AB001->S", phase.Shorthand); } [Fact] public void ShorthandSuggestions_KombiniertKatalogUndBisherigeStundenwerte() { var codes = new FakeShorthandCodes([new ShorthandCode { Code = "Tb" }, new ShorthandCode { Code = "SH" }]); var vm = new LessonDialogViewModel(new FakeLessons(), codes, new FakeAlternativeLessonPaths([]), Guid.NewGuid(), Guid.NewGuid(), [], ["Plenum", "LDE", "Tb"], null); // "Tb" doppelt (Katalog + Historie), soll nur einmal erscheinen Assert.Equal(["LDE", "Plenum", "SH", "Tb"], vm.ShorthandSuggestions); } [Fact] public void RecomputeTimes_LeitetUhrzeitJePhaseAusBeginnUndKumulierterDauerAb() { var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid()); vm.StartTimeText = "12:00"; vm.AddPhaseCommand.Execute(null); vm.Phases[0].DurationMinutes = 15; vm.AddPhaseCommand.Execute(null); vm.Phases[1].DurationMinutes = 10; Assert.Equal("ab 12:00", vm.Phases[0].ComputedTimeDisplay); Assert.Equal("ab 12:15", vm.Phases[1].ComputedTimeDisplay); Assert.Equal("25 Minuten gesamt", vm.TotalDurationDisplay); } [Fact] public void RecomputeTimes_OhneBeginnBleibtDieUhrzeitanzeigeLeer() { var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid()); vm.AddPhaseCommand.Execute(null); vm.Phases[0].DurationMinutes = 15; Assert.Equal("", vm.Phases[0].ComputedTimeDisplay); } [Fact] public void Save_ErfordertThemaUndGueltigesDatum() { var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid()); vm.Topic = ""; vm.DateText = "keinDatum"; vm.SaveCommand.Execute(null); Assert.Null(vm.Result); Assert.NotEqual("", vm.TopicError); Assert.NotEqual("", vm.DateTextError); } [Fact] public void Save_UngueltigesBeginnFormatWirdAbgelehnt() { var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid()); vm.Topic = "Thema"; vm.DateText = "01.09.2025"; vm.StartTimeText = "nicht-uhrzeit"; vm.SaveCommand.Execute(null); Assert.Null(vm.Result); Assert.NotEqual("", vm.StartTimeTextError); } [Fact] public void Save_SetztUnitIdGroupIdUndUebernimmtPhasenUndBeginn() { var unitId = Guid.NewGuid(); var groupId = Guid.NewGuid(); var lessons = new FakeLessons(); var vm = new LessonDialogViewModel(lessons, new FakeShorthandCodes([]), new FakeAlternativeLessonPaths([]), unitId, groupId, [], [], null) { Topic = "Brechung", DateText = "01.09.2025", StartTimeText = "11:45", }; vm.AddPhaseCommand.Execute(null); vm.Phases[0].Name = "Einstieg"; vm.Phases[0].DurationMinutes = 10; vm.Phases[0].Activity = "Begrüßung"; vm.Phases[0].Material = "AB01"; vm.Phases[0].Shorthand = "L->AB01"; vm.SaveCommand.Execute(null); Assert.NotNull(vm.Result); Assert.Equal(unitId, vm.Result!.UnitId); Assert.Equal(groupId, vm.Result.GroupId); Assert.Equal(new TimeOnly(11, 45), vm.Result.StartTime); Assert.Single(vm.Result.Phases); Assert.Equal("Einstieg", vm.Result.Phases[0].Name); Assert.Equal(10, vm.Result.Phases[0].DurationMinutes); Assert.Equal("L->AB01", vm.Result.Phases[0].Shorthand); Assert.Single(lessons.GetByUnit(unitId)); } [Fact] public void HasAlternativePath_AngehaktOeffnetZuweisungUndUebernimmtErgebnis() { var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid()); var path = new AlternativeLessonPath { Name = "Kurzversion" }; vm.OnPickAlternativePath = _ => Task.FromResult(path); vm.AddPhaseCommand.Execute(null); vm.Phases[0].HasAlternativePath = true; Assert.True(vm.Phases[0].HasAlternativePath); Assert.Equal("Kurzversion", vm.Phases[0].AlternativePathName); Assert.Equal(path.Id, vm.Phases[0].AlternativePathId); } [Fact] public void HasAlternativePath_AbbruchDerZuweisungLaesstEsUnangehakt() { var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid()); vm.OnPickAlternativePath = _ => Task.FromResult(null); // abgebrochen vm.AddPhaseCommand.Execute(null); vm.Phases[0].HasAlternativePath = true; Assert.False(vm.Phases[0].HasAlternativePath); Assert.Null(vm.Phases[0].AlternativePathId); } [Fact] public void HasAlternativePath_AbwaehlenEntferntZuordnungOhneDialogZuOeffnen() { var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid()); var path = new AlternativeLessonPath { Name = "Kurzversion" }; var dialogCalls = 0; vm.OnPickAlternativePath = _ => { dialogCalls++; return Task.FromResult(path); }; vm.AddPhaseCommand.Execute(null); vm.Phases[0].HasAlternativePath = true; // zuweisen -> 1 Dialogaufruf vm.Phases[0].HasAlternativePath = false; // abwählen -> kein weiterer Dialogaufruf Assert.Equal(1, dialogCalls); Assert.False(vm.Phases[0].HasAlternativePath); Assert.Equal("", vm.Phases[0].AlternativePathName); Assert.Null(vm.Phases[0].AlternativePathId); } [Fact] public void Save_UebernimmtZugewiesenenAlternativePfad() { var vm = BuildVm(Guid.NewGuid(), Guid.NewGuid()); var path = new AlternativeLessonPath { Name = "Kurzversion" }; vm.OnPickAlternativePath = _ => Task.FromResult(path); vm.Topic = "Thema"; vm.DateText = "01.09.2025"; vm.AddPhaseCommand.Execute(null); vm.Phases[0].HasAlternativePath = true; vm.SaveCommand.Execute(null); Assert.Equal(path.Id, vm.Result!.Phases[0].AlternativePathId); } [Fact] public void AddPhaseInternal_LoestBestehendeZuordnungBeimBearbeitenAuf() { var path = new AlternativeLessonPath { Name = "Kurzversion" }; var alternativePaths = new FakeAlternativeLessonPaths([path]); var editing = new Lesson { Topic = "Thema", Date = new DateOnly(2025, 9, 1), Phases = [new LessonPhaseStep { Name = "Einstieg", AlternativePathId = path.Id }], }; var vm = new LessonDialogViewModel(new FakeLessons(), new FakeShorthandCodes([]), alternativePaths, Guid.NewGuid(), Guid.NewGuid(), [], [], editing); Assert.True(vm.Phases[0].HasAlternativePath); Assert.Equal("Kurzversion", vm.Phases[0].AlternativePathName); } }