"""Tests for python-markdown-oembed extension.""" from __future__ import annotations import json import re import warnings from unittest.mock import MagicMock, patch import markdown import pytest from mdx_oembed import endpoints from mdx_oembed.inlinepatterns import OEMBED_LINK_RE, _is_image_url, _sanitize_html from mdx_oembed.oembed import ( OEmbedConsumer, OEmbedEndpoint, OEmbedError, OEmbedNoEndpoint, ) # --------------------------------------------------------------------------- # Regex tests # --------------------------------------------------------------------------- _OEMBED_RE = re.compile(OEMBED_LINK_RE) def test_ignore_relative_image_link(): assert _OEMBED_RE.search("![image](/image.png)") is None def test_match_absolute_url(): m = _OEMBED_RE.search("![img](http://example.com/photo.png)") assert m is not None def test_match_youtube_link(): m = _OEMBED_RE.search("![video](http://www.youtube.com/watch?v=ABC)") assert m is not None assert m.group(2) == "http://www.youtube.com/watch?v=ABC" def test_match_youtube_short_link(): m = _OEMBED_RE.search("![video](http://youtu.be/ABC)") assert m is not None def test_match_https(): m = _OEMBED_RE.search("![video](https://youtu.be/ABC)") assert m is not None def test_match_protocol_relative(): m = _OEMBED_RE.search("![video](//youtu.be/ABC)") assert m is not None def test_alt_text_captured(): m = _OEMBED_RE.search("![my alt text](https://example.com/embed)") assert m is not None assert m.group(1) == "my alt text" # --------------------------------------------------------------------------- # Image URL detection # --------------------------------------------------------------------------- @pytest.mark.parametrize( "ext", ["png", "jpg", "jpeg", "gif", "webp", "avif", "svg", "bmp", "tiff", "ico"], ) def test_common_image_extensions(ext: str): assert _is_image_url(f"http://example.com/photo.{ext}") is True def test_image_url_case_insensitive(): assert _is_image_url("http://example.com/Photo.PNG") is True assert _is_image_url("http://example.com/photo.JpEg") is True def test_image_url_query_string_ignored(): assert _is_image_url("http://example.com/photo.jpg?size=large") is True def test_non_image_url(): assert _is_image_url("http://www.youtube.com/watch?v=ABC") is False def test_no_extension_url(): assert _is_image_url("http://example.com/embed") is False # --------------------------------------------------------------------------- # HTML sanitization # --------------------------------------------------------------------------- def test_sanitize_allows_iframe(): html = ( '' ) result = _sanitize_html(html) assert "", "type": "video"}).encode() mock_resp = MagicMock() mock_resp.status = 200 mock_resp.read.return_value = body mock_resp.headers.get_content_charset.return_value = "utf-8" mock_resp.__enter__ = MagicMock(return_value=mock_resp) mock_resp.__exit__ = MagicMock(return_value=False) with patch("mdx_oembed.oembed.urlopen", return_value=mock_resp): data = consumer.embed("http://example.com/video/1") assert data["html"] == "" # --------------------------------------------------------------------------- # Extension integration tests (mocked HTTP) # --------------------------------------------------------------------------- def _make_mock_consumer( html_response: str = "", ) -> MagicMock: """Create a mock OEmbedConsumer that returns the given HTML.""" consumer = MagicMock() data = {"html": html_response, "type": "video"} response = MagicMock() response.get = lambda key, default=None: data.get(key, default) response.__getitem__ = lambda self_inner, key: data[key] consumer.embed.return_value = response return consumer def _make_photo_consumer( photo_url: str = "https://example.com/photo.jpg", width: int = 640, height: int = 480, ) -> MagicMock: consumer = MagicMock() data = {"type": "photo", "url": photo_url, "width": width, "height": height} response = MagicMock() response.get = lambda key, default=None: data.get(key, default) response.__getitem__ = lambda self_inner, key: data[key] consumer.embed.return_value = response return consumer def _make_failing_consumer( exc_class: type[Exception] = Exception, msg: str = "fail" ) -> MagicMock: consumer = MagicMock() consumer.embed.side_effect = exc_class(msg) return consumer def _convert( text: str, consumer: MagicMock | None = None, **ext_config: object, ) -> str: """Helper: convert markdown with a mocked consumer.""" if consumer is None: consumer = _make_mock_consumer() with patch("mdx_oembed.extension.OEmbedConsumer", return_value=consumer): md = markdown.Markdown( extensions=["oembed"], extension_configs={"oembed": ext_config} if ext_config else {}, ) return md.convert(text) # --- basic embedding --- def test_youtube_embed(): output = _convert("![video](http://www.youtube.com/watch?v=ABC)") assert "alert("xss")' ) output = _convert("![v](http://www.youtube.com/watch?v=ABC)", evil_consumer) assert " MagicMock: if "youtube" in url: resp = MagicMock() data = {"html": "", "type": "video"} resp.get = lambda key, default=None: data.get(key, default) resp.__getitem__ = lambda self_inner, key: data[key] return resp raise OEmbedNoEndpoint("nope") consumer = MagicMock() consumer.embed.side_effect = side_effect with patch("mdx_oembed.extension.OEmbedConsumer", return_value=consumer): md = markdown.Markdown( extensions=["oembed"], extension_configs={ "oembed": {"allowed_endpoints": [endpoints.YOUTUBE]}, }, ) yt_output = md.convert("![v](http://www.youtube.com/watch?v=A)") assert "