Files
wehub-resource-sync 67296cb4a7
Deploy Docs / deploy (push) Failing after 1s
chore: import upstream snapshot with attribution
2026-07-13 12:26:53 +08:00

93 lines
3.0 KiB
Python

from __future__ import annotations
import asyncio
from datetime import datetime, timezone
from unittest.mock import AsyncMock
from unittest.mock import MagicMock
from src.models import RSSSourceConfig
from src.scrapers.rss import RSSScraper
_FEED = """<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0"><channel><title>Test</title>
<item>
<guid>entry-1</guid>
<title>Item 1</title>
<link>https://example.com/item-1</link>
<pubDate>Fri, 24 Apr 2026 12:00:00 GMT</pubDate>
<description>Short summary from feed.</description>
</item>
</channel></rss>
"""
_SINCE = datetime(2026, 4, 24, 0, 0, tzinfo=timezone.utc)
def _make_feed_client(feed_text: str) -> AsyncMock:
response = MagicMock()
response.text = feed_text
response.raise_for_status.return_value = None
client = AsyncMock()
client.get.return_value = response
return client
def test_rss_ids_are_deterministic() -> None:
client = _make_feed_client(_FEED)
source = RSSSourceConfig(name="Test", url="https://example.com/feed.xml")
scraper = RSSScraper([source], client)
first = asyncio.run(scraper.fetch(_SINCE))[0].id
second = asyncio.run(scraper.fetch(_SINCE))[0].id
assert first == second
assert first == "rss:example.com_feed.xml:5e2d5d1e58e94d76"
def _make_registry(name: str, extractor):
registry = MagicMock()
registry.get.side_effect = lambda n: extractor if n == name else None
return registry
def test_content_extractor_replaces_feed_content() -> None:
client = _make_feed_client(_FEED)
extractor = AsyncMock()
extractor.extract.return_value = "Full article text from extractor."
source = RSSSourceConfig(
name="Test", url="https://example.com/feed.xml", content_extractor="my-ext"
)
scraper = RSSScraper([source], client, extractors=_make_registry("my-ext", extractor))
items = asyncio.run(scraper.fetch(_SINCE))
assert len(items) == 1
assert items[0].content == "Full article text from extractor."
extractor.extract.assert_awaited_once_with("https://example.com/item-1", client)
def test_content_extractor_falls_back_on_none() -> None:
client = _make_feed_client(_FEED)
extractor = AsyncMock()
extractor.extract.return_value = None # extraction failed
source = RSSSourceConfig(
name="Test", url="https://example.com/feed.xml", content_extractor="my-ext"
)
scraper = RSSScraper([source], client, extractors=_make_registry("my-ext", extractor))
items = asyncio.run(scraper.fetch(_SINCE))
assert len(items) == 1
assert items[0].content == "Short summary from feed."
def test_unknown_extractor_name_ignored() -> None:
client = _make_feed_client(_FEED)
source = RSSSourceConfig(
name="Test", url="https://example.com/feed.xml", content_extractor="nonexistent"
)
scraper = RSSScraper([source], client, extractors=_make_registry("other", AsyncMock()))
items = asyncio.run(scraper.fetch(_SINCE))
assert len(items) == 1
assert items[0].content == "Short summary from feed."