using LehrerApp.Core.Models; using LehrerApp.Core.Services; using LehrerApp.Desktop.ViewModels.Groups; using Xunit; namespace LehrerApp.Desktop.Tests; /// Tests für die Serienerzeugung von Stunden aus dem Stundenplan (4.2.5). public sealed class GenerateLessonSeriesDialogViewModelTests { private static GenerateLessonSeriesDialogViewModel BuildVm(FakeTimetableSlots slots, FakeLessons lessons, FakeSchoolHolidays holidays, Guid unitId, Guid groupId, DateOnly? from = null, DateOnly? to = null) { // Bewusst kein "using": SchoolCalendarSettingsService liest den Pfad nur bei Bedarf. var tempPath = System.IO.Path.Combine( System.IO.Path.GetTempPath(), $"lehrerapp-lessonseries-tests-{Guid.NewGuid():N}"); Directory.CreateDirectory(tempPath); var vm = new GenerateLessonSeriesDialogViewModel(slots, lessons, holidays, new PublicHolidayService(), new SchoolCalendarSettingsService(tempPath), unitId, groupId, null, null); if (from is not null) vm.FromDateText = from.Value.ToString("dd.MM.yyyy"); if (to is not null) vm.ToDateText = to.Value.ToString("dd.MM.yyyy"); return vm; } [Fact] public void Save_ErzeugtStundeProVorkommenDesWochentagsImZeitraum() { var groupId = Guid.NewGuid(); var unitId = Guid.NewGuid(); var from = new DateOnly(2025, 9, 1); var to = from.AddDays(13); // deckt zwei Vorkommen des Wochentags von "from" ab (Tag 0 und 7) var slots = new FakeTimetableSlots(); slots.Add(new TimetableSlot { GroupId = groupId, Weekday = from.DayOfWeek, PeriodNumber = 1 }); var lessons = new FakeLessons(); var vm = BuildVm(slots, lessons, new FakeSchoolHolidays(), unitId, groupId, from, to); vm.SaveCommand.Execute(null); Assert.NotNull(vm.Result); Assert.Equal(2, vm.Result!.Created); Assert.Equal(0, vm.Result.SkippedHoliday); Assert.Equal(0, vm.Result.SkippedExisting); Assert.All(lessons.GetByGroupAndRange(groupId, from, to), l => Assert.Equal(unitId, l.UnitId)); } [Fact] public void Save_UeberspringtTerminInSchulferien() { var groupId = Guid.NewGuid(); var unitId = Guid.NewGuid(); var from = new DateOnly(2025, 9, 1); var to = from.AddDays(13); var secondOccurrence = from.AddDays(7); var slots = new FakeTimetableSlots(); slots.Add(new TimetableSlot { GroupId = groupId, Weekday = from.DayOfWeek, PeriodNumber = 1 }); var holidays = new FakeSchoolHolidays(); holidays.Add(new SchoolHoliday { Name = "Ferien", StartDate = secondOccurrence, EndDate = secondOccurrence.AddDays(3) }); var vm = BuildVm(slots, new FakeLessons(), holidays, unitId, groupId, from, to); vm.SaveCommand.Execute(null); Assert.Equal(1, vm.Result!.Created); Assert.Equal(1, vm.Result.SkippedHoliday); } [Fact] public void Save_UeberspringtBereitsVorhandeneStundeAmSelbenDatumUndPeriode() { var groupId = Guid.NewGuid(); var unitId = Guid.NewGuid(); var from = new DateOnly(2025, 9, 1); var to = from.AddDays(13); var slots = new FakeTimetableSlots(); slots.Add(new TimetableSlot { GroupId = groupId, Weekday = from.DayOfWeek, PeriodNumber = 1 }); var lessons = new FakeLessons(); lessons.Add(new Lesson { UnitId = Guid.NewGuid(), GroupId = groupId, Date = from, LessonNumber = 1, Topic = "Bestehend" }); var vm = BuildVm(slots, lessons, new FakeSchoolHolidays(), unitId, groupId, from, to); vm.SaveCommand.Execute(null); Assert.Equal(1, vm.Result!.Created); Assert.Equal(1, vm.Result.SkippedExisting); Assert.Equal(2, lessons.GetByGroupAndRange(groupId, from, to).Count); // 1 alt + 1 neu } [Fact] public void Save_Doppelstunde_ErzeugtNurEineStundeVerankertAnDerErstenPeriode() { var groupId = Guid.NewGuid(); var unitId = Guid.NewGuid(); var from = new DateOnly(2025, 9, 1); var slots = new FakeTimetableSlots(); slots.Add(new TimetableSlot { GroupId = groupId, Weekday = from.DayOfWeek, PeriodNumber = 3 }); slots.Add(new TimetableSlot { GroupId = groupId, Weekday = from.DayOfWeek, PeriodNumber = 4 }); var lessons = new FakeLessons(); var vm = BuildVm(slots, lessons, new FakeSchoolHolidays(), unitId, groupId, from, from); vm.SaveCommand.Execute(null); Assert.Equal(1, vm.Result!.Created); var created = lessons.GetByGroupAndRange(groupId, from, from).Select(l => l.LessonNumber).ToList(); Assert.Equal([3], created); } [Fact] public void Save_Dreifachstunde_ErzeugtNurEineStundeVerankertAnDerErstenPeriode() { var groupId = Guid.NewGuid(); var unitId = Guid.NewGuid(); var from = new DateOnly(2025, 9, 1); var slots = new FakeTimetableSlots(); slots.Add(new TimetableSlot { GroupId = groupId, Weekday = from.DayOfWeek, PeriodNumber = 3 }); slots.Add(new TimetableSlot { GroupId = groupId, Weekday = from.DayOfWeek, PeriodNumber = 4 }); slots.Add(new TimetableSlot { GroupId = groupId, Weekday = from.DayOfWeek, PeriodNumber = 5 }); var lessons = new FakeLessons(); var vm = BuildVm(slots, lessons, new FakeSchoolHolidays(), unitId, groupId, from, from); vm.SaveCommand.Execute(null); Assert.Equal(1, vm.Result!.Created); var created = lessons.GetByGroupAndRange(groupId, from, from).Select(l => l.LessonNumber).ToList(); Assert.Equal([3], created); } [Fact] public void Save_ZweiGetrennteEinzelstundenAmSelbenTagBleibenGetrennt() { // Perioden 1 und 3 sind NICHT aufeinanderfolgend (Periode 2 fehlt) — keine Doppelstunde, // beide bekommen eine eigene Lesson. var groupId = Guid.NewGuid(); var unitId = Guid.NewGuid(); var from = new DateOnly(2025, 9, 1); var slots = new FakeTimetableSlots(); slots.Add(new TimetableSlot { GroupId = groupId, Weekday = from.DayOfWeek, PeriodNumber = 1 }); slots.Add(new TimetableSlot { GroupId = groupId, Weekday = from.DayOfWeek, PeriodNumber = 3 }); var lessons = new FakeLessons(); var vm = BuildVm(slots, lessons, new FakeSchoolHolidays(), unitId, groupId, from, from); vm.SaveCommand.Execute(null); Assert.Equal(2, vm.Result!.Created); var created = lessons.GetByGroupAndRange(groupId, from, from).Select(l => l.LessonNumber).OrderBy(n => n).ToList(); Assert.Equal([1, 3], created); } [Fact] public void Save_OhneStundenplanEintrag_SetztFehlerUndErzeugtNichts() { var groupId = Guid.NewGuid(); var vm = BuildVm(new FakeTimetableSlots(), new FakeLessons(), new FakeSchoolHolidays(), Guid.NewGuid(), groupId, new DateOnly(2025, 9, 1), new DateOnly(2025, 9, 14)); vm.SaveCommand.Execute(null); Assert.Null(vm.Result); Assert.NotEqual("", vm.DateError); } [Fact] public void Save_UngueltigesDatumsformat_SetztFehler() { var vm = BuildVm(new FakeTimetableSlots(), new FakeLessons(), new FakeSchoolHolidays(), Guid.NewGuid(), Guid.NewGuid()); vm.FromDateText = "keinDatum"; vm.SaveCommand.Execute(null); Assert.Null(vm.Result); Assert.NotEqual("", vm.DateError); } [Fact] public void Save_EndeVorBeginn_SetztFehler() { var vm = BuildVm(new FakeTimetableSlots(), new FakeLessons(), new FakeSchoolHolidays(), Guid.NewGuid(), Guid.NewGuid(), new DateOnly(2025, 9, 10), new DateOnly(2025, 9, 1)); vm.SaveCommand.Execute(null); Assert.Null(vm.Result); Assert.NotEqual("", vm.DateError); } }