Completed
Pull Request — master (#44)
by Paolo
05:54
created

biosample.tests.test_tasks_submission   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 483
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 27
eloc 248
dl 0
loc 483
rs 10
c 0
b 0
f 0

25 Methods

Rating   Name   Duplication   Size   Complexity  
A SubmitTaskTestCase.test_unmanaged() 0 13 1
A SubmitTaskTestCase.common_test() 0 20 1
A SubmissionHelperTestCase.setUp() 0 16 1
A SubmissionHelperTestCase.test_properties() 0 14 1
A SubmissionHelperTestCase.test_update_sample() 0 24 2
A SplitSubmissionTaskTestCase.setUp() 0 10 1
A SubmissionHelperTestCase.test_recover_submission_error() 0 21 1
A SubmissionHelperTestCase.test_start_submission() 0 22 1
A SubmissionHelperTestCase.test_mark_submission() 0 14 1
A SubmitTaskTestCase.setUp() 0 24 1
A SplitSubmissionTaskTestCase.generic_check() 0 23 2
A SubmitTaskTestCase.test_submit() 0 9 1
A SubmissionHelperTestCase.test_read_samples() 0 23 1
A SplitSubmissionTaskTestCase.tearDown() 0 6 1
A SplitSubmissionTaskTestCase.test_sample_already_in_submission() 0 11 1
A SplitSubmissionTaskTestCase.test_split_submission() 0 8 1
A SubmitTaskTestCase.test_token_expired() 0 14 1
A SubmissionHelperTestCase.test_read_token() 0 8 1
A SubmissionHelperTestCase.test_create_sample() 0 14 1
A SubmissionHelperTestCase.test_create_submission() 0 14 1
A SubmitTaskTestCase.tearDown() 0 8 1
A SubmissionHelperTestCase.test_add_samples() 0 14 1
A SplitSubmissionTaskTestCase.test_split_submission_partial() 0 9 1
A SubmitTaskTestCase.test_issues_with_api() 0 15 1
A SubmissionHelperTestCase.test_recover_submission() 0 26 1
1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3
"""
4
Created on Wed Jul 17 10:51:40 2019
5
6
@author: Paolo Cozzi <[email protected]>
7
"""
8
9
from unittest.mock import patch, PropertyMock, Mock
10
11
from django.test import TestCase
12
13
from common.constants import READY, COMPLETED, ERROR, SUBMITTED, WAITING
14
15
from .common import TaskFailureMixin, RedisMixin, BaseMixin
16
from ..models import Submission as USISubmission, SubmissionData
17
from ..tasks.submission import (
18
    SubmissionHelper, SplitSubmissionTask, SubmitTask)
19
20
21
class SplitSubmissionTaskTestCase(TaskFailureMixin, TestCase):
22
    def setUp(self):
23
        # call Mixin method
24
        super().setUp()
25
26
        # setting tasks
27
        self.my_task = SplitSubmissionTask()
28
29
        # patching objects
30
        self.mock_chord_patcher = patch('biosample.tasks.submission.chord')
31
        self.mock_chord = self.mock_chord_patcher.start()
32
33
        # other function are not called since chord is patched
34
35
    def tearDown(self):
36
        # stopping mock objects
37
        self.mock_chord_patcher.stop()
38
39
        # calling base object
40
        super().tearDown()
41
42
    def generic_check(self, res, n_of_submission, n_of_submissiondata):
43
        """Generic check for created data"""
44
45
        # assert a success with data uploading
46
        self.assertEqual(res, "success")
47
48
        # get usi_submission qs
49
        usi_submissions_qs = USISubmission.objects.all()
50
51
        # asserting two biosample.submission data objects
52
        self.assertEqual(usi_submissions_qs.count(), n_of_submission)
53
54
        # assert two data for each submission
55
        for usi_submission in usi_submissions_qs:
56
            self.assertEqual(usi_submission.status, READY)
57
58
            # grab submission data queryset
59
            submission_data_qs = SubmissionData.objects.filter(
60
                submission=usi_submission)
61
            self.assertEqual(submission_data_qs.count(), n_of_submissiondata)
62
63
        # assert mock objects called
64
        self.assertTrue(self.mock_chord.called)
65
66
    # ovverride MAX_SAMPLES in order to split data
67
    @patch('biosample.tasks.submission.MAX_SAMPLES', 2)
68
    def test_split_submission(self):
69
        """Test splitting submission data"""
70
71
        res = self.my_task.run(submission_id=self.submission_id)
72
73
        self.generic_check(res, n_of_submission=2, n_of_submissiondata=2)
74
        self.assertEqual(self.n_to_submit, SubmissionData.objects.count())
75
76
    # ovverride MAX_SAMPLES in order to split data
77
    @patch('biosample.tasks.submission.MAX_SAMPLES', 2)
78
    def test_split_submission_partial(self):
79
        """Test splitting submission data with some data already submitted"""
80
81
        self.name_qs.filter(pk__in=[3, 4]).update(status=COMPLETED)
82
83
        res = self.my_task.run(submission_id=self.submission_id)
84
85
        self.generic_check(res, n_of_submission=1, n_of_submissiondata=2)
86
87
    @patch('biosample.tasks.submission.MAX_SAMPLES', 2)
88
    def test_sample_already_in_submission(self):
89
        """Test splitting submission with sample in a opened submission"""
90
91
        # call task once
92
        self.my_task.run(submission_id=self.submission_id)
93
94
        # call a task a second time
95
        res = self.my_task.run(submission_id=self.submission_id)
96
97
        self.generic_check(res, n_of_submission=2, n_of_submissiondata=2)
98
99
100
class SubmissionHelperTestCase(BaseMixin, RedisMixin, TestCase):
101
102
    # overriding BaseMixin features
103
    fixtures = [
104
        'biosample/account',
105
        'biosample/managedteam',
106
        'biosample/submission',
107
        'biosample/submissiondata',
108
        'image_app/animal',
109
        'image_app/dictbreed',
110
        'image_app/dictcountry',
111
        'image_app/dictrole',
112
        'image_app/dictsex',
113
        'image_app/dictspecie',
114
        'image_app/dictstage',
115
        'image_app/dictuberon',
116
        'image_app/name',
117
        'image_app/ontology',
118
        'image_app/organization',
119
        'image_app/publication',
120
        'image_app/sample',
121
        'image_app/submission',
122
        'image_app/user'
123
    ]
124
125
    def setUp(self):
126
        # call Mixin method
127
        super().setUp()
128
129
        # set my pk
130
        self.usi_submission_id = 1
131
132
        # Instantiating SubmissionHelper with biosample.submission pk
133
        self.submission_helper = SubmissionHelper(self.usi_submission_id)
134
135
        # get a biosample.submission object
136
        self.usi_submission = USISubmission.objects.get(
137
            pk=self.usi_submission_id)
138
139
        # set attributes with class baseMixin class attributes for semplicity
140
        self.submission_helper.root = self.my_root
141
142
    def test_properties(self):
143
        """Asserting read properties"""
144
145
        owner = self.submission_helper.owner
146
        self.assertEqual(owner.username, "test")
147
148
        team_name = self.submission_helper.team_name
149
        self.assertEqual(team_name, "subs.test-team-1")
150
151
        self.assertIsNone(self.submission_helper.usi_submission_name)
152
        self.submission_helper.usi_submission_name = "test"
153
154
        self.usi_submission.refresh_from_db()
155
        self.assertEqual(self.usi_submission.usi_submission_name, "test")
156
157
    def test_read_token(self):
158
        """testing token from redis DB"""
159
160
        token = self.submission_helper.read_token()
161
        self.assertEqual(self.token, token)
162
163
        # assert called mock objects
164
        self.assertTrue(self.mock_root.called)
165
166
    @patch.object(SubmissionHelper, "read_samples")
167
    def test_recover_submission(self, my_helper):
168
        """Testing submission recover"""
169
170
        # base case: no usi_submission_name so False is expected
171
        self.assertFalse(self.submission_helper.recover_submission())
172
173
        # assign a usi_submission_name
174
        self.submission_helper.usi_submission_name = "test-submission"
175
176
        # asserted final returned status
177
        self.assertTrue(self.submission_helper.recover_submission())
178
179
        # assert a recovered document
180
        self.assertEqual(
181
            "test-submission",
182
            self.submission_helper.usi_submission.name)
183
184
        # assert get_samples called functions called
185
        self.assertTrue(my_helper.called)
186
187
        # asserting others mock objects called
188
        self.assertFalse(self.my_root.get_team_by_name.called)
189
        self.assertFalse(self.my_team.create_submission.called)
190
        self.assertTrue(self.my_root.get_submission_by_name.called)
191
        self.assertTrue(self.my_submission.propertymock.called)
192
193
    def test_recover_submission_error(self):
194
        """Testing submission recover for a closed submission"""
195
196
        # assign a usi_submission_name
197
        self.submission_helper.usi_submission_name = "test-submission"
198
199
        # change submission status
200
        self.my_submission.propertymock = PropertyMock(
201
            return_value='Completed')
202
        type(self.my_submission).status = self.my_submission.propertymock
203
204
        self.assertRaisesRegex(
205
            Exception,
206
            "Cannot recover submission",
207
            self.submission_helper.recover_submission)
208
209
        # asserting others mock objects called
210
        self.assertFalse(self.my_root.get_team_by_name.called)
211
        self.assertFalse(self.my_team.create_submission.called)
212
        self.assertTrue(self.my_root.get_submission_by_name.called)
213
        self.assertTrue(self.my_submission.propertymock.called)
214
215
    def test_create_submission(self):
216
        """Testing submission create"""
217
218
        self.submission_helper.create_submission()
219
220
        # assert a new document
221
        self.assertEqual(
222
            "new-submission",
223
            self.submission_helper.usi_submission.name)
224
225
        # asserting others mock objects called
226
        self.assertTrue(self.my_root.get_team_by_name.called)
227
        self.assertTrue(self.my_team.create_submission.called)
228
        self.assertFalse(self.my_root.get_submission_by_name.called)
229
230
    @patch.object(SubmissionHelper, "create_submission")
231
    @patch.object(SubmissionHelper, "recover_submission")
232
    def test_start_submission(self, my_recover, my_create):
233
        """testing start a submission"""
234
235
        def create_submission():
236
            """Simulate create submission (already tested)"""
237
238
            self.submission_helper.usi_submission = self.new_submission
239
            return self.new_submission
240
241
        my_recover.return_value = False
242
        my_create.side_effect = create_submission
243
244
        usi_submission = self.submission_helper.start_submission()
245
246
        # assert a new document
247
        self.assertEqual("new-submission", usi_submission.name)
248
249
        # assert mock methods called
250
        self.assertTrue(my_recover.called)
251
        self.assertTrue(my_create.called)
252
253
    def test_read_samples(self):
254
255
        # creating mock samples
256
        my_samples = [
257
            Mock(**{'alias': 'IMAGEA000000001',
258
                    'title': 'a 4-year old pig organic fed'}),
259
            Mock(**{'alias': 'IMAGES000000001',
260
                    'title': 'semen collected when the animal turns to 4'}),
261
        ]
262
263
        # mocking set samples
264
        self.my_submission.get_samples.return_value = my_samples
265
        self.submission_helper.usi_submission = self.my_submission
266
267
        # calling function
268
        submitted_samples = self.submission_helper.read_samples()
269
270
        self.assertIsInstance(submitted_samples, dict)
271
        self.assertEqual(len(submitted_samples), 2)
272
273
        keys = submitted_samples.keys()
274
        self.assertIn('IMAGEA000000001', keys)
275
        self.assertIn('IMAGES000000001', keys)
276
277
    def test_create_sample(self):
278
        # get a model object
279
        submission_data = SubmissionData.objects.get(pk=1)
280
        model = submission_data.content_object
281
282
        # get a biosample submission
283
        self.submission_helper.usi_submission = self.my_submission
284
285
        # add model to biosample submission
286
        self.submission_helper.create_or_update_sample(model)
287
288
        # testing things
289
        self.assertEqual(
290
            self.my_submission.create_sample.call_count, 1)
291
292
    def test_update_sample(self):
293
        # creating mock samples
294
        my_samples = [
295
            Mock(**{'alias': 'IMAGEA000000001',
296
                    'title': 'a 4-year old pig organic fed'}),
297
        ]
298
299
        # mocking set samples
300
        self.my_submission.get_samples.return_value = my_samples
301
        self.submission_helper.usi_submission = self.my_submission
302
303
        # read samples through function (already tested)
304
        self.submission_helper.read_samples()
305
306
        # get a model object
307
        submission_data = SubmissionData.objects.get(pk=1)
308
        model = submission_data.content_object
309
310
        # add model to biosample submission
311
        self.submission_helper.create_or_update_sample(model)
312
313
        # testing patch
314
        for sample in my_samples:
315
            self.assertTrue(sample.patch.called)
316
317
    @patch.object(SubmissionHelper, "create_or_update_sample")
318
    def test_add_samples(self, my_create):
319
        """Test adding samples"""
320
321
        # simulate a submission recover: mark an animal as already submitted
322
        submission_data = SubmissionData.objects.get(pk=1)
323
        submission_data.content_object.name.status = SUBMITTED
324
        submission_data.content_object.name.save()
325
326
        # calling method
327
        self.submission_helper.add_samples()
328
329
        # assert create sample in biosample called once
330
        my_create.assert_called_once()
331
332
    def test_mark_submission(self):
333
        """test adding status message to submission"""
334
335
        self.submission_helper.mark_fail(message="test")
336
337
        self.usi_submission.refresh_from_db()
338
        self.usi_submission.status = ERROR
339
        self.usi_submission.message = "test"
340
341
        self.submission_helper.mark_success()
342
343
        self.usi_submission.refresh_from_db()
344
        self.usi_submission.status = SUBMITTED
345
        self.usi_submission.message = "Submitted to biosample"
346
347
348
class SubmitTaskTestCase(BaseMixin, TestCase):
349
    """Test submission task"""
350
351
    # overriding BaseMixin features
352
    fixtures = [
353
        'biosample/account',
354
        'biosample/managedteam',
355
        'biosample/submission',
356
        'biosample/submissiondata',
357
        'image_app/animal',
358
        'image_app/dictbreed',
359
        'image_app/dictcountry',
360
        'image_app/dictrole',
361
        'image_app/dictsex',
362
        'image_app/dictspecie',
363
        'image_app/dictstage',
364
        'image_app/dictuberon',
365
        'image_app/name',
366
        'image_app/ontology',
367
        'image_app/organization',
368
        'image_app/publication',
369
        'image_app/sample',
370
        'image_app/submission',
371
        'image_app/user'
372
    ]
373
374
    def setUp(self):
375
        # call Mixin method
376
        super().setUp()
377
378
        # setting tasks
379
        self.my_task = SubmitTask()
380
381
        # set my pk
382
        self.usi_submission_id = 1
383
384
        # get a biosample.submission object
385
        self.usi_submission = USISubmission.objects.get(
386
            pk=self.usi_submission_id)
387
388
        # starting mocked objects
389
        self.mock_read_patcher = patch.object(SubmissionHelper, "read_token")
390
        self.mock_read = self.mock_read_patcher.start()
391
392
        self.mock_start_patcher = patch.object(
393
            SubmissionHelper, "start_submission")
394
        self.mock_start = self.mock_start_patcher.start()
395
396
        self.mock_add_patcher = patch.object(SubmissionHelper, "add_samples")
397
        self.mock_add = self.mock_add_patcher.start()
398
399
    def tearDown(self):
400
        # stopping mock objects
401
        self.mock_read_patcher.stop()
402
        self.mock_start_patcher.stop()
403
        self.mock_add_patcher.stop()
404
405
        # calling base object
406
        super().tearDown()
407
408
    def common_test(self, task_result, message, status):
409
        # assert a success with data uploading
410
        self.assertEqual(task_result, ("success", self.usi_submission_id))
411
412
        # check submission status and message
413
        self.usi_submission.refresh_from_db()
414
415
        # check submission.status changed
416
        self.assertEqual(self.usi_submission.status, status)
417
        self.assertIn(message, self.usi_submission.message)
418
419
        # no status changes for UID submission (will callback change status)
420
        self.assertEqual(self.submission_obj.status, WAITING)
421
        self.assertEqual(
422
            self.submission_obj.message,
423
            "Waiting for biosample submission")
424
425
        self.assertTrue(self.mock_read.called)
426
        self.assertTrue(self.mock_start.called)
427
        self.assertTrue(self.mock_add.called)
428
429
    def test_submit(self):
430
        """Test submitting into biosample"""
431
432
        # NOTE that I'm calling the function directly, without delay
433
        # (AsyncResult). I've patched the time consuming task
434
        # Remeber that submission_id will be biosample.models.Submission.id
435
        res = self.my_task.run(usi_submission_id=self.usi_submission_id)
436
437
        self.common_test(res, "Submitted to biosample", SUBMITTED)
438
439
    def test_issues_with_api(self):
440
        """Test errors with submission API"""
441
442
        # Set a side effect on the patched methods
443
        # so that they raise the errors we want.
444
        self.mock_add.side_effect = ConnectionError()
445
446
        # call task. No retries with issues at EBI
447
        res = self.my_task.run(usi_submission_id=self.usi_submission_id)
448
449
        # this is the message I want
450
        message = "Errors in EBI API endpoints. Please try again later"
451
452
        # assert anyway a success
453
        self.common_test(res, message, READY)
454
455
    def test_token_expired(self):
456
        """Testing token expiring during a submission"""
457
458
        # simulating a token expiring during a submission
459
        self.mock_add.side_effect = RuntimeError("Your token is expired")
460
461
        # calling task
462
        res = self.my_task.run(usi_submission_id=self.usi_submission_id)
463
464
        message = (
465
            "Your token is expired: please submit again to resume submission")
466
467
        # assert anyway a success
468
        self.common_test(res, message, READY)
469
470
    def test_unmanaged(self):
471
        """Testing unmanaged Exception"""
472
473
        # simulating a token expiring during a submission
474
        self.mock_add.side_effect = Exception("Unmanaged")
475
476
        # calling task
477
        res = self.my_task.run(usi_submission_id=self.usi_submission_id)
478
479
        message = "Exception: Unmanaged"
480
481
        # assert anyway a success
482
        self.common_test(res, message, ERROR)
483