11import datetime as dt
22from unittest .mock import patch
33
4- from apps .downloads .models import Release , ReleaseFile
4+ from django .db import IntegrityError , transaction
5+ from django .db .models import URLField
6+
7+ from apps .downloads .models import OS , Release , ReleaseFile
58from apps .downloads .tests .base import BaseDownloadTests
69
710
@@ -160,7 +163,7 @@ def test_update_supernav(self):
160163 release = self .python_3 ,
161164 slug = slug ,
162165 name = "Python 3.10" ,
163- url = f"/ftp/python/{ slug } .zip" ,
166+ url = f"https://www.python.org /ftp/python/{ slug } .zip" ,
164167 download_button = True ,
165168 )
166169
@@ -179,7 +182,7 @@ def test_update_supernav(self):
179182 os = self .windows ,
180183 release = release ,
181184 name = "MSIX" ,
182- url = "/ftp/python/pymanager/pymanager-25.0.msix" ,
185+ url = "https://www.python.org /ftp/python/pymanager/pymanager-25.0.msix" ,
183186 download_button = True ,
184187 )
185188
@@ -199,7 +202,7 @@ def test_update_supernav_skips_os_without_files(self):
199202 """
200203 # Arrange
201204 from apps .boxes .models import Box
202- from apps .downloads .models import OS , update_supernav
205+ from apps .downloads .models import update_supernav
203206
204207 # Create an OS without any release files
205208 OS .objects .create (name = "Android" , slug = "android" )
@@ -215,7 +218,7 @@ def test_update_supernav_skips_os_without_files(self):
215218 release = self .python_3 ,
216219 slug = slug ,
217220 name = "Python 3.10" ,
218- url = f"/ftp/python/{ slug } .zip" ,
221+ url = f"https://www.python.org /ftp/python/{ slug } .zip" ,
219222 download_button = True ,
220223 )
221224
@@ -247,7 +250,7 @@ def test_release_file_save_triggers_box_updates(self, mock_home, mock_sources, m
247250 os = self .windows ,
248251 release = self .python_3 ,
249252 name = "Windows installer" ,
250- url = "/ftp/python/3.10.19/python-3.10.19.exe" ,
253+ url = "https://www.python.org /ftp/python/3.10.19/python-3.10.19.exe" ,
251254 download_button = True ,
252255 )
253256
@@ -268,7 +271,7 @@ def test_release_file_save_skips_unpublished_release(self, mock_home, mock_sourc
268271 os = self .windows ,
269272 release = self .draft_release ,
270273 name = "Windows installer draft" ,
271- url = "/ftp/python/9.7.2/python-9.7.2.exe" ,
274+ url = "https://www.python.org /ftp/python/9.7.2/python-9.7.2.exe" ,
272275 )
273276
274277 mock_supernav .assert_not_called ()
@@ -289,3 +292,22 @@ def test_release_file_delete_triggers_box_updates(self, mock_home, mock_sources,
289292 mock_supernav .assert_called ()
290293 mock_sources .assert_called ()
291294 mock_home .assert_called ()
295+
296+ def test_release_file_urls_not_python_dot_org (self ):
297+ for field in ReleaseFile ._meta .get_fields (): # noqa: SLF001
298+ if not isinstance (field , URLField ):
299+ continue
300+ with self .subTest (field .name ), transaction .atomic ():
301+ kwargs = {
302+ "url" : "https://www.python.org/ftp/python/9.7.2/python-9.7.2.exe" ,
303+ # field.name may be 'url', but will replace the default value.
304+ field .name : "https://notpython.com/python-9.7.2.txt" ,
305+ }
306+
307+ with self .assertRaises (IntegrityError ):
308+ ReleaseFile .objects .create (
309+ os = self .windows ,
310+ release = self .draft_release ,
311+ name = "Windows installer draft" ,
312+ ** kwargs ,
313+ )
0 commit comments