mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-07-21 22:27:13 +08:00
feat: render remote images in Feishu cards
This commit is contained in:
151
tests/test_feishu_markdown_images.py
Normal file
151
tests/test_feishu_markdown_images.py
Normal file
@@ -0,0 +1,151 @@
|
||||
import socket
|
||||
|
||||
import pytest
|
||||
|
||||
from channel.feishu import feishu_static_card
|
||||
|
||||
|
||||
class FakeResponse:
|
||||
def __init__(self, status_code=200, headers=None, chunks=None):
|
||||
self.status_code = status_code
|
||||
self.headers = headers or {}
|
||||
self._chunks = chunks or []
|
||||
self.closed = False
|
||||
|
||||
def iter_content(self, chunk_size=8192):
|
||||
yield from self._chunks
|
||||
|
||||
def close(self):
|
||||
self.closed = True
|
||||
|
||||
def json(self):
|
||||
return getattr(self, "json_body", {})
|
||||
|
||||
|
||||
def test_remote_markdown_images_are_uploaded_once_and_replaced_with_image_keys():
|
||||
calls = []
|
||||
|
||||
def upload(url):
|
||||
calls.append(url)
|
||||
return "img_v2_chart"
|
||||
|
||||
markdown = (
|
||||
"Before\n\n"
|
||||
"Again "
|
||||
)
|
||||
|
||||
result = feishu_static_card.resolve_markdown_images(markdown, upload)
|
||||
|
||||
assert result == (
|
||||
"Before\n\nAgain "
|
||||
)
|
||||
assert calls == ["https://cdn.example.com/chart.png"]
|
||||
|
||||
|
||||
def test_existing_feishu_image_keys_and_regular_links_are_untouched():
|
||||
def unexpected(_url):
|
||||
raise AssertionError("uploader should not be called")
|
||||
|
||||
markdown = " [docs](https://example.com/docs)"
|
||||
|
||||
assert feishu_static_card.resolve_markdown_images(markdown, unexpected) == markdown
|
||||
|
||||
|
||||
def test_failed_remote_image_upload_degrades_to_readable_alt_text():
|
||||
markdown = "Result: "
|
||||
|
||||
result = feishu_static_card.resolve_markdown_images(markdown, lambda _url: None)
|
||||
|
||||
assert result == "Result: [Image unavailable: latency chart]"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"url",
|
||||
[
|
||||
"http://127.0.0.1/image.png",
|
||||
"http://169.254.169.254/latest/meta-data",
|
||||
"http://[::1]/image.png",
|
||||
],
|
||||
)
|
||||
def test_public_image_guard_rejects_non_public_literal_addresses(url):
|
||||
with pytest.raises(ValueError, match="non-public"):
|
||||
feishu_static_card.validate_public_image_url(url)
|
||||
|
||||
|
||||
def test_image_download_revalidates_redirect_targets(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
socket,
|
||||
"getaddrinfo",
|
||||
lambda *args, **kwargs: [
|
||||
(socket.AF_INET, socket.SOCK_STREAM, 6, "", ("93.184.216.34", 0))
|
||||
],
|
||||
)
|
||||
redirect = FakeResponse(
|
||||
status_code=302,
|
||||
headers={"Location": "http://127.0.0.1/private.png"},
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError, match="non-public"):
|
||||
feishu_static_card.download_public_image(
|
||||
"https://example.com/image.png", get=lambda *args, **kwargs: redirect
|
||||
)
|
||||
|
||||
assert redirect.closed is True
|
||||
|
||||
|
||||
def test_image_download_enforces_content_type_and_size(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
socket,
|
||||
"getaddrinfo",
|
||||
lambda *args, **kwargs: [
|
||||
(socket.AF_INET, socket.SOCK_STREAM, 6, "", ("93.184.216.34", 0))
|
||||
],
|
||||
)
|
||||
not_image = FakeResponse(
|
||||
headers={"Content-Type": "text/html"},
|
||||
chunks=[b"not an image"],
|
||||
)
|
||||
too_large = FakeResponse(
|
||||
headers={"Content-Type": "image/png", "Content-Length": "11"},
|
||||
chunks=[b"01234567890"],
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError, match="not an image"):
|
||||
feishu_static_card.download_public_image(
|
||||
"https://example.com/image.png", get=lambda *args, **kwargs: not_image
|
||||
)
|
||||
with pytest.raises(ValueError, match="too large"):
|
||||
feishu_static_card.download_public_image(
|
||||
"https://example.com/image.png",
|
||||
get=lambda *args, **kwargs: too_large,
|
||||
max_bytes=10,
|
||||
)
|
||||
|
||||
|
||||
def test_public_image_is_uploaded_to_feishu_without_a_temp_file(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
feishu_static_card,
|
||||
"download_public_image",
|
||||
lambda _url: (b"png-bytes", "image/png"),
|
||||
)
|
||||
response = FakeResponse()
|
||||
response.json_body = {
|
||||
"code": 0,
|
||||
"data": {"image_key": "img_v2_uploaded"},
|
||||
}
|
||||
calls = []
|
||||
|
||||
def post(url, **kwargs):
|
||||
calls.append((url, kwargs))
|
||||
return response
|
||||
|
||||
image_key = feishu_static_card.upload_public_image_to_feishu(
|
||||
"https://example.com/image.png", "tenant-token", post=post
|
||||
)
|
||||
|
||||
assert image_key == "img_v2_uploaded"
|
||||
assert calls[0][1]["headers"] == {"Authorization": "Bearer tenant-token"}
|
||||
filename, payload, content_type = calls[0][1]["files"]["image"]
|
||||
assert filename == "markdown-image.png"
|
||||
assert payload == b"png-bytes"
|
||||
assert content_type == "image/png"
|
||||
Reference in New Issue
Block a user