Add full-length Step 3 Day 1 simulation

This commit is contained in:
hz
2026-07-14 16:54:11 -05:00
parent 8aaf7d61e9
commit 206a5c0124
19 changed files with 1520 additions and 32 deletions
@@ -0,0 +1,87 @@
using QuestionsApi.Models;
using QuestionsApi.Services;
using Xunit;
namespace QuestionsApi.Tests;
public sealed class ExamSimulationTests
{
[Fact]
public void DayOneFormHasOfficialItemAndBlockCounts()
{
Assert.Equal(232, ExamSimulationStore.BlockSizes.Sum());
Assert.Equal(12, ExamSimulationStore.BlockSizes.Length);
Assert.All(ExamSimulationStore.BlockSizes, size => Assert.InRange(size, 18, 20));
}
[Fact]
public void BlueprintTargetsFillTheCompleteForm()
{
var targets = ExamSimulationStore.BuildTargets();
Assert.Equal(ExamSimulationStore.RequiredQuestions, targets.Values.Sum());
Assert.Equal(14, targets.Count);
Assert.All(targets.Values, count => Assert.True(count > 0));
}
[Fact]
public void MoreCorrectAnswersProduceAHigherPrediction()
{
var items = BuildItems(100);
var questions = BuildQuestions(100);
var allCorrect = items.ToDictionary(
item => item.QuestionExternalId,
item => new ExamSimulationResponse { QuestionExternalId = item.QuestionExternalId, ChoiceId = "A" });
var allWrong = items.ToDictionary(
item => item.QuestionExternalId,
item => new ExamSimulationResponse { QuestionExternalId = item.QuestionExternalId, ChoiceId = "B" });
var passing = ExamSimulationScoring.Calculate(items, allCorrect, questions);
var failing = ExamSimulationScoring.Calculate(items, allWrong, questions);
Assert.True(passing.PredictedScore > failing.PredictedScore);
Assert.True(passing.PassProbability > failing.PassProbability);
Assert.Equal(100, passing.Correct);
Assert.Equal(0, failing.Correct);
}
[Fact]
public void SeenItemsAreReportedAndCarryLessScoringWeight()
{
var questions = BuildQuestions(20);
var unseen = BuildItems(20);
var seen = BuildItems(20, wasSeen: true);
var responses = unseen.ToDictionary(
item => item.QuestionExternalId,
item => new ExamSimulationResponse { QuestionExternalId = item.QuestionExternalId, ChoiceId = "A" });
var unseenScore = ExamSimulationScoring.Calculate(unseen, responses, questions);
var seenScore = ExamSimulationScoring.Calculate(seen, responses, questions);
Assert.Equal(0, unseenScore.SeenCount);
Assert.Equal(20, seenScore.SeenCount);
Assert.True(unseenScore.AbilityElo > seenScore.AbilityElo);
}
[Theory]
[InlineData("Cardiovascular System", "Cardiovascular")]
[InlineData("Professionalism and Ethics", "Social Sciences")]
[InlineData("Endocrine, Diabetes & Metabolism", "Endocrine")]
public void QuestionsMapToExpectedBlueprintCategory(string system, string expected)
{
Assert.Equal(expected, ExamSimulationStore.BlueprintCategory(new Question { System = system }));
}
private static List<ExamSimulationItem> BuildItems(int count, bool wasSeen = false) =>
Enumerable.Range(0, count).Select(index => new ExamSimulationItem
{
QuestionExternalId = $"question-{index}",
QuestionElo = 1000,
WasSeen = wasSeen,
BlueprintCategory = "Cardiovascular"
}).ToList();
private static Dictionary<string, Question> BuildQuestions(int count) =>
Enumerable.Range(0, count).ToDictionary(
index => $"question-{index}",
index => new Question { ExternalId = $"question-{index}", CorrectChoiceId = "A" });
}