"""Tests for quiz-maker skill: JSON parse, fallback, and export smoke.""" from pathlib import Path from agent.models import QuizMakerInput, QuizOutline, QuizQuestion from agent.prompts import fallback_quiz, quiz_max_tokens, quiz_to_markdown from agent.runner import AgentRunner from agent.tools.quiz import create_quiz, create_quiz_docx, create_quiz_html def test_quiz_max_tokens_scales_with_question_count(): assert quiz_max_tokens(5) == 1020 assert quiz_max_tokens(3) == 660 assert quiz_max_tokens(12) == 1536 def test_parse_quiz_outline_normalizes_count(): runner = AgentRunner() raw = ( '{"title": "Science Quiz", "instructions": "Circle one.", "questions": [' '{"prompt": "Q1?", "choices": ["a", "b", "c", "d"], "correct_index": 0, "explanation": "e1"},' '{"prompt": "Q2?", "choices": ["a", "b", "c", "d"], "correct_index": 1, "explanation": "e2"},' '{"prompt": "Q3?", "choices": ["a", "b", "c", "d"], "correct_index": 2, "explanation": "e3"}' "]}" ) outline = runner._parse_quiz_outline(raw, expected_questions=5) assert len(outline.questions) == 5 assert outline.title == "Science Quiz" def test_parse_quiz_outline_trims_extra_questions(): runner = AgentRunner() questions = ",".join( f'{{"prompt": "Q{i}?", "choices": ["a","b","c","d"], "correct_index": 0, "explanation": ""}}' for i in range(1, 8) ) raw = f'{{"title": "Long", "questions": [{questions}]}}' outline = runner._parse_quiz_outline(raw, expected_questions=5) assert len(outline.questions) == 5 def test_parse_quiz_outline_or_error_empty(): runner = AgentRunner() outline, err = runner._parse_quiz_outline_or_error("", 5, None) assert outline is None assert "empty" in err.lower() def test_fallback_quiz_has_requested_count(): req = QuizMakerInput(topic="Fractions", grade="5", question_count=7) outline = fallback_quiz(req) assert len(outline.questions) == 7 assert "Fractions" in outline.title assert all(len(q.choices) == 4 for q in outline.questions) def test_quiz_to_markdown_includes_answers(): outline = QuizOutline( title="Test", instructions="Read carefully.", questions=[ QuizQuestion( prompt="2+2?", choices=["4", "3", "5", "6"], correct_index=0, explanation="Basic addition.", ), QuizQuestion( prompt="3+3?", choices=["6", "5", "7", "8"], correct_index=0, explanation="Also addition.", ), QuizQuestion( prompt="1+1?", choices=["2", "1", "3", "4"], correct_index=0, explanation="Easy.", ), ], ) md = quiz_to_markdown(outline) assert "2+2?" in md assert "**Answer:** A" in md def test_create_quiz_docx_and_html(tmp_path: Path): outline = QuizOutline( title="Smoke Quiz", instructions="Circle the best answer.", questions=[ QuizQuestion( prompt="Sample?", choices=["Yes", "No", "Maybe", "Sometimes"], correct_index=0, explanation="Because.", ), QuizQuestion( prompt="Another?", choices=["A", "B", "C", "D"], correct_index=2, explanation="C is correct.", ), QuizQuestion( prompt="Third?", choices=["1", "2", "3", "4"], correct_index=1, explanation="Two.", ), ], ) docx_path = tmp_path / "quiz.docx" html_path = tmp_path / "quiz.html" create_quiz_docx(outline, docx_path) create_quiz_html(outline, html_path) assert docx_path.stat().st_size > 100 html_text = html_path.read_text(encoding="utf-8") assert "Smoke Quiz" in html_text assert "Answer key" in html_text paths = create_quiz(outline, tmp_path / "out", stem="worksheet") assert paths["docx"].exists() assert paths["html"].exists()