79 lines
3.5 KiB
C#
79 lines
3.5 KiB
C#
using QuestionsApi.Models;
|
|
using QuestionsApi.Services;
|
|
using Xunit;
|
|
|
|
namespace QuestionsApi.Tests;
|
|
|
|
public sealed class DailyMissionPolicyTests
|
|
{
|
|
private static readonly DateTime Now = new(2026, 8, 1, 12, 0, 0, DateTimeKind.Utc);
|
|
|
|
[Fact]
|
|
public void DueReviewsArePrioritizedAndCappedAtTen()
|
|
{
|
|
var candidates = DailyMissionStore.BuildCandidates(Dashboard(17), Questions(), Now);
|
|
Assert.Equal(DailyMissionTypes.DueReviews, candidates[0].Type);
|
|
Assert.Equal(10, candidates[0].Target);
|
|
}
|
|
|
|
[Fact]
|
|
public void NoReviewsFallsBackToCoverageGaps()
|
|
{
|
|
var candidates = DailyMissionStore.BuildCandidates(Dashboard(0), Questions(), Now);
|
|
Assert.DoesNotContain(candidates, value => value.Type == DailyMissionTypes.DueReviews);
|
|
Assert.True(candidates.Count >= 3);
|
|
Assert.Contains(candidates.Take(3), value => value.Type == DailyMissionTypes.DifficultyFourCorrect);
|
|
Assert.All(candidates.Where(value => value.Type == DailyMissionTypes.WeakSystemCorrect),
|
|
value => Assert.Equal(3, value.Target));
|
|
}
|
|
|
|
[Fact]
|
|
public void HardMissionRequiresThreeEligiblePublishedQuestions()
|
|
{
|
|
var questions = Questions();
|
|
questions.RemoveAll(value => value.Step3System == Step3SystemCatalog.All[1] && value.Difficulty >= 4);
|
|
var candidates = DailyMissionStore.BuildCandidates(Dashboard(0), questions, Now);
|
|
Assert.DoesNotContain(candidates, value => value.Type == DailyMissionTypes.DifficultyFourCorrect
|
|
&& value.Step3System == Step3SystemCatalog.All[1]);
|
|
}
|
|
|
|
[Fact]
|
|
public void QuestionProgressRequiresCorrectEligibleActivityAfterMissionStart()
|
|
{
|
|
var system = Step3SystemCatalog.All[0];
|
|
var mission = new DailyMission
|
|
{
|
|
Type = DailyMissionTypes.DifficultyFourCorrect, Step3System = system, StartsUtc = Now
|
|
};
|
|
var question = new Question { Step3System = system, Difficulty = 4 };
|
|
Assert.True(DailyMissionStore.QualifiesQuestion(mission,
|
|
new QuestionAttempt { IsCorrect = true, CreatedUtc = Now }, question));
|
|
Assert.False(DailyMissionStore.QualifiesQuestion(mission,
|
|
new QuestionAttempt { IsCorrect = false, CreatedUtc = Now }, question));
|
|
Assert.False(DailyMissionStore.QualifiesQuestion(mission,
|
|
new QuestionAttempt { IsCorrect = true, IsSkip = true, CreatedUtc = Now }, question));
|
|
Assert.False(DailyMissionStore.QualifiesQuestion(mission,
|
|
new QuestionAttempt { IsCorrect = true, CreatedUtc = Now.AddSeconds(-1) }, question));
|
|
question.Difficulty = 3;
|
|
Assert.False(DailyMissionStore.QualifiesQuestion(mission,
|
|
new QuestionAttempt { IsCorrect = true, CreatedUtc = Now }, question));
|
|
}
|
|
|
|
private static DashboardResponse Dashboard(int pending) => new()
|
|
{
|
|
MissedQuestionCount = pending,
|
|
Systems = Step3SystemCatalog.All.Take(4).Select((system, index) => new DashboardSystemPerformance
|
|
{
|
|
System = system, MasteryState = index == 0 ? MasteryStates.Unseen : MasteryStates.Learning,
|
|
MasteryScore = index * 10, DistinctQuestions = index, Difficulty4PlusSuccessful = index
|
|
}).ToList()
|
|
};
|
|
|
|
private static List<Question> Questions() => Step3SystemCatalog.All.Take(4)
|
|
.SelectMany(system => Enumerable.Range(0, 6).Select(index => new Question
|
|
{
|
|
ExternalId = $"{system}-{index}", Step3System = system, Difficulty = index < 3 ? 4 : 2,
|
|
IsPublished = true
|
|
})).ToList();
|
|
}
|