using LehrerApp.Core.Models; using LehrerApp.Core.Services; using Xunit; namespace LehrerApp.Tests; public sealed class AttendanceBalanceServiceTests { private readonly AttendanceBalanceService _service = new(); private static readonly DateOnly From = new(2025, 8, 1); private static readonly DateOnly To = new(2026, 7, 31); [Fact] public void Calculate_ZaehltJedenStatusKorrekt() { var entries = new List<(DateOnly, AttendanceStatus?)> { (new DateOnly(2025, 9, 1), AttendanceStatus.Present), (new DateOnly(2025, 9, 2), AttendanceStatus.Present), (new DateOnly(2025, 9, 3), AttendanceStatus.Excused), (new DateOnly(2025, 9, 4), AttendanceStatus.Unexcused), (new DateOnly(2025, 9, 5), AttendanceStatus.Truant), (new DateOnly(2025, 9, 6), AttendanceStatus.OtherSchoolEvent), (new DateOnly(2025, 9, 7), AttendanceStatus.ExcusePending), (new DateOnly(2025, 9, 8), AttendanceStatus.Late), (new DateOnly(2025, 9, 9), AttendanceStatus.SignificantlyLate), (new DateOnly(2025, 9, 10), AttendanceStatus.LeftDuringClass), (new DateOnly(2025, 9, 11), AttendanceStatus.LearningIsland), (new DateOnly(2025, 9, 12), AttendanceStatus.Suspended), }; var balance = _service.Calculate(entries, From, To); Assert.Equal(12, balance.TotalChecked); Assert.Equal(4, balance.Present); Assert.Equal(1, balance.Excused); Assert.Equal(3, balance.Unexcused); // Unexcused + Truant + im Unterricht abgängig Assert.Equal(3, balance.SchoolEvent); // Schulveranstaltung + Lerninsel + Suspendierung Assert.Equal(1, balance.ExcusePending); Assert.Equal(41.7, balance.AbsenceRatePercent); } [Fact] public void Calculate_IgnoriertEintraegeOhneStatusUndAusserhalbDesZeitraums() { var entries = new List<(DateOnly, AttendanceStatus?)> { (new DateOnly(2025, 9, 1), null), (new DateOnly(2024, 9, 1), AttendanceStatus.Unexcused), // vor dem Zeitraum (new DateOnly(2025, 9, 1), AttendanceStatus.Present), }; var balance = _service.Calculate(entries, From, To); Assert.Equal(1, balance.TotalChecked); Assert.Equal(1, balance.Present); } [Fact] public void Calculate_SchulischVeranlassteAbwesenheitZaehltNichtAlsFehlzeit() { var entries = new List<(DateOnly, AttendanceStatus?)> { (new DateOnly(2025, 9, 1), AttendanceStatus.OtherSchoolEvent), (new DateOnly(2025, 9, 2), AttendanceStatus.Present), }; var balance = _service.Calculate(entries, From, To); Assert.Equal(0.0, balance.AbsenceRatePercent); } [Fact] public void ExceedsThreshold_UeberZwanzigProzentFehlquote_IstTrue() { var entries = Enumerable.Range(0, 10) .Select(i => (From.AddDays(i), (AttendanceStatus?)(i < 3 ? AttendanceStatus.Unexcused : AttendanceStatus.Present))) .ToList(); var balance = _service.Calculate(entries, From, To); Assert.Equal(30.0, balance.AbsenceRatePercent); Assert.True(balance.ExceedsThreshold); } [Fact] public void ExceedsThreshold_KeineKontrolliertenStunden_IstFalseUndRateIstNull() { var balance = _service.Calculate([], From, To); Assert.Equal(0.0, balance.AbsenceRatePercent); Assert.False(balance.ExceedsThreshold); } }