1
1
import os
2
+ import tempfile
3
+ import time
4
+ from datetime import timedelta
2
5
from shutil import copyfile
6
+ from unittest .mock import patch , MagicMock
7
+
8
+ from django .utils import timezone
9
+ from rasterio .errors import RasterioIOError
10
+ from storages .backends .s3 import S3Storage
3
11
4
12
from core .settings .utils import absolute_path
5
13
from cplus_api .models .layer import (
6
14
InputLayer ,
7
15
COMMON_LAYERS_DIR
8
16
)
9
- from cplus_api .tasks .sync_default_layers import sync_default_layers
17
+ from cplus_api .tasks .sync_default_layers import (
18
+ sync_default_layers ,
19
+ ProcessFile
20
+ )
10
21
from cplus_api .tests .common import BaseAPIViewTransactionTest
22
+ from cplus_api .tests .factories import InputLayerF
11
23
12
24
13
25
class TestSyncDefaultLayer (BaseAPIViewTransactionTest ):
14
26
def setUp (self , * args , ** kwargs ):
15
27
super ().setUp (* args , ** kwargs )
16
- # print(help(self))
17
- # breakpoint()
18
28
self .superuser .username = os .getenv ('ADMIN_USERNAME' )
19
29
self .superuser .save ()
20
30
@@ -73,6 +83,7 @@ def test_new_layer(self):
73
83
74
84
def test_file_updated (self ):
75
85
input_layer , source_path , dest_path = self .base_run ()
86
+ time .sleep (5 )
76
87
first_modified_on = input_layer .modified_on
77
88
copyfile (source_path , dest_path )
78
89
sync_default_layers ()
@@ -87,3 +98,128 @@ def test_file_updated(self):
87
98
input_layer .refresh_from_db ()
88
99
self .assertEqual (input_layer .name , 'New Name' )
89
100
self .assertEqual (input_layer .description , 'New Description' )
101
+
102
+ def test_delete_invalid_layers (self ):
103
+ input_layer , source_path , dest_path = self .base_run ()
104
+ invalid_common_layer_1 = InputLayerF .create (
105
+ name = '' ,
106
+ privacy_type = InputLayer .PrivacyTypes .COMMON ,
107
+ file = input_layer .file
108
+ )
109
+ invalid_common_layer_2 = InputLayerF .create (
110
+ name = 'invalid_common_layer_2' ,
111
+ privacy_type = InputLayer .PrivacyTypes .COMMON ,
112
+ file = None
113
+ )
114
+ private_layer_1 = InputLayerF .create (
115
+ name = '' ,
116
+ privacy_type = InputLayer .PrivacyTypes .PRIVATE ,
117
+ file = input_layer .file
118
+ )
119
+ private_layer_2 = InputLayerF .create (
120
+ name = 'private_layer_2' ,
121
+ privacy_type = InputLayer .PrivacyTypes .PRIVATE
122
+ )
123
+
124
+ sync_default_layers ()
125
+
126
+ # Calling refresh_from_db() on these 2 variable would result
127
+ # in InputLayer.DoesNotExist as they have been deleted,
128
+ # because they are invalid common layers
129
+ with self .assertRaises (InputLayer .DoesNotExist ):
130
+ invalid_common_layer_1 .refresh_from_db ()
131
+ with self .assertRaises (InputLayer .DoesNotExist ):
132
+ invalid_common_layer_2 .refresh_from_db ()
133
+
134
+ # These layers are not deleted, so we could still call refresh_from_db
135
+ private_layer_1 .refresh_from_db ()
136
+ private_layer_2 .refresh_from_db ()
137
+
138
+ def test_invalid_input_layers_not_created (self ):
139
+ source_path = absolute_path (
140
+ 'cplus_api' , 'tests' , 'data' ,
141
+ 'pathways' , 'test_pathway_2.tif'
142
+ )
143
+ dest_path = (
144
+ f'/home/web/media/minio_test/{ COMMON_LAYERS_DIR } /'
145
+ f'{ InputLayer .ComponentTypes .NCS_PATHWAY } /test_pathway_2.tif'
146
+ )
147
+ os .makedirs (os .path .dirname (dest_path ), exist_ok = True )
148
+ copyfile (source_path , dest_path )
149
+ with patch .object (
150
+ ProcessFile , 'read_metadata' , autospec = True
151
+ ) as mock_read_metadata :
152
+ mock_read_metadata .side_effect = [
153
+ RasterioIOError ('error' ),
154
+ RasterioIOError ('error' ),
155
+ RasterioIOError ('error' )
156
+ ]
157
+ sync_default_layers ()
158
+
159
+ self .assertFalse (InputLayer .objects .exists ())
160
+
161
+ def test_invalid_input_layers_not_deleted (self ):
162
+ input_layer , source_path , dest_path = self .base_run ()
163
+ time .sleep (5 )
164
+ first_modified_on = input_layer .modified_on
165
+ copyfile (source_path , dest_path )
166
+ sync_default_layers ()
167
+ with patch .object (
168
+ ProcessFile , 'read_metadata' , autospec = True
169
+ ) as mock_read_metadata :
170
+ mock_read_metadata .side_effect = [
171
+ RasterioIOError ('error' ),
172
+ RasterioIOError ('error' ),
173
+ RasterioIOError ('error' )
174
+ ]
175
+ sync_default_layers ()
176
+
177
+ self .assertTrue (InputLayer .objects .exists ())
178
+
179
+ # Check modified_on is updated
180
+ input_layer .refresh_from_db ()
181
+ self .assertNotEquals (input_layer .modified_on , first_modified_on )
182
+
183
+ def run_s3 (self , mock_storage , mock_named_tmp_file = None ):
184
+ source_path = absolute_path (
185
+ 'cplus_api' , 'tests' , 'data' ,
186
+ 'pathways' , 'test_pathway_2.tif'
187
+ )
188
+ dest_path = (
189
+ f'/home/web/media/minio_test/{ COMMON_LAYERS_DIR } /'
190
+ f'{ InputLayer .ComponentTypes .NCS_PATHWAY } /test_pathway_2.tif'
191
+ )
192
+ os .makedirs (os .path .dirname (dest_path ), exist_ok = True )
193
+ copyfile (source_path , dest_path )
194
+
195
+ storage = S3Storage (bucket_name = 'test-bucket' )
196
+ s3_client = MagicMock ()
197
+ s3_client .list_objects .return_value = {
198
+ 'Contents' : [
199
+ {
200
+ 'Key' : 'common_layers/ncs_pathway/test_pathway_2.tif' ,
201
+ 'LastModified' : timezone .now () + timedelta (days = 1 )
202
+ }
203
+ ]
204
+ }
205
+ storage .connection .meta .client = s3_client
206
+ mock_storage .return_value = storage
207
+ if mock_named_tmp_file :
208
+ (mock_named_tmp_file .return_value .
209
+ __enter__ .return_value ).name = dest_path
210
+ sync_default_layers ()
211
+
212
+ @patch ('cplus_api.tasks.sync_default_layers.select_input_layer_storage' )
213
+ def test_invalid_input_layers_not_created_s3 (self , mock_storage ):
214
+ self .run_s3 (mock_storage )
215
+ self .assertFalse (InputLayer .objects .exists ())
216
+
217
+ @patch ('cplus_api.tasks.sync_default_layers.select_input_layer_storage' )
218
+ @patch .object (tempfile , 'NamedTemporaryFile' )
219
+ def test_invalid_input_layers_created_s3 (
220
+ self ,
221
+ mock_named_tmp_file ,
222
+ mock_storage
223
+ ):
224
+ self .run_s3 (mock_storage , mock_named_tmp_file )
225
+ self .assertTrue (InputLayer .objects .exists ())
0 commit comments