using Flashsharp.Domain; using Flashsharp.Scheduling; namespace Flashsharp.Tests; public sealed class SchedulerTests { static readonly DateTime Now = new(2026, 10, 22, 12, 0, 0, DateTimeKind.Utc); [Fact] public void First_good_review_is_due_tomorrow() { var next = Scheduler.Next(null, ReviewRating.Good, Now); Assert.Equal(86_400, next.IntervalSeconds); Assert.Equal(1, next.Repetitions); Assert.Equal(0, next.Lapses); Assert.Equal(2.5, next.Ease, 3); Assert.Equal(Now.AddDays(1), next.DueAt); } [Fact] public void Third_good_review_multiplies_by_ease() { var first = Scheduler.Next(null, ReviewRating.Good, Now); var second = Scheduler.Next(first, ReviewRating.Good, Now); var third = Scheduler.Next(second, ReviewRating.Good, Now); Assert.Equal(3 * 86_400, second.IntervalSeconds); Assert.Equal((int)(3 * 86_400 * 2.5), third.IntervalSeconds); Assert.Equal(3, third.Repetitions); } [Fact] public void Again_resets_repetitions_and_adds_a_lapse() { var first = Scheduler.Next(null, ReviewRating.Good, Now); var next = Scheduler.Next(first, ReviewRating.Again, Now); Assert.Equal(600, next.IntervalSeconds); Assert.Equal(0, next.Repetitions); Assert.Equal(1, next.Lapses); Assert.Equal(2.3, next.Ease, 3); } [Fact] public void Hard_does_not_reset_repetitions() { var first = Scheduler.Next(null, ReviewRating.Good, Now); var next = Scheduler.Next(first, ReviewRating.Hard, Now); Assert.Equal(2, next.Repetitions); Assert.Equal(0, next.Lapses); Assert.True(next.IntervalSeconds >= 43_200); } [Fact] public void Easy_starts_at_three_days_and_raises_ease() { var next = Scheduler.Next(null, ReviewRating.Easy, Now); Assert.Equal(3 * 86_400, next.IntervalSeconds); Assert.Equal(2.65, next.Ease, 3); } [Fact] public void Ease_never_goes_below_floor() { var state = Scheduler.Next(null, ReviewRating.Again, Now); for (var i = 0; i < 20; i++) { state = Scheduler.Next(state, ReviewRating.Again, Now); } Assert.Equal(1.3, state.Ease, 3); } }