using LehrerApp.Core.Models; using LehrerApp.Data.Repositories; using Xunit; namespace LehrerApp.Data.Tests; // Baustein 3: prüft für jedes der ~19 "einfachen" Repositories (keine Kaskaden/Batches), dass // Save/Delete den OnChange-Hook mit dem korrekten EntityType auslöst. Tabellengetrieben statt // eine Datei pro Repository, um die immer gleiche Prüfung nicht 19x zu wiederholen. public sealed class ChangeHookMatrixTests { public static IEnumerable Cases() { yield return Case("SeatingPlan", nameof(SeatingPlan), db => { var group = new LearningGroup { Name = "8a", SchoolYear = "2025/26" }; new GroupRepository(db).Save(group); var repo = new SeatingPlanRepository(db); var plan = new SeatingPlan { GroupId = group.Id, Name = "Standard", Rows = 2, Columns = 2 }; repo.Save(plan); return (plan.Id, () => repo.Delete(plan.Id)); }); yield return Case("GroupMembership", nameof(GroupMembership), db => { var repo = new GroupMembershipRepository(db); var membership = new GroupMembership { StudentId = Guid.NewGuid(), GroupId = Guid.NewGuid() }; repo.Save(membership); return (membership.Id, () => repo.Delete(membership.Id)); }); yield return Case("GradingKeyTemplate", nameof(GradingKeyTemplate), db => { var repo = new GradingKeyTemplateRepository(db); var template = new GradingKeyTemplate { Name = "Standard", GradingSystem = GradingSystem.Points0To15 }; repo.Save(template); return (template.Id, () => repo.Delete(template.Id)); }); yield return Case("Grade", nameof(Grade), db => { var repo = new GradeRepository(db); var grade = new Grade { GroupId = Guid.NewGuid(), StudentId = Guid.NewGuid(), Value = "2" }; repo.Save(grade); return (grade.Id, () => repo.Delete(grade.Id)); }); yield return Case("GradingScheme", nameof(GradingScheme), db => { var repo = new GradingSchemeRepository(db); var scheme = new GradingScheme { GroupId = Guid.NewGuid(), ExamsPercent = 60, ParticipationPercent = 30, OtherPercent = 10 }; repo.Save(scheme); return (scheme.Id, () => repo.Delete(scheme.Id)); }); yield return Case("ReportGrade", nameof(ReportGrade), db => { var repo = new ReportGradeRepository(db); var grade = new ReportGrade { StudentId = Guid.NewGuid(), GroupId = Guid.NewGuid(), Period = "1. Halbjahr" }; repo.Save(grade); return (grade.Id, () => repo.Delete(grade.Id)); }); yield return Case("Unit", nameof(Unit), db => { var repo = new UnitRepository(db); var unit = new Unit { GroupId = Guid.NewGuid(), Title = "Einheit" }; repo.Save(unit); return (unit.Id, () => repo.Delete(unit.Id)); }); yield return Case("Lesson", nameof(Lesson), db => { var repo = new LessonRepository(db); var lesson = new Lesson { UnitId = Guid.NewGuid(), GroupId = Guid.NewGuid(), Date = new DateOnly(2026, 3, 12) }; repo.Save(lesson); return (lesson.Id, () => repo.Delete(lesson.Id)); }); yield return Case("WorkTask", nameof(WorkTask), db => { var repo = new WorkTaskRepository(db); var task = new WorkTask { Title = "Klausur korrigieren", Category = TaskCategory.Correction }; repo.Save(task); return (task.Id, () => repo.Delete(task.Id)); }); yield return Case("TimeEntry", nameof(TimeEntry), db => { var repo = new TimeEntryRepository(db); var entry = new TimeEntry { Date = new DateOnly(2026, 3, 12), DurationMinutes = 30, Category = "Vorbereitung" }; repo.Save(entry); return (entry.Id, () => repo.Delete(entry.Id)); }); yield return Case("ParticipationAspect", nameof(ParticipationAspect), db => { var repo = new ParticipationAspectRepository(db); var aspect = new ParticipationAspect { GroupId = Guid.NewGuid(), Key = "quality", Label = "Qualität" }; repo.Save(aspect); return (aspect.Id, () => repo.Delete(aspect.Id)); }); yield return Case("ParticipationSection", nameof(ParticipationSection), db => { var repo = new ParticipationSectionRepository(db); var section = new ParticipationSection { GroupId = Guid.NewGuid(), Label = "1. Abschnitt", StartDate = new DateOnly(2026, 3, 1), EndDate = new DateOnly(2026, 4, 1), }; repo.Save(section); return (section.Id, () => repo.Delete(section.Id)); }); yield return Case("Subject", nameof(Subject), db => { var repo = new SubjectRepository(db); var subject = new Subject { Name = "Chemie" }; repo.Save(subject); return (subject.Id, () => repo.Delete(subject.Id)); }); yield return Case("ShorthandCode", nameof(ShorthandCode), db => { var repo = new ShorthandCodeRepository(db); var code = new ShorthandCode { Code = "Tb", Label = "Tafelbild" }; repo.Save(code); return (code.Id, () => repo.Delete(code.Id)); }); yield return Case("AlternativeLessonPath", nameof(AlternativeLessonPath), db => { var repo = new AlternativeLessonPathRepository(db); var path = new AlternativeLessonPath { Name = "Kurzversion" }; repo.Save(path); return (path.Id, () => repo.Delete(path.Id)); }); yield return Case("TimetableSlot", nameof(TimetableSlot), db => { var repo = new TimetableSlotRepository(db); var slot = new TimetableSlot { GroupId = Guid.NewGuid(), Weekday = DayOfWeek.Tuesday, PeriodNumber = 3 }; repo.Save(slot); return (slot.Id, () => repo.Delete(slot.Id)); }); yield return Case("SchoolHoliday", nameof(SchoolHoliday), db => { var repo = new SchoolHolidayRepository(db); var holiday = new SchoolHoliday { Name = "Osterferien", StartDate = new DateOnly(2026, 3, 30), EndDate = new DateOnly(2026, 4, 10) }; repo.Save(holiday); return (holiday.Id, () => repo.Delete(holiday.Id)); }); yield return Case("SupervisionDuty", nameof(SupervisionDuty), db => { var repo = new SupervisionDutyRepository(db); var duty = new SupervisionDuty { Weekday = DayOfWeek.Monday, AfterPeriod = 2, Location = "Pausenhof" }; repo.Save(duty); return (duty.Id, () => repo.Delete(duty.Id)); }); yield return Case("SubstitutionEntry", nameof(SubstitutionEntry), db => { var repo = new SubstitutionEntryRepository(db); var entry = new SubstitutionEntry { Date = new DateOnly(2026, 3, 12), Kind = SubstitutionKind.Lesson, PeriodNumber = 3, Description = "Vertretung 8a" }; repo.Save(entry); return (entry.Id, () => repo.Delete(entry.Id)); }); } [Theory] [MemberData(nameof(Cases))] public void Save_LoestOnChangeMitKorrektemEntityTypeAus( string _, string entityType, Func setup) { using var db = new LiteDbContext(new MemoryStream()); var calls = new List<(string EntityType, string Operation)>(); db.OnChange = (type, id, op, payload) => calls.Add((type, op)); setup(db); Assert.Contains((entityType, "Save"), calls); } [Theory] [MemberData(nameof(Cases))] public void Delete_LoestOnChangeMitKorrektemEntityTypeAus( string _, string entityType, Func setup) { using var db = new LiteDbContext(new MemoryStream()); var (id, delete) = setup(db); var calls = new List<(string EntityType, string EntityId, string Operation)>(); db.OnChange = (type, entId, op, payload) => calls.Add((type, entId, op)); delete(); Assert.Contains((entityType, id.ToString(), "Delete"), calls); } private static object[] Case(string name, string entityType, Func setup) => [name, entityType, setup]; }