using Microsoft.Extensions.Configuration; using QuestionsApi.Models; using QuestionsApi.Services; using Xunit; namespace QuestionsApi.Tests; public sealed class DynamicQuestionTests { [Fact] public void Generator_IsDeterministic_AndPreservesCorrectAnswer() { var blueprint = Blueprint(); var generator = new DynamicQuestionGenerator(); var first = generator.Generate(blueprint, "same-seed", "one").Question; var second = generator.Generate(blueprint, "same-seed", "two").Question; Assert.Equal(first.Stem, second.Stem); Assert.Equal(first.Choices.Select(value => value.Text), second.Choices.Select(value => value.Text)); Assert.Equal("correct", first.CorrectChoiceId); Assert.Null(DynamicQuestionGenerator.Validate(blueprint)); } [Fact] public void SignedTokens_AreUserBound_AndRejectTampering() { var configuration = new ConfigurationBuilder() .AddInMemoryCollection(new Dictionary { ["DynamicQuestions:SigningKey"] = "unit-test-key" }) .Build(); var service = new DynamicQuestionTokenService(configuration); var claims = new DynamicGenerationClaims { UserId = "user-one", BlueprintExternalId = "test", BlueprintVersion = 1, ObjectiveId = "objective", Seed = "seed", Nonce = "nonce", GeneratorVersion = 1, ExpiresUtc = DateTime.UtcNow.AddMinutes(5) }; var token = service.SignGeneration(claims); Assert.Equal("seed", service.ReadGeneration(token, "user-one").Seed); Assert.Throws(() => service.ReadGeneration(token, "user-two")); Assert.ThrowsAny(() => service.ReadGeneration(token + "x", "user-one")); } private static DynamicQuestionBlueprint Blueprint() => new() { ExternalId = "test", ObjectiveId = "objective", StemTemplate = "Patient age {{age}}.", LeadIn = "What is correct?", Variables = new() { ["age"] = new DynamicVariableDefinition { Minimum = 20, Maximum = 80, Step = 1 } }, CorrectAnswer = new DynamicAnswerTemplate { Id = "correct", TextTemplate = "Correct" }, Distractors = [ new() { Id = "a", TextTemplate = "A" }, new() { Id = "b", TextTemplate = "B" }, new() { Id = "c", TextTemplate = "C" }, new() { Id = "d", TextTemplate = "D" } ], References = [new QuestionReference { Title = "Reference", Url = "https://example.test" }] }; }