11import  logging 
22from  io  import  StringIO 
33
4- from  django .test  import  override_settings 
4+ from  django .test  import  RequestFactory ,  override_settings 
55from  rest_framework  import  status 
66from  rest_framework .test  import  APITransactionTestCase 
77
88from  qfieldcloud .authentication .models  import  AuthToken 
9+ from  qfieldcloud .core .exceptions  import  InvalidRangeError 
910from  qfieldcloud .core .models  import  (
1011    Person ,
1112    Project ,
1415from  qfieldcloud .core .tests .utils  import  (
1516    setup_subscription_plans ,
1617)
17- from  qfieldcloud .filestorage .utils  import  parse_range 
18+ from  qfieldcloud .filestorage .utils  import  get_range ,  parse_range_header 
1819
1920logging .disable (logging .CRITICAL )
2021
@@ -39,9 +40,9 @@ def setUp(self):
3940        )
4041
4142    def  test_parsing_range_function_succeeds (self ):
42-         self .assertEquals (parse_range ("bytes=4-8" , 10 ), (4 , 8 ))
43+         self .assertEquals (parse_range_header ("bytes=4-8" , 10 ), (4 , 8 ))
4344
44-         start_byte , end_byte  =  parse_range ("bytes=2-" , 10 )
45+         start_byte , end_byte  =  parse_range_header ("bytes=2-" , 10 )
4546
4647        self .assertEquals (start_byte , 2 )
4748        self .assertIsNone (end_byte )
@@ -50,39 +51,85 @@ def test_parsing_wrong_invalid_range_function_succeeds(self):
5051        file_size  =  1000000 
5152
5253        # not starting with 'bytes' 
53-         self .assertIsNone (parse_range ("byte=4-8" , file_size ))
54+         self .assertIsNone (parse_range_header ("byte=4-8" , file_size ))
5455
5556        # start byte can not be negative 
56-         self .assertIsNone (parse_range ("bytes=-1-15" , file_size ))
57+         self .assertIsNone (parse_range_header ("bytes=-1-15" , file_size ))
5758
5859        # start and end bytes can not be negative 
59-         self .assertIsNone (parse_range ("bytes=-10--15" , file_size ))
60+         self .assertIsNone (parse_range_header ("bytes=-10--15" , file_size ))
6061
6162        # start position cannot be greater than the end position 
62-         self .assertIsNone (parse_range ("bytes=9-1" , file_size ))
63+         self .assertIsNone (parse_range_header ("bytes=9-1" , file_size ))
6364
6465        # suffix ranges are not supported (yet), see https://www.rfc-editor.org/rfc/rfc9110.html#rule.suffix-range 
65-         self .assertIsNone (parse_range ("bytes=-5" , file_size ))
66+         self .assertIsNone (parse_range_header ("bytes=-5" , file_size ))
6667
6768        # bytes should be numbers 
68-         self .assertIsNone (parse_range ("bytes=one-two" , file_size ))
69+         self .assertIsNone (parse_range_header ("bytes=one-two" , file_size ))
6970        # whitespaces are not accepted 
70-         self .assertIsNone (parse_range ("bytes= 1-9" , file_size ))
71-         self .assertIsNone (parse_range ("bytes=1 -9" , file_size ))
72-         self .assertIsNone (parse_range ("bytes=1- 9" , file_size ))
73-         self .assertIsNone (parse_range ("bytes=1-9 " , file_size ))
74-         self .assertIsNone (parse_range ("bytes=1- " , file_size ))
75-         self .assertIsNone (parse_range (" bytes=1-9" , file_size ))
71+         self .assertIsNone (parse_range_header ("bytes= 1-9" , file_size ))
72+         self .assertIsNone (parse_range_header ("bytes=1 -9" , file_size ))
73+         self .assertIsNone (parse_range_header ("bytes=1- 9" , file_size ))
74+         self .assertIsNone (parse_range_header ("bytes=1-9 " , file_size ))
75+         self .assertIsNone (parse_range_header ("bytes=1- " , file_size ))
76+         self .assertIsNone (parse_range_header (" bytes=1-9" , file_size ))
7677        # typos in bytes 
77-         self .assertIsNone (parse_range ("bites=0-9" , file_size ))
78-         self .assertIsNone (parse_range ("starting bytes=0-9" , file_size ))
79-         self .assertIsNone (parse_range ("bytes=0-9 closing bytes" , file_size ))
78+         self .assertIsNone (parse_range_header ("bites=0-9" , file_size ))
79+         self .assertIsNone (parse_range_header ("starting bytes=0-9" , file_size ))
80+         self .assertIsNone (parse_range_header ("bytes=0-9 closing bytes" , file_size ))
8081        # empty range 
81-         self .assertIsNone (parse_range ("bytes=0-0" , file_size ))
82-         self .assertIsNone (parse_range ("bytes=1-1" , file_size ))
82+         self .assertIsNone (parse_range_header ("bytes=0-0" , file_size ))
83+         self .assertIsNone (parse_range_header ("bytes=1-1" , file_size ))
8384        # multiple ranges are not supported (yet), see https://www.rfc-editor.org/rfc/rfc9110.html#section-14.1.2-9.4.1 
84-         self .assertIsNone (parse_range ("bytes=1-5, 10-15" , file_size ))
85-         self .assertIsNone (parse_range ("bytes=1-5,10-15" , file_size ))
85+         self .assertIsNone (parse_range_header ("bytes=1-5, 10-15" , file_size ))
86+         self .assertIsNone (parse_range_header ("bytes=1-5,10-15" , file_size ))
87+ 
88+     def  test_get_range_function (self ):
89+         factory  =  RequestFactory ()
90+ 
91+         request  =  factory .get ("" )
92+         range  =  get_range (request , 10 )
93+ 
94+         self .assertIsNone (range )
95+ 
96+         request  =  factory .get ("" , headers = {"Range" : "bytes=4-8" })
97+         range  =  get_range (request , 10 )
98+ 
99+         self .assertIsNotNone (range )
100+         # typing is not aware that the above function checks for `None` 
101+         assert  range 
102+ 
103+         self .assertEqual (range .start , 4 )
104+         self .assertEqual (range .end , 8 )
105+         self .assertEqual (range .length , 5 )
106+         self .assertEqual (range .total_size , 10 )
107+         self .assertEqual (range .header , "bytes=4-8" )
108+ 
109+         request  =  factory .get ("" , headers = {"Range" : "bytes=4-8" })
110+ 
111+         request  =  factory .get ("" , headers = {"Range" : "bytes=2-" })
112+         range  =  get_range (request , 10 )
113+ 
114+         self .assertIsNotNone (range )
115+         assert  range 
116+ 
117+         self .assertEqual (range .start , 2 )
118+         self .assertEqual (range .end , 9 )
119+         self .assertEqual (range .length , 8 )
120+         self .assertEqual (range .total_size , 10 )
121+         self .assertEqual (range .header , "bytes=2-" )
122+ 
123+         request  =  factory .get ("" , headers = {"Range" : "bytes= 2 - " })
124+ 
125+         with  self .assertRaises (InvalidRangeError ):
126+             get_range (request , 10 )
127+ 
128+         with  override_settings (QFIELDCLOUD_MINIMUM_RANGE_HEADER_LENGTH = 3 ):
129+             request  =  factory .get ("" , headers = {"Range" : "bytes=0-1" })
130+ 
131+             with  self .assertRaises (InvalidRangeError ):
132+                 get_range (request , 10 )
86133
87134    def  test_upload_file_then_download_range_succeeds (self ):
88135        for  project  in  [self .project_default_storage , self .project_webdav_storage ]:
0 commit comments