Skip to content

Commit 50e6c34

Browse files
committed
Added test case for feeds
1 parent 1a5e5a3 commit 50e6c34

File tree

2 files changed

+145
-7
lines changed

2 files changed

+145
-7
lines changed

publications/feeds.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,11 @@ def __init__(self, *args, **kwargs):
1111

1212
def add_root_elements(self, handler):
1313
super().add_root_elements(handler)
14-
14+
handler.startPrefixMapping('georss', 'http://www.georss.org/georss')
1515
handler.startPrefixMapping('geo', 'http://www.w3.org/2003/01/geo/wgs84_pos#')
1616

17-
if self.feed_type_variant in ["georss", "geoatom"]:
18-
handler.startPrefixMapping('georss', 'http://www.georss.org/georss')
19-
20-
if self.feed_type_variant in ["w3cgeo", "geoatom"]:
21-
handler.startPrefixMapping('geo', 'http://www.w3.org/2003/01/geo/wgs84_pos#')
17+
def rss_attributes(self):
18+
return {"version": self._version, "xmlns:atom": "http://www.w3.org/2005/Atom", "xmlns:georss": "http://www.georss.org/georss"}
2219

2320
def add_item_elements(self, handler, item):
2421
super().add_item_elements(handler, item)
@@ -38,6 +35,12 @@ def add_item_elements(self, handler, item):
3835

3936

4037
class CustomGeoAtomFeed(Atom1Feed):
38+
def root_attributes(self):
39+
attrs = super().root_attributes()
40+
attrs['xmlns:georss'] = 'http://www.georss.org/georss'
41+
attrs['xmlns:geo'] = 'http://www.w3.org/2003/01/geo/wgs84_pos#'
42+
return attrs
43+
4144
def add_root_elements(self, handler):
4245
super().add_root_elements(handler)
4346
handler.startPrefixMapping('georss', 'http://www.georss.org/georss')
@@ -56,7 +59,6 @@ def add_item_elements(self, handler, item):
5659
handler.addQuickElement("geo:lat", item["geo_lat"])
5760
handler.addQuickElement("geo:long", item["geo_long"])
5861

59-
6062
def _format_georss_geometry(geometry):
6163
georss_data = []
6264

