import pytest import time from pathlib import Path from quiz.utils.importer import parse_markdown_question, import_question_file, ImportStats from quiz.models import Question, Option @pytest.mark.django_db @pytest.mark.import_tests class TestMarkdownParsing: """Test parsing of various Obsidian markdown question formats""" def test_parse_single_choice_question(self): """Test parsing standard single choice question (SCQ)""" content = """--- tags: [ah2, provfråga, frågetyp/scq, anatomi] date: 2022-01-15 --- What is the correct answer? **Välj ett alternativ:** - A: Wrong answer - B: Correct answer - C: Another wrong ```spoiler-block: B ``` """ is_question, data = parse_markdown_question(Path("test.md"), content) assert is_question is True assert data['text'] == 'What is the correct answer?' assert data['correct_answer'] == 'B' assert data['has_answer'] is True assert data['question_type'] == 'scq' assert len(data['options']) == 3 assert data['options'][0] == ('A', 'Wrong answer') assert data['options'][1] == ('B', 'Correct answer') def test_parse_multiple_choice_question(self): """Test parsing multiple choice question (MCQ) with 'och' separator""" content = """--- tags: [ah2, provfråga, frågetyp/mcq, cerebrum] date: 2022-01-15 --- Vilka av följande räknas till storhjärnans basala kärnor? **Välj två alternativ** - A: Putamen - B: Nucleus Ruber - C: Substantia nigra - D: Nucleus caudatus ```spoiler-block: A och D ``` """ is_question, data = parse_markdown_question(Path("test.md"), content) assert is_question is True assert 'Vilka av följande' in data['text'] assert data['correct_answer'] == 'A,D' # Normalized to comma-separated assert data['has_answer'] is True assert data['question_type'] == 'mcq' assert len(data['options']) == 4 def test_parse_multiple_choice_comma_separated(self): """Test MCQ with comma-separated answer""" content = """--- tags: [frågetyp/mcq] --- Select two options: - A: Option A - B: Option B - C: Option C - D: Option D ```spoiler-block: B, C ``` """ is_question, data = parse_markdown_question(Path("test.md"), content) assert data['correct_answer'] == 'B,C' assert data['has_answer'] is True def test_parse_matching_question(self): """Test parsing matching question (DND/Matching)""" content = """--- tags: [ah2, provfråga, frågetyp/matching, anatomi, öra] date: 2023-05-31 --- **Matcha rätt funktion med rätt lob:** (1p för alla rätt, inga delpoäng) - Smak - Syn - Somatosensorik - Motorik - Hörsel **Alternativ:** - Lobus frontalis - Lobus Insularis - Lobus temporalis - Lobus parietalis - Lobus occipitalis ```spoiler-block: Smak: Lobus Insularis Syn: Lobus occipitalis Somatosensorik: Lobus parietalis Motorik: Lobus frontalis Hörsel: Lobus temporalis ``` """ is_matching, data = parse_markdown_question(Path("test.md"), content) assert is_matching is True assert data['question_type'] == 'matching' assert data['has_answer'] is True assert len(data['left_items']) == 5 assert len(data['top_items']) == 5 assert len(data['correct_pairs']) == 5 def test_parse_textalternativ_question(self): """Test text alternative question type""" content = """--- tags: [frågetyp/textalternativ, öga, anatomi] --- Svara på följande frågor: a) Bokstaven B sitter i en lob, vilken? - Lobus temporalis - Lobus frontalis - Lobus parietalis b) Vilket funktionellt centra återfinns där? - Syncentrum - Motorcentrum - Somatosensoriskt centrum ```spoiler-block: a) Lobus parietalis b) Somatosensoriskt centrum ``` """ is_question, data = parse_markdown_question(Path("test.md"), content) assert is_question is True assert data['question_type'] == 'textalternativ' assert data['has_answer'] is True assert 'Lobus parietalis' in data['correct_answer'] assert 'Somatosensoriskt centrum' in data['correct_answer'] def test_parse_textfalt_question(self): """Test text field (fill-in) question type""" content = """--- tags: [frågetyp/textfält, öga] --- **Fyll i rätt siffra!** a) Vilken siffra pekar på gula fläcken? b) Vilken siffra pekar på choroidea? ```spoiler-block: a) 7 b) 6 ``` """ is_question, data = parse_markdown_question(Path("test.md"), content) assert is_question is True assert data['question_type'] == 'textfält' assert data['has_answer'] is True assert '7' in data['correct_answer'] assert '6' in data['correct_answer'] @pytest.mark.django_db @pytest.mark.import_tests class TestQuestionImport: """Test actual import of questions to database""" def test_import_single_question(self, tmp_path): """Test importing a single question file""" question_file = tmp_path / "question1.md" question_file.write_text("""--- tags: [frågetyp/scq] --- Test question? - A: Correct - B: Wrong ```spoiler-block: A ``` """) stats = ImportStats() result = import_question_file(question_file, tmp_path, stats, force=True) assert result in ['imported', 'updated'] assert stats.questions_with_answers == 1 # Verify in database question = Question.objects.get(text='Test question?') assert question.correct_answer == 'A' assert question.options.count() == 2 def test_mtime_tracking(self, tmp_path): """Test that file modification time is tracked""" question_file = tmp_path / "question4.md" question_file.write_text("""--- tags: [frågetyp/scq] --- What is the correct answer? ```spoiler-block: A ``` """) stats = ImportStats() import_question_file(question_file, tmp_path, stats, force=True) question = Question.objects.get(text='What is the correct answer?') assert question.file_mtime == question_file.stat().st_mtime def test_update_existing_question(self, tmp_path): """Test updating an existing question""" question_file = tmp_path / "question5.md" # Initial import question_file.write_text("""--- tags: [frågetyp/scq] --- Question to update? ```spoiler-block: A ``` """) import_question_file(question_file, tmp_path, ImportStats(), force=True) # Update the file time.sleep(0.1) question_file.write_text("""--- tags: [frågetyp/scq] --- Question to update? ```spoiler-block: B ``` """) stats = ImportStats() result = import_question_file(question_file, tmp_path, stats, force=False) assert result == 'updated' assert Question.objects.get(text='Question to update?').correct_answer == 'B'