Initial commit: GitHub Release Monitor CLI tool
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
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
"""Tests for the HTTP server."""
|
||||
|
||||
import sqlite3
|
||||
import threading
|
||||
import tempfile
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
from src.db import _create_tables, add_repo, upsert_entry
|
||||
from src.server import create_handler
|
||||
from http.server import HTTPServer
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def db_path(tmp_path):
|
||||
"""Create a temporary database with sample data."""
|
||||
path = str(tmp_path / "test.db")
|
||||
conn = __import__("sqlite3").connect(path)
|
||||
_create_tables(conn)
|
||||
repo_id = add_repo(conn, "owner/repo")
|
||||
upsert_entry(conn, repo_id, "release", "v1.0.0", "Release v1.0.0",
|
||||
"# Changelog\n\n- New feature",
|
||||
"2025-01-15T10:30:00Z",
|
||||
"https://github.com/owner/repo/releases/tag/v1.0.0")
|
||||
conn.close()
|
||||
return path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def server(db_path):
|
||||
"""Start a test HTTP server in a background thread."""
|
||||
handler = create_handler(db_path)
|
||||
server = HTTPServer(("127.0.0.1", 0), handler)
|
||||
port = server.server_address[1]
|
||||
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
||||
thread.start()
|
||||
yield f"http://127.0.0.1:{port}"
|
||||
server.shutdown()
|
||||
|
||||
|
||||
class TestFeedEndpoint:
|
||||
def test_feed_returns_200(self, server):
|
||||
resp = httpx.get(f"{server}/feed.xml")
|
||||
assert resp.status_code == 200
|
||||
|
||||
def test_feed_content_type(self, server):
|
||||
resp = httpx.get(f"{server}/feed.xml")
|
||||
assert "application/rss+xml" in resp.headers["content-type"]
|
||||
|
||||
def test_feed_contains_entry(self, server):
|
||||
resp = httpx.get(f"{server}/feed.xml")
|
||||
assert "owner/repo" in resp.text
|
||||
assert "v1.0.0" in resp.text
|
||||
|
||||
def test_feed_is_valid_xml(self, server):
|
||||
import xml.etree.ElementTree as ET
|
||||
resp = httpx.get(f"{server}/feed.xml")
|
||||
root = ET.fromstring(resp.text)
|
||||
assert root.tag == "rss"
|
||||
|
||||
|
||||
class TestIndexEndpoint:
|
||||
def test_index_returns_200(self, server):
|
||||
resp = httpx.get(server + "/")
|
||||
assert resp.status_code == 200
|
||||
|
||||
def test_index_content_type(self, server):
|
||||
resp = httpx.get(server + "/")
|
||||
assert "text/html" in resp.headers["content-type"]
|
||||
|
||||
def test_index_links_to_feed(self, server):
|
||||
resp = httpx.get(server + "/")
|
||||
assert "/feed.xml" in resp.text
|
||||
|
||||
|
||||
class TestHealthEndpoint:
|
||||
def test_health_returns_200(self, server):
|
||||
resp = httpx.get(f"{server}/health")
|
||||
assert resp.status_code == 200
|
||||
|
||||
def test_health_body(self, server):
|
||||
resp = httpx.get(f"{server}/health")
|
||||
assert resp.text == "OK"
|
||||
|
||||
|
||||
class TestNotFound:
|
||||
def test_unknown_path(self, server):
|
||||
resp = httpx.get(f"{server}/unknown")
|
||||
assert resp.status_code == 404
|
||||
Reference in New Issue
Block a user