tests/test_feeds.py

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import os
2+
import django
3+
4+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "optimap.settings")
5+
django.setup()
6+
7+
import xml.etree.ElementTree as ET
8+
from django.test import TestCase
9+
from django.contrib.gis.geos import Point, LineString, Polygon, GeometryCollection
10+
from datetime import datetime
11+
from publications.models import Publication
12+
13+
14+
class GeoFeedTestCase(TestCase):
15+
@classmethod
16+
def setUpTestData(cls):
17+
""" Set up test publications with geospatial data """
18+
cls.pub1 = Publication.objects.create(
19+
title="Point Test",
20+
abstract="Publication with a single point inside a collection.",
21+
url="https://example.com/point",
22+
status="p",
23+
publicationDate=datetime(2023, 5, 10),
24+
doi="10.1234/test-doi-1",
25+
geometry=GeometryCollection(Point(12.4924, 41.8902))
26+
)
27+
28+
cls.pub2 = Publication.objects.create(
29+
title="Polygon Test",
30+
abstract="Publication with a polygon inside a collection.",
31+
url="https://example.com/polygon",
32+
status="p",
33+
publicationDate=datetime(2023, 5, 15),
34+
doi="10.1234/test-doi-2",
35+
geometry=GeometryCollection(Polygon([
36+
(10.0, 50.0), (11.0, 51.0), (12.0, 50.0), (10.0, 50.0)
37+
]))
38+
)
39+
40+
cls.pub3 = Publication.objects.create(
41+
title="LineString Test",
42+
abstract="Publication with a linestring inside a collection.",
43+
url="https://example.com/linestring",
44+
status="p",
45+
publicationDate=datetime(2023, 5, 20),
46+
doi="10.1234/test-doi-3",
47+
geometry=GeometryCollection(LineString([(5.0, 45.0), (6.0, 46.0), (7.0, 45.5)]))
48+
)
49+
50+
def _fetch_feed(self, feed_type):
51+
""" Helper function to fetch RSS/Atom feed content """
52+
feed_urls = {
53+
"georss": "/feed/georss/",
54+
"geoatom": "/feed/geoatom/",
55+
"w3cgeo": "/feed/w3cgeo/",
56+
}
57+
58+
if feed_type not in feed_urls:
59+
self.fail(f"Invalid feed type requested: {feed_type}")
60+
61+
url = feed_urls[feed_type]
62+
response = self.client.get(url)
63+
64+
self.assertEqual(response.status_code, 200, f"Feed {feed_type} did not return 200")
65+
66+
xml_content = response.content.decode('utf-8')
67+
68+
return xml_content
69+
70+
def _compare_with_reference(self, generated_xml, filename):
71+
""" Save and compare generated XML with reference file """
72+
reference_path = os.path.join(os.path.dirname(__file__), "reference", filename)
73+
74+
if not os.path.exists(reference_path):
75+
os.makedirs(os.path.dirname(reference_path), exist_ok=True)
76+
with open(reference_path, "w", encoding="utf-8") as f:
77+
f.write(generated_xml)
78+
return
79+
80+
with open(reference_path, "r", encoding="utf-8") as f:
81+
reference_xml = f.read()
82+
83+
self.maxDiff = None
84+
85+
self.assertEqual(
86+
generated_xml.strip(),
87+
reference_xml.strip(),
88+
f"{filename} does not match reference! Check {reference_path}."
89+
)
90+
91+
def _extract_namespaces(self, xml_content):
92+
""" Extract namespaces dynamically from XML content """
93+
try:
94+
from io import StringIO
95+
xml_buffer = StringIO(xml_content)
96+
97+
namespace_map = {}
98+
for event, (prefix, uri) in ET.iterparse(xml_buffer, events=['start-ns']):
99+
namespace_map[prefix] = uri
100+
101+
return namespace_map
102+
except ET.ParseError as error:
103+
print("XML Parsing Error:", str(error))
104+
return {}
105+
106+
def _parse_xml(self, xml_content):
107+
""" Parse XML while handling namespace prefixes """
108+
try:
109+
return ET.ElementTree(ET.fromstring(xml_content))
110+
except ET.ParseError as e:
111+
print("XML Parse Error:", str(e))
112+
self.fail("Invalid XML response! Check namespace prefixes.")
113+
114+
def test_georss_feed(self):
115+
""" Test GeoRSS feed structure and content """
116+
georss_xml = self._fetch_feed("georss")
117+
self._compare_with_reference(georss_xml, "expected_georss.xml")
118+
119+
root = self._parse_xml(georss_xml).getroot()
120+
namespace = self._extract_namespaces(georss_xml)
121+
122+
points = root.findall(".//georss:point", namespaces=namespace)
123+
self.assertGreaterEqual(len(points), 1, "Expected at least one <georss:point> element")
124+
125+
def test_geoatom_feed(self):
126+
""" Test GeoAtom feed structure and content """
127+
geoatom_xml = self._fetch_feed("geoatom")
128+
self._compare_with_reference(geoatom_xml, "expected_geoatom.xml")
129+
130+
root = self._parse_xml(geoatom_xml).getroot()
131+
namespace = self._extract_namespaces(geoatom_xml)
132+
133+
latitudes = root.findall(".//geo:lat", namespaces=namespace)
134+
longitudes = root.findall(".//geo:long", namespaces=namespace)
135+
self.assertGreaterEqual(len(latitudes), 1, "Expected at least one <geo:lat> element")
136+
self.assertGreaterEqual(len(longitudes), 1, "Expected at least one <geo:long> element")

0 commit comments

Comments
 (0)