"""exercise_rich_text: ยง11 Normalisierung und ID-Sammlung.""" from __future__ import annotations import pytest from fastapi import HTTPException from unittest.mock import MagicMock from exercise_rich_text import ( assert_no_inline_media_references_on_create, collect_inline_exercise_media_ids, normalize_inline_exercise_media_markup, sanitize_exercise_rich_html, validate_inline_exercise_media_ids_for_exercise, ) def test_normalize_strips_script_and_handlers() -> None: raw = '
Hallo
Link' out = normalize_inline_exercise_media_markup(raw) assert out is not None lowered = out.lower() assert "script" not in lowered assert "onclick" not in lowered assert "javascript:" not in lowered assert "Hallo" in out assert "Link" in out def test_normalize_keeps_inline_media_span() -> None: raw = 'x' out = sanitize_exercise_rich_html(raw) assert out is not None assert 'data-shinkan-exercise-media="7"' in out assert 'data-shinkan-exercise-media-size="full"' in out assert "shinkan-inline-media" in out assert "evil" not in out s = 'Vor {{exerciseMedia: 42 }} nach
' out = normalize_inline_exercise_media_markup(s) assert 'data-shinkan-exercise-media="42"' in out assert 'data-shinkan-exercise-media-size="medium"' in out assert "{{" not in out def test_collect_merges_braces_and_data_attr() -> None: html = '{{ExercisemEDIA: 1}}\n' assert collect_inline_exercise_media_ids(html) == {1, 2} def test_assert_no_inline_on_create_raises() -> None: with pytest.raises(HTTPException) as ei: assert_no_inline_media_references_on_create({5}) assert ei.value.status_code == 400 body = ei.value.detail assert isinstance(body, dict) assert body["code"] == "INLINE_EXERCISE_MEDIA_ON_CREATE" def test_validate_ids_sql_mock() -> None: mock_cur = MagicMock() mock_cur.fetchall.return_value = [{"id": 1}] with pytest.raises(HTTPException) as ei: validate_inline_exercise_media_ids_for_exercise(mock_cur, 100, {1, 99}) assert ei.value.detail["invalid_exercise_media_ids"] == [99]