CLI tool that tracks GitHub releases or tags from user-defined repositories and exposes them as a subscribable RSS 2.0 feed. Features: - Track releases or tags per repository - SQLite storage with automatic deduplication - RSS 2.0 feed with markdown-stripped release notes - Built-in HTTP server for local feed access - Rate-limit-aware GitHub API client - 64 passing tests
135 lines
4.5 KiB
Python
135 lines
4.5 KiB
Python
"""Tests for RSS feed generation."""
|
|
|
|
import xml.etree.ElementTree as ET
|
|
|
|
from src.rss import generate_feed, strip_markdown, format_rfc822
|
|
|
|
|
|
class TestStripMarkdown:
|
|
def test_plain_text(self):
|
|
assert strip_markdown("Hello world") == "Hello world"
|
|
|
|
def test_bold_and_lists(self):
|
|
result = strip_markdown("**bold** and - item1\n- item2")
|
|
assert "bold" in result
|
|
assert "item1" in result
|
|
|
|
def test_empty_string(self):
|
|
assert strip_markdown("") == ""
|
|
|
|
def test_none(self):
|
|
assert strip_markdown("") == ""
|
|
|
|
def test_code_blocks(self):
|
|
result = strip_markdown("```python\nprint('hello')\n```")
|
|
assert "print" in result or "hello" in result
|
|
|
|
|
|
class TestFormatRfc822:
|
|
def test_iso_to_rfc822(self):
|
|
result = format_rfc822("2025-01-15T10:30:00Z")
|
|
assert "Wed" in result or "15" in result
|
|
assert "2025" in result
|
|
|
|
def test_with_tz_offset(self):
|
|
result = format_rfc822("2025-01-15T10:30:00+00:00")
|
|
assert "2025" in result
|
|
|
|
|
|
class TestGenerateFeed:
|
|
def _entries(self, count=1):
|
|
return [
|
|
{
|
|
"tag_name": f"v1.{i}.0",
|
|
"title": f"Release v1.{i}.0",
|
|
"body": f"# Release notes\n\n- Feature {i}",
|
|
"published_at": "2025-01-15T10:30:00Z",
|
|
"html_url": f"https://github.com/owner/repo/releases/tag/v1.{i}.0",
|
|
"owner_repo": "owner/repo",
|
|
}
|
|
for i in range(count)
|
|
]
|
|
|
|
def test_basic_feed(self):
|
|
entries = self._entries(2)
|
|
xml_str = generate_feed(entries)
|
|
root = ET.fromstring(xml_str)
|
|
assert root.tag == "rss"
|
|
assert root.get("version") == "2.0"
|
|
|
|
channel = root[0]
|
|
assert channel.tag == "channel"
|
|
assert channel.find("title").text == "GitHub Release Monitor"
|
|
assert channel.find("description").text == "Latest releases from tracked GitHub repositories"
|
|
|
|
items = channel.findall("item")
|
|
assert len(items) == 2
|
|
|
|
def test_item_structure(self):
|
|
entries = self._entries(1)
|
|
xml_str = generate_feed(entries)
|
|
root = ET.fromstring(xml_str)
|
|
channel = root[0]
|
|
item = channel.find("item")
|
|
|
|
assert item.find("title").text == "[owner/repo] Release v1.0.0"
|
|
assert "Feature 0" in item.find("description").text
|
|
assert item.find("link").text == "https://github.com/owner/repo/releases/tag/v1.0.0"
|
|
assert item.find("pubDate").text is not None
|
|
assert item.find("guid").text == "github.com/owner/repo/releases/tag/v1.0.0"
|
|
|
|
def test_description_truncation(self):
|
|
entry = {
|
|
"tag_name": "v1.0.0",
|
|
"title": "Release",
|
|
"body": "A" * 400,
|
|
"published_at": "2025-01-15T10:30:00Z",
|
|
"html_url": "https://example.com",
|
|
"owner_repo": "owner/repo",
|
|
}
|
|
xml_str = generate_feed([entry])
|
|
root = ET.fromstring(xml_str)
|
|
desc = root[0].find("item").find("description").text
|
|
assert len(desc) == 301 # 300 chars + 1 ellipsis char
|
|
|
|
def test_tag_entry_no_body(self):
|
|
entry = {
|
|
"tag_name": "v1.0.0",
|
|
"title": "v1.0.0",
|
|
"body": "",
|
|
"published_at": "2025-01-15T10:30:00Z",
|
|
"html_url": "https://github.com/owner/repo/tags/v1.0.0",
|
|
"owner_repo": "owner/repo",
|
|
}
|
|
xml_str = generate_feed([entry])
|
|
root = ET.fromstring(xml_str)
|
|
desc = root[0].find("item").find("description").text
|
|
assert desc == "Tag v1.0.0"
|
|
|
|
def test_item_cap(self):
|
|
entries = self._entries(100)
|
|
xml_str = generate_feed(entries)
|
|
root = ET.fromstring(xml_str)
|
|
items = root[0].findall("item")
|
|
assert len(items) == 50 # RSS_MAX_ITEMS
|
|
|
|
def test_xml_escaping(self):
|
|
entry = {
|
|
"tag_name": "v1.0.0",
|
|
"title": "Release with <special> & 'chars'",
|
|
"body": "Some **bold** text",
|
|
"published_at": "2025-01-15T10:30:00Z",
|
|
"html_url": "https://example.com?a=1&b=2",
|
|
"owner_repo": "owner/repo",
|
|
}
|
|
xml_str = generate_feed([entry])
|
|
# Should parse without error
|
|
root = ET.fromstring(xml_str)
|
|
assert root is not None
|
|
|
|
def test_base_url(self):
|
|
entries = self._entries(1)
|
|
xml_str = generate_feed(entries, base_url="http://example.com:9000")
|
|
root = ET.fromstring(xml_str)
|
|
assert root[0].find("link").text == "http://example.com:9000/"
|