72 lines
2.6 KiB
C#
72 lines
2.6 KiB
C#
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<string, string?> { ["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<ArgumentException>(() => service.ReadGeneration(token, "user-two"));
|
|
Assert.ThrowsAny<Exception>(() => 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" }]
|
|
};
|
|
}
|