# -*- coding: utf-8 -*-
"""Tests for ToutiaoChannel."""
import json
from unittest.mock import MagicMock, patch
import pytest
from agent_reach.channels.toutiao import ToutiaoChannel, _parse_search_results
@pytest.fixture
def ch():
return ToutiaoChannel()
class TestToutiaoChannelAttributes:
def test_name(self, ch):
assert ch.name == "toutiao"
def test_tier(self, ch):
assert ch.tier == 0
def test_backends(self, ch):
assert ch.backends
class TestToutiaoCanHandle:
def test_matches_toutiao(self, ch):
assert ch.can_handle("https://www.toutiao.com/article/123")
def test_matches_search(self, ch):
assert ch.can_handle("https://so.toutiao.com/search?keyword=ai")
def test_rejects_other(self, ch):
assert not ch.can_handle("https://www.baidu.com")
class TestToutiaoCheck:
def test_check_ok(self, ch):
mock_resp = MagicMock()
mock_resp.__enter__ = lambda s: s
mock_resp.__exit__ = MagicMock(return_value=False)
mock_resp.status = 200
mock_resp.read.return_value = b'{"data": {"title": "test"}}'
with patch("urllib.request.urlopen", return_value=mock_resp):
status, msg = ch.check()
assert status == "ok"
def test_check_exception(self, ch):
with patch("urllib.request.urlopen", side_effect=Exception("timeout")):
status, msg = ch.check()
assert status == "warn"
assert "timeout" in msg
class TestParseSearchResults:
def _make_script(self, data: dict) -> str:
# Production code skips scripts < 1000 chars; pad to exceed threshold
payload = json.dumps({"data": data})
padding = "x" * max(0, 1001 - len(payload))
padded = payload[:-1] + f',"_pad":"{padding}"' + payload[-1]
return f''
def test_extracts_article_url_first(self):
html = self._make_script({
"title": "测试文章",
"article_url": "https://www.toutiao.com/article/1",
"template_key": "undefined-default",
})
results = _parse_search_results(html)
assert len(results) == 1
assert results[0]["url"] == "https://www.toutiao.com/article/1"
assert results[0]["title"] == "测试文章"
def test_fallback_to_source_url(self):
html = self._make_script({
"title": "备用 URL 文章",
"source_url": "https://www.toutiao.com/source/1",
"template_key": "undefined-default",
})
results = _parse_search_results(html)
assert len(results) == 1
assert results[0]["url"] == "https://www.toutiao.com/source/1"
def test_skips_entries_without_url(self):
html = self._make_script({
"title": "无 URL 文章",
"template_key": "undefined-default",
})
results = _parse_search_results(html)
assert results == []
def test_skips_entries_without_title(self):
html = self._make_script({
"article_url": "https://www.toutiao.com/article/1",
"template_key": "undefined-default",
})
results = _parse_search_results(html)
assert results == []
def test_skips_skip_templates(self):
html = self._make_script({
"title": "广告",
"article_url": "https://www.toutiao.com/article/1",
"template_key": "Bottom-ad",
})
results = _parse_search_results(html)
assert results == []
def test_empty_html(self):
assert _parse_search_results("") == []
def test_short_scripts_ignored(self):
# script < 1000 chars should be skipped
html = ''
assert _parse_search_results(html) == []