Files
org-social-relay/app/feeds/test_discover_relay_nodes.py
2026-01-19 14:50:13 +01:00

204 lines
7.8 KiB
Python

from django.test import TestCase
from django.conf import settings
from unittest.mock import patch, Mock
from app.feeds.models import Feed
from app.feeds.tasks import discover_feeds_from_relay_nodes
class DiscoverRelayNodesTest(TestCase):
"""Test cases for discover_feeds_from_relay_nodes task using Given/When/Then structure."""
def setUp(self):
"""Set up test fixtures."""
# Clear any existing feeds
Feed.objects.all().delete()
def test_discovers_feeds_from_real_relay_list(self):
"""Test that feeds are discovered from the actual relay list file."""
# Given: The real relay list file exists (data/relay-list.txt)
# And we mock the HTTP responses from relays
# Mock relay response
mock_relay_response = Mock()
mock_relay_response.status_code = 200
mock_relay_response.json.return_value = {
"type": "Success",
"data": [
"https://test-feed1.com/social.org",
"https://test-feed2.com/social.org",
],
}
mock_relay_response.raise_for_status = Mock()
# Mock feed validation responses
mock_feed_response = Mock()
mock_feed_response.status_code = 200
mock_feed_response.text = """#+TITLE: Test Feed
#+NICK: testuser
#+DESCRIPTION: Test description
* Posts
"""
mock_feed_response.content = mock_feed_response.text.encode("utf-8")
mock_feed_response.history = []
def mock_requests_get(url, timeout=None):
if "/feeds" in url:
return mock_relay_response
# Feed validation requests
mock_feed_response.url = url
mock_feed_response.raise_for_status = Mock()
return mock_feed_response
# When: We run the discovery task
with patch("requests.get", side_effect=mock_requests_get):
discover_feeds_from_relay_nodes()
# Then: Feeds should be discovered (the real relay list has relays)
# We can't predict exact count since it depends on real file content
# So we just verify the function runs without errors
self.assertGreaterEqual(Feed.objects.count(), 0)
def test_handles_relay_connection_failure(self):
"""Test that the task handles relay connection failures gracefully."""
# Given: The real relay list file exists
# And relay connections will fail
# Mock a connection error
with patch("requests.get", side_effect=Exception("Connection failed")):
# When: We run the discovery task
initial_count = Feed.objects.count()
discover_feeds_from_relay_nodes()
# Then: No new feeds should be created and no exception raised
self.assertEqual(Feed.objects.count(), initial_count)
def test_skips_existing_feeds(self):
"""Test that the task skips feeds that already exist."""
# Given: A feed already exists
Feed.objects.create(url="https://existing-feed.com/social.org")
initial_count = Feed.objects.count()
# Mock relay response with the existing feed
mock_relay_response = Mock()
mock_relay_response.status_code = 200
mock_relay_response.json.return_value = {
"type": "Success",
"data": [
"https://existing-feed.com/social.org", # Already exists
],
}
mock_relay_response.raise_for_status = Mock()
def mock_requests_get(url, timeout=None):
if "/feeds" in url:
return mock_relay_response
# Should not validate existing feed
if "existing-feed" in url:
self.fail("Should not validate existing feed")
return Mock()
# When: We run the discovery task
with patch("requests.get", side_effect=mock_requests_get):
discover_feeds_from_relay_nodes()
# Then: No new feeds should be added
self.assertEqual(Feed.objects.count(), initial_count)
def test_handles_invalid_feed_during_validation(self):
"""Test that the task skips invalid feeds during validation."""
# Given: The real relay list file exists
# Mock relay response with valid and invalid feeds
mock_relay_response = Mock()
mock_relay_response.status_code = 200
mock_relay_response.json.return_value = {
"type": "Success",
"data": [
"https://valid-feed.com/social.org",
"https://invalid-feed.com/social.org",
],
}
mock_relay_response.raise_for_status = Mock()
# Mock feed validation responses
def mock_requests_get(url, timeout=None):
if "/feeds" in url:
return mock_relay_response
# Valid feed response
if "valid-feed" in url:
mock_response = Mock()
mock_response.status_code = 200
mock_response.text = """#+TITLE: Valid Feed
#+NICK: validuser
#+DESCRIPTION: Valid description
* Posts
"""
mock_response.content = mock_response.text.encode("utf-8")
mock_response.url = url
mock_response.history = []
mock_response.raise_for_status = Mock()
return mock_response
# Invalid feed response (missing required fields)
if "invalid-feed" in url:
mock_response = Mock()
mock_response.status_code = 200
mock_response.text = "Invalid content without Org Social headers"
mock_response.content = mock_response.text.encode("utf-8")
mock_response.url = url
mock_response.history = []
mock_response.raise_for_status = Mock()
return mock_response
# Default response for any other URL
mock_response = Mock()
mock_response.status_code = 404
mock_response.raise_for_status = Mock(side_effect=Exception("404"))
return mock_response
# When: We run the discovery task
initial_count = Feed.objects.count()
with patch("requests.get", side_effect=mock_requests_get):
discover_feeds_from_relay_nodes()
# Then: Only the valid feed should be added (or none if validation failed for both)
# We verify that invalid feed was not added
self.assertFalse(
Feed.objects.filter(url="https://invalid-feed.com/social.org").exists()
)
# And if valid feed was processed, it should exist
# (we're more lenient here as the test DB might interfere)
if Feed.objects.count() > initial_count:
self.assertTrue(
Feed.objects.filter(url="https://valid-feed.com/social.org").exists()
)
def test_filters_out_own_domain(self):
"""Test that the task filters out its own domain from relay list."""
# Given: The real relay list file exists
# We verify the function doesn't try to fetch from its own domain
own_domain = settings.SITE_DOMAIN
def mock_requests_get(url, timeout=None):
# Should not be called for our own domain
if own_domain in url and "/feeds" in url:
self.fail(f"Should not fetch from own domain: {url}")
# Mock response for other relays
mock_response = Mock()
mock_response.status_code = 200
mock_response.json.return_value = {"type": "Success", "data": []}
mock_response.raise_for_status = Mock()
return mock_response
# When: We run the discovery task
with patch("requests.get", side_effect=mock_requests_get):
discover_feeds_from_relay_nodes()
# Then: The function should complete without calling own domain
# (if it called own domain, the test would fail in mock_requests_get)