11import datetime as dt
22from unittest .mock import patch
33
4+ from django .core .exceptions import ValidationError
45from django .db import IntegrityError , transaction
56from django .db .models import URLField
67
7- from apps .downloads .models import OS , Release , ReleaseFile
8+ from apps .downloads .models import (
9+ OS ,
10+ RELEASE_FILE_SIDECAR_SUFFIXES ,
11+ RELEASE_FILE_URL_FIELDS ,
12+ Release ,
13+ ReleaseFile ,
14+ )
815from apps .downloads .tests .base import BaseDownloadTests
916
1017
18+ def release_file_url_field_names ():
19+ return tuple (
20+ field .name
21+ for field in ReleaseFile ._meta .get_fields () # noqa: SLF001
22+ if isinstance (field , URLField )
23+ )
24+
25+
1126class DownloadModelTests (BaseDownloadTests ):
1227 def test_stringification (self ):
1328 self .assertEqual (str (self .osx ), "macOS" )
@@ -300,7 +315,7 @@ def test_release_file_urls_not_python_dot_org(self):
300315 with self .subTest (field .name ), transaction .atomic ():
301316 kwargs = {
302317 "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.
318+ # field.name may be " url" , but will replace the default value.
304319 field .name : "https://notpython.com/python-9.7.2.txt" ,
305320 }
306321
@@ -311,3 +326,96 @@ def test_release_file_urls_not_python_dot_org(self):
311326 name = "Windows installer draft" ,
312327 ** kwargs ,
313328 )
329+
330+ def test_release_file_rejects_new_http_urls (self ):
331+ for field_name in RELEASE_FILE_URL_FIELDS :
332+ with self .subTest (field_name ):
333+ kwargs = {
334+ "url" : "https://www.python.org/ftp/python/9.7.2/python-9.7.2.exe" ,
335+ # field_name may be 'url', but will replace the default value.
336+ field_name : "http://www.python.org/ftp/python/9.7.2/python-9.7.2.exe" ,
337+ }
338+ release_file = ReleaseFile (
339+ os = self .windows ,
340+ release = self .draft_release ,
341+ name = "Windows installer draft" ,
342+ slug = f"windows-installer-draft-{ field_name } " ,
343+ ** kwargs ,
344+ )
345+
346+ with self .assertRaises (ValidationError ) as cm :
347+ release_file .full_clean ()
348+ self .assertIn (field_name , cm .exception .message_dict )
349+
350+ def test_release_file_url_fields_cover_model_url_fields (self ):
351+ self .assertEqual (RELEASE_FILE_URL_FIELDS , release_file_url_field_names ())
352+
353+ def test_release_file_sidecar_suffixes_cover_sidecar_url_fields (self ):
354+ sidecar_field_names = set (RELEASE_FILE_URL_FIELDS ) - {"url" }
355+
356+ self .assertEqual (set (RELEASE_FILE_SIDECAR_SUFFIXES ), sidecar_field_names )
357+
358+ def test_release_file_allows_existing_http_urls_to_be_edited (self ):
359+ release_file = ReleaseFile .objects .create (
360+ os = self .windows ,
361+ release = self .draft_release ,
362+ name = "Windows installer draft" ,
363+ url = "http://www.python.org/ftp/python/9.7.2/python-9.7.2.exe" ,
364+ )
365+
366+ release_file .description = "Updated legacy metadata"
367+
368+ release_file .full_clean ()
369+
370+ def test_release_file_rejects_existing_mismatched_sidecar_urls (self ):
371+ artifact_url = "https://www.python.org/ftp/python/9.7.2/Python-9.7.2-sidecar.tgz"
372+ release_file = ReleaseFile .objects .create (
373+ os = self .linux ,
374+ release = self .draft_release ,
375+ name = "Source tarball draft" ,
376+ slug = "source-tarball-draft-mismatch" ,
377+ url = artifact_url ,
378+ gpg_signature_file = artifact_url .replace ("9.7.2" , "9.7.1" ) + ".asc" ,
379+ )
380+
381+ release_file .description = "Updated metadata"
382+
383+ with self .assertRaises (ValidationError ) as cm :
384+ release_file .full_clean ()
385+ self .assertIn ("gpg_signature_file" , cm .exception .message_dict )
386+
387+ def test_release_file_sidecar_urls_must_extend_artifact_url (self ):
388+ artifact_url = "https://www.python.org/ftp/python/9.7.2/Python-9.7.2-sidecar.tgz"
389+
390+ for field_name , suffix in RELEASE_FILE_SIDECAR_SUFFIXES .items ():
391+ with self .subTest (field_name ):
392+ wrong_artifact_url = artifact_url .replace ("9.7.2" , "9.7.1" )
393+ release_file = ReleaseFile (
394+ os = self .linux ,
395+ release = self .draft_release ,
396+ name = "Source tarball draft" ,
397+ slug = f"source-tarball-draft-{ field_name } " ,
398+ url = artifact_url ,
399+ ** {field_name : f"{ wrong_artifact_url } { suffix } " },
400+ )
401+
402+ with self .assertRaises (ValidationError ) as cm :
403+ release_file .full_clean ()
404+ self .assertIn (field_name , cm .exception .message_dict )
405+
406+ def test_release_file_accepts_sidecar_urls_for_same_artifact (self ):
407+ artifact_url = "https://www.python.org/ftp/python/9.7.2/Python-9.7.2-sidecar.tgz"
408+ sidecar_urls = {}
409+ for field_name , suffix in RELEASE_FILE_SIDECAR_SUFFIXES .items ():
410+ sidecar_urls [field_name ] = f"{ artifact_url } { suffix } "
411+
412+ release_file = ReleaseFile (
413+ os = self .linux ,
414+ release = self .draft_release ,
415+ name = "Source tarball draft" ,
416+ slug = "source-tarball-draft" ,
417+ url = artifact_url ,
418+ ** sidecar_urls ,
419+ )
420+
421+ release_file .full_clean ()
0 commit comments