using QuestionsApi.Services.Flashcards; using Xunit; namespace QuestionsApi.Tests; public sealed class FlashcardClozeParserTests { [Fact] public void Parse_ExtractsAnswerAndHint() { var deletions = FlashcardClozeParser.Parse("The capital of France is {{c1::Paris::country capital}}."); var deletion = Assert.Single(deletions); Assert.Equal(1, deletion.Number); Assert.Equal("Paris", deletion.Answer); Assert.Equal("country capital", deletion.Hint); } [Fact] public void Parse_WithoutHint_LeavesHintNull() { var deletions = FlashcardClozeParser.Parse("{{c1::Paris}}"); Assert.Null(Assert.Single(deletions).Hint); } [Fact] public void DistinctClozeNumbers_DeduplicatesRepeatedNumbersAndSorts() { var text = "{{c2::b}} some text {{c1::a}} more {{c1::a-again}} {{c3::c}}"; var numbers = FlashcardClozeParser.DistinctClozeNumbers(text); Assert.Equal([1, 2, 3], numbers); } [Theory] [InlineData("no cloze markers here")] [InlineData("{{c0::invalid, number must be >= 1}}")] [InlineData("{{cX::not a number}}")] [InlineData("{{c1::}}")] public void HasValidCloze_IsFalse_ForMalformedOrMissingMarkers(string text) { Assert.False(FlashcardClozeParser.HasValidCloze(text)); } [Fact] public void HasValidCloze_IsTrue_WhenAtLeastOneValidMarkerExists() { Assert.True(FlashcardClozeParser.HasValidCloze("prefix {{c1::answer}} suffix")); } [Fact] public void RenderFront_HidesTargetNumber_RevealsOthers() { var text = "{{c1::Paris}} is the capital of {{c2::France}}"; var front1 = FlashcardClozeParser.RenderFront(text, 1); var front2 = FlashcardClozeParser.RenderFront(text, 2); Assert.Equal("[...] is the capital of France", front1); Assert.Equal("Paris is the capital of [...]", front2); } [Fact] public void RenderFront_UsesHintWhenPresent() { var text = "{{c1::Paris::capital city}} is in France"; var front = FlashcardClozeParser.RenderFront(text, 1); Assert.Equal("[capital city] is in France", front); } [Fact] public void RenderBack_RevealsAllAnswers() { var text = "{{c1::Paris}} is the capital of {{c2::France::hint}}"; var back = FlashcardClozeParser.RenderBack(text, 1); Assert.Equal("Paris is the capital of France", back); } [Fact] public void Parse_SupportsMultilineClozeContent() { var text = "Line one\n{{c1::multi\nline\nanswer}}\nLine two"; var deletion = Assert.Single(FlashcardClozeParser.Parse(text)); Assert.Equal("multi\nline\nanswer", deletion.Answer); } }