Spaces:
Paused
Paused
| """Unit tests for report_generator prompt assembly.""" | |
| from __future__ import annotations | |
| import pytest | |
| from app.answer_grid import AnswerGrid, QuestionMeta, StudentRow, WrongAnswer | |
| from app.report_generator import ( | |
| build_student_markdown, | |
| build_wrong_block, | |
| extract_html, | |
| ) | |
| def _make_grid(official, student_answers) -> AnswerGrid: | |
| n = len(official) | |
| return AnswerGrid( | |
| total_questions=n, | |
| official_answers=tuple(official), | |
| students=(StudentRow(name="Alice", answers=tuple(student_answers)),), | |
| questions=tuple( | |
| QuestionMeta(number=i + 1, text=f"Q{i+1}", options=("A) a", "B) b", "C) c")) | |
| for i in range(n) | |
| ), | |
| ) | |
| def test_build_wrong_block_full(): | |
| wa = WrongAnswer( | |
| question=QuestionMeta( | |
| number=3, | |
| text="What is 2+2?", | |
| options=("A) 3", "B) 4", "C) 5"), | |
| ), | |
| student_answer="A", | |
| correct_answer="B", | |
| ) | |
| block = build_wrong_block(wa) | |
| assert "### 第 3 題" in block | |
| assert "What is 2+2?" in block | |
| assert "A) 3" in block | |
| assert "**學生作答**: A" in block | |
| assert "**正確答案**: B" in block | |
| def test_build_wrong_block_without_options(): | |
| wa = WrongAnswer( | |
| question=QuestionMeta(number=1, text="Q1", options=()), | |
| student_answer="A", | |
| correct_answer="B", | |
| ) | |
| block = build_wrong_block(wa) | |
| assert "**選項**" not in block | |
| assert "**學生作答**: A" in block | |
| def test_build_wrong_block_blank_student_answer(): | |
| wa = WrongAnswer( | |
| question=QuestionMeta(number=1, text="Q", options=()), | |
| student_answer=None, | |
| correct_answer="A", | |
| ) | |
| block = build_wrong_block(wa) | |
| assert "(未作答)" in block | |
| def test_build_student_markdown_filters_to_wrong_only(): | |
| grid = _make_grid(["A", "B", "C"], ["A", "X", "C"]) | |
| result = build_student_markdown(grid, 0) | |
| assert result.wrong_count == 1 | |
| assert result.total_questions == 3 | |
| assert "### 第 2 題" in result.markdown_prompt | |
| assert "### 第 1 題" not in result.markdown_prompt | |
| assert "### 第 3 題" not in result.markdown_prompt | |
| def test_build_student_markdown_header_contains_counts(): | |
| grid = _make_grid(["A", "B", "C"], ["X", "Y", "Z"]) | |
| result = build_student_markdown(grid, 0) | |
| assert "總題數:3,答錯:3" in result.markdown_prompt | |
| assert "學生:Alice" in result.markdown_prompt | |
| def test_build_student_markdown_all_correct_path(): | |
| grid = _make_grid(["A", "B"], ["A", "B"]) | |
| result = build_student_markdown(grid, 0) | |
| assert result.wrong_count == 0 | |
| assert "答錯:0" in result.markdown_prompt | |
| assert "全部答對" in result.markdown_prompt | |
| def test_build_student_markdown_preserves_question_ordering(): | |
| grid = _make_grid(["A", "B", "C", "D"], ["X", "B", "Y", "Z"]) | |
| result = build_student_markdown(grid, 0) | |
| # Blocks must appear in Q1, Q3, Q4 order | |
| idx1 = result.markdown_prompt.index("### 第 1 題") | |
| idx3 = result.markdown_prompt.index("### 第 3 題") | |
| idx4 = result.markdown_prompt.index("### 第 4 題") | |
| assert idx1 < idx3 < idx4 | |
| def test_build_student_markdown_out_of_range(): | |
| grid = _make_grid(["A"], ["A"]) | |
| with pytest.raises(IndexError): | |
| build_student_markdown(grid, 5) | |
| def test_extract_html(content, expected): | |
| assert extract_html(content) == expected | |