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