Completed
Pull Request — master (#44)
by Paolo
11:03 queued 04:44
created

biosample.tests.test_tasks_submission   A

Complexity

Total Complexity 37

Size/Duplication

Total Lines 628
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 37
eloc 313
dl 0
loc 628
rs 9.44
c 0
b 0
f 0

35 Methods

Rating   Name   Duplication   Size   Complexity  
A SubmitTaskTestCase.test_unmanaged() 0 13 1
A SubmissionHelperTestCase.setUp() 0 16 1
A SubmitTaskTestCase.common_test() 0 20 1
A SubmissionCompleteTaskTestCase.test_unmanaged_exception() 0 9 1
A SubmissionHelperTestCase.test_properties() 0 14 1
A SubmissionHelperTestCase.test_update_sample() 0 27 2
A SubmissionCompleteTaskTestCase.setUp() 0 14 1
A SplitSubmissionMixin.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 16 1
A SubmitTaskTestCase.setUp() 0 24 1
A SplitSubmissionTaskUpdateTestCase.setUp() 0 8 1
A SplitSubmissionMixin.generic_check() 0 23 2
A SplitSubmissionTaskUpdateTestCase.test_split_submission() 0 8 1
A SubmissionCompleteTaskTestCase.test_token_expired() 0 11 1
A SubmissionCompleteTaskTestCase.test_submission_complete() 0 6 1
A SubmissionHelperTestCase.test_read_samples() 0 23 1
A SubmitTaskTestCase.test_submit() 0 9 1
A SubmissionCompleteTaskTestCase.test_error_api_endpoint() 0 9 1
A SplitSubmissionTaskTestCase.test_sample_already_in_submission() 0 11 1
A SubmissionCompleteTaskTestCase.test_error_has_higher_priority() 0 14 1
A SubmitTaskTestCase.test_token_expired() 0 14 1
A SplitSubmissionTaskTestCase.test_split_submission() 0 8 1
A SubmissionHelperTestCase.test_read_token() 0 8 1
A SubmissionHelperTestCase.test_create_sample() 0 17 1
A SplitSubmissionTaskUpdateTestCase.test_split_submission_issues() 0 16 1
A SubmissionHelperTestCase.test_create_submission() 0 14 1
A SubmitTaskTestCase.tearDown() 0 8 1
A SubmissionCompleteTaskTestCase.common_check() 0 26 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
A SplitSubmissionMixin.tearDown() 0 6 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 (
14
    READY, COMPLETED, ERROR, SUBMITTED, WAITING, STATUSES)
15
16
from .common import TaskFailureMixin, RedisMixin, BaseMixin
17
from ..models import Submission as USISubmission, SubmissionData
18
from ..tasks.submission import (
19
    SubmissionHelper, SplitSubmissionTask, SubmitTask, SubmissionError,
20
    SubmissionCompleteTask)
21
22
23
class SubmissionFeaturesMixin(BaseMixin):
24
    """Common features for SubmitTask and SubmissionHelper"""
25
26
    # overriding BaseMixin features
27
    fixtures = [
28
        'biosample/account',
29
        'biosample/managedteam',
30
        'biosample/submission',
31
        'biosample/submissiondata',
32
        'image_app/animal',
33
        'image_app/dictbreed',
34
        'image_app/dictcountry',
35
        'image_app/dictrole',
36
        'image_app/dictsex',
37
        'image_app/dictspecie',
38
        'image_app/dictstage',
39
        'image_app/dictuberon',
40
        'image_app/name',
41
        'image_app/ontology',
42
        'image_app/organization',
43
        'image_app/publication',
44
        'image_app/sample',
45
        'image_app/submission',
46
        'image_app/user'
47
    ]
48
49
50
class SplitSubmissionMixin(TaskFailureMixin, BaseMixin):
51
    """Generic stuff for SplitSubmissionTask tests"""
52
53
    def setUp(self):
54
        # call Mixin method
55
        super().setUp()
56
57
        # setting tasks
58
        self.my_task = SplitSubmissionTask()
59
60
        # patching objects
61
        self.mock_chord_patcher = patch('biosample.tasks.submission.chord')
62
        self.mock_chord = self.mock_chord_patcher.start()
63
64
        # other function are not called since chord is patched
65
66
    def tearDown(self):
67
        # stopping mock objects
68
        self.mock_chord_patcher.stop()
69
70
        # calling base object
71
        super().tearDown()
72
73
    def generic_check(self, res, n_of_submission, n_of_submissiondata):
74
        """Generic check for created data"""
75
76
        # assert a success with data uploading
77
        self.assertEqual(res, "success")
78
79
        # get usi_submission qs
80
        usi_submissions_qs = USISubmission.objects.all()
81
82
        # asserting two biosample.submission data objects
83
        self.assertEqual(usi_submissions_qs.count(), n_of_submission)
84
85
        # assert two data for each submission
86
        for usi_submission in usi_submissions_qs:
87
            self.assertEqual(usi_submission.status, READY)
88
89
            # grab submission data queryset
90
            submission_data_qs = SubmissionData.objects.filter(
91
                submission=usi_submission)
92
            self.assertEqual(submission_data_qs.count(), n_of_submissiondata)
93
94
        # assert mock objects called
95
        self.assertTrue(self.mock_chord.called)
96
97
98
class SplitSubmissionTaskTestCase(SplitSubmissionMixin, TestCase):
99
    # ovverride MAX_SAMPLES in order to split data
100
    @patch('biosample.tasks.submission.MAX_SAMPLES', 2)
101
    def test_split_submission(self):
102
        """Test splitting submission data"""
103
104
        res = self.my_task.run(submission_id=self.submission_id)
105
106
        self.generic_check(res, n_of_submission=2, n_of_submissiondata=2)
107
        self.assertEqual(self.n_to_submit, SubmissionData.objects.count())
108
109
    # ovverride MAX_SAMPLES in order to split data
110
    @patch('biosample.tasks.submission.MAX_SAMPLES', 2)
111
    def test_split_submission_partial(self):
112
        """Test splitting submission data with some data already submitted"""
113
114
        self.name_qs.filter(pk__in=[3, 4]).update(status=COMPLETED)
115
116
        res = self.my_task.run(submission_id=self.submission_id)
117
118
        self.generic_check(res, n_of_submission=1, n_of_submissiondata=2)
119
120
    @patch('biosample.tasks.submission.MAX_SAMPLES', 2)
121
    def test_sample_already_in_submission(self):
122
        """Test splitting submission with sample in a opened submission"""
123
124
        # call task once
125
        self.my_task.run(submission_id=self.submission_id)
126
127
        # call a task a second time
128
        res = self.my_task.run(submission_id=self.submission_id)
129
130
        self.generic_check(res, n_of_submission=2, n_of_submissiondata=2)
131
132
133
class SplitSubmissionTaskUpdateTestCase(
134
        SubmissionFeaturesMixin, SplitSubmissionMixin, TestCase):
135
    """
136
    A particoular test case: I submit data once and then biosample tell me
137
    that there are errors in submission data. So I mark name with need revision
138
    status and the submission is already opened. I can't send data within a
139
    new submission, I need to restore things a submit the data I have (since
140
    to_biosample() is called on user data when submitting, there are no issues
141
    with old data in biosample.models)"""
142
143
    def setUp(self):
144
        # call Mixin method
145
        super().setUp()
146
147
        # ok in this case, my samples are READY since I passed validation
148
        # after fail into biosample stage. My submission will be in SUBMITTED
149
        # stage
150
        USISubmission.objects.update(status=SUBMITTED)
151
152
    # ovverride MAX_SAMPLES in order to split data
153
    @patch('biosample.tasks.submission.MAX_SAMPLES', 2)
154
    def test_split_submission(self):
155
        """Test splitting submission data"""
156
157
        res = self.my_task.run(submission_id=self.submission_id)
158
159
        self.generic_check(res, n_of_submission=2, n_of_submissiondata=2)
160
        self.assertEqual(self.n_to_submit, SubmissionData.objects.count())
161
162
    # ovverride MAX_SAMPLES in order to split data
163
    @patch('biosample.tasks.submission.MAX_SAMPLES', 2)
164
    def test_split_submission_issues(self):
165
        """Test splitting submission data with issues"""
166
167
        # add a new submission data with the same sample, just to test
168
        # exception handling
169
        # https://stackoverflow.com/a/4736172/4385116
170
        submission_data = SubmissionData.objects.get(pk=1)
171
        submission_data.pk = None
172
        submission_data.save()
173
174
        self.assertRaisesRegex(
175
            SubmissionError,
176
            "More than one submission opened",
177
            self.my_task.run,
178
            submission_id=self.submission_id)
179
180
181
class SubmissionHelperTestCase(RedisMixin, SubmissionFeaturesMixin, TestCase):
182
183
    def setUp(self):
184
        # call Mixin method
185
        super().setUp()
186
187
        # set my pk
188
        self.usi_submission_id = 1
189
190
        # Instantiating SubmissionHelper with biosample.submission pk
191
        self.submission_helper = SubmissionHelper(self.usi_submission_id)
192
193
        # get a biosample.submission object
194
        self.usi_submission = USISubmission.objects.get(
195
            pk=self.usi_submission_id)
196
197
        # set attributes with class baseMixin class attributes for semplicity
198
        self.submission_helper.root = self.my_root
199
200
    def test_properties(self):
201
        """Asserting read properties"""
202
203
        owner = self.submission_helper.owner
204
        self.assertEqual(owner.username, "test")
205
206
        team_name = self.submission_helper.team_name
207
        self.assertEqual(team_name, "subs.test-team-1")
208
209
        self.assertIsNone(self.submission_helper.usi_submission_name)
210
        self.submission_helper.usi_submission_name = "test"
211
212
        self.usi_submission.refresh_from_db()
213
        self.assertEqual(self.usi_submission.usi_submission_name, "test")
214
215
    def test_read_token(self):
216
        """testing token from redis DB"""
217
218
        token = self.submission_helper.read_token()
219
        self.assertEqual(self.token, token)
220
221
        # assert called mock objects
222
        self.assertTrue(self.mock_root.called)
223
224
    @patch.object(SubmissionHelper, "read_samples")
225
    def test_recover_submission(self, my_helper):
226
        """Testing submission recover"""
227
228
        # base case: no usi_submission_name so False is expected
229
        self.assertFalse(self.submission_helper.recover_submission())
230
231
        # assign a usi_submission_name
232
        self.submission_helper.usi_submission_name = "test-submission"
233
234
        # asserted final returned status
235
        self.assertTrue(self.submission_helper.recover_submission())
236
237
        # assert a recovered document
238
        self.assertEqual(
239
            "test-submission",
240
            self.submission_helper.usi_submission.name)
241
242
        # assert get_samples called functions called
243
        self.assertTrue(my_helper.called)
244
245
        # asserting others mock objects called
246
        self.assertFalse(self.my_root.get_team_by_name.called)
247
        self.assertFalse(self.my_team.create_submission.called)
248
        self.assertTrue(self.my_root.get_submission_by_name.called)
249
        self.assertTrue(self.my_submission.propertymock.called)
250
251
    def test_recover_submission_error(self):
252
        """Testing submission recover for a closed submission"""
253
254
        # assign a usi_submission_name
255
        self.submission_helper.usi_submission_name = "test-submission"
256
257
        # change submission status
258
        self.my_submission.propertymock = PropertyMock(
259
            return_value='Completed')
260
        type(self.my_submission).status = self.my_submission.propertymock
261
262
        self.assertRaisesRegex(
263
            SubmissionError,
264
            "Cannot recover submission",
265
            self.submission_helper.recover_submission)
266
267
        # asserting others mock objects called
268
        self.assertFalse(self.my_root.get_team_by_name.called)
269
        self.assertFalse(self.my_team.create_submission.called)
270
        self.assertTrue(self.my_root.get_submission_by_name.called)
271
        self.assertTrue(self.my_submission.propertymock.called)
272
273
    def test_create_submission(self):
274
        """Testing submission create"""
275
276
        self.submission_helper.create_submission()
277
278
        # assert a new document
279
        self.assertEqual(
280
            "new-submission",
281
            self.submission_helper.usi_submission.name)
282
283
        # asserting others mock objects called
284
        self.assertTrue(self.my_root.get_team_by_name.called)
285
        self.assertTrue(self.my_team.create_submission.called)
286
        self.assertFalse(self.my_root.get_submission_by_name.called)
287
288
    @patch.object(SubmissionHelper, "create_submission")
289
    @patch.object(SubmissionHelper, "recover_submission")
290
    def test_start_submission(self, my_recover, my_create):
291
        """testing start a submission"""
292
293
        def create_submission():
294
            """Simulate create submission (already tested)"""
295
296
            self.submission_helper.usi_submission = self.new_submission
297
            return self.new_submission
298
299
        my_recover.return_value = False
300
        my_create.side_effect = create_submission
301
302
        usi_submission = self.submission_helper.start_submission()
303
304
        # assert a new document
305
        self.assertEqual("new-submission", usi_submission.name)
306
307
        # assert mock methods called
308
        self.assertTrue(my_recover.called)
309
        self.assertTrue(my_create.called)
310
311
    def test_read_samples(self):
312
313
        # creating mock samples
314
        my_samples = [
315
            Mock(**{'alias': 'IMAGEA000000001',
316
                    'title': 'a 4-year old pig organic fed'}),
317
            Mock(**{'alias': 'IMAGES000000001',
318
                    'title': 'semen collected when the animal turns to 4'}),
319
        ]
320
321
        # mocking set samples
322
        self.my_submission.get_samples.return_value = my_samples
323
        self.submission_helper.usi_submission = self.my_submission
324
325
        # calling function
326
        submitted_samples = self.submission_helper.read_samples()
327
328
        self.assertIsInstance(submitted_samples, dict)
329
        self.assertEqual(len(submitted_samples), 2)
330
331
        keys = submitted_samples.keys()
332
        self.assertIn('IMAGEA000000001', keys)
333
        self.assertIn('IMAGES000000001', keys)
334
335
    def test_create_sample(self):
336
        # get a model object
337
        submission_data = SubmissionData.objects.get(pk=1)
338
        model = submission_data.content_object
339
340
        # get a biosample submission
341
        self.submission_helper.usi_submission = self.my_submission
342
343
        # add model to biosample submission
344
        self.submission_helper.create_or_update_sample(model)
345
346
        # assert status
347
        self.assertEqual(model.name.status, SUBMITTED)
348
349
        # testing things
350
        self.assertEqual(
351
            self.my_submission.create_sample.call_count, 1)
352
353
    def test_update_sample(self):
354
        # creating mock samples
355
        my_samples = [
356
            Mock(**{'alias': 'IMAGEA000000001',
357
                    'title': 'a 4-year old pig organic fed'}),
358
        ]
359
360
        # mocking set samples
361
        self.my_submission.get_samples.return_value = my_samples
362
        self.submission_helper.usi_submission = self.my_submission
363
364
        # read samples through function (already tested)
365
        self.submission_helper.read_samples()
366
367
        # get a model object
368
        submission_data = SubmissionData.objects.get(pk=1)
369
        model = submission_data.content_object
370
371
        # add model to biosample submission
372
        self.submission_helper.create_or_update_sample(model)
373
374
        # assert status
375
        self.assertEqual(model.name.status, SUBMITTED)
376
377
        # testing patch
378
        for sample in my_samples:
379
            self.assertTrue(sample.patch.called)
380
381
    @patch.object(SubmissionHelper, "create_or_update_sample")
382
    def test_add_samples(self, my_create):
383
        """Test adding samples"""
384
385
        # simulate a submission recover: mark an animal as already submitted
386
        submission_data = SubmissionData.objects.get(pk=1)
387
        submission_data.content_object.name.status = SUBMITTED
388
        submission_data.content_object.name.save()
389
390
        # calling method
391
        self.submission_helper.add_samples()
392
393
        # assert create sample in biosample called once
394
        my_create.assert_called_once()
395
396
    def test_mark_submission(self):
397
        """test adding status message to submission"""
398
399
        self.submission_helper.mark_fail(message="test")
400
401
        self.usi_submission.refresh_from_db()
402
        self.assertEqual(self.usi_submission.status, ERROR)
403
        self.assertEqual(self.usi_submission.message, "test")
404
405
        self.submission_helper.mark_success()
406
407
        self.usi_submission.refresh_from_db()
408
        self.assertEqual(self.usi_submission.status, SUBMITTED)
409
        self.assertEqual(
410
            self.usi_submission.message,
411
            "Waiting for biosample validation")
412
413
414
class SubmitTaskTestCase(SubmissionFeaturesMixin, TestCase):
415
    """Test submission task"""
416
417
    def setUp(self):
418
        # call Mixin method
419
        super().setUp()
420
421
        # setting tasks
422
        self.my_task = SubmitTask()
423
424
        # set my pk
425
        self.usi_submission_id = 1
426
427
        # get a biosample.submission object
428
        self.usi_submission = USISubmission.objects.get(
429
            pk=self.usi_submission_id)
430
431
        # starting mocked objects
432
        self.mock_read_patcher = patch.object(SubmissionHelper, "read_token")
433
        self.mock_read = self.mock_read_patcher.start()
434
435
        self.mock_start_patcher = patch.object(
436
            SubmissionHelper, "start_submission")
437
        self.mock_start = self.mock_start_patcher.start()
438
439
        self.mock_add_patcher = patch.object(SubmissionHelper, "add_samples")
440
        self.mock_add = self.mock_add_patcher.start()
441
442
    def tearDown(self):
443
        # stopping mock objects
444
        self.mock_read_patcher.stop()
445
        self.mock_start_patcher.stop()
446
        self.mock_add_patcher.stop()
447
448
        # calling base object
449
        super().tearDown()
450
451
    def common_test(self, task_result, message, status):
452
        # assert a success with data uploading
453
        self.assertEqual(task_result, ("success", self.usi_submission_id))
454
455
        # check submission status and message
456
        self.usi_submission.refresh_from_db()
457
458
        # check submission.status changed
459
        self.assertEqual(self.usi_submission.status, status)
460
        self.assertIn(message, self.usi_submission.message)
461
462
        # no status changes for UID submission (will callback change status)
463
        self.assertEqual(self.submission_obj.status, WAITING)
464
        self.assertEqual(
465
            self.submission_obj.message,
466
            "Waiting for biosample submission")
467
468
        self.assertTrue(self.mock_read.called)
469
        self.assertTrue(self.mock_start.called)
470
        self.assertTrue(self.mock_add.called)
471
472
    def test_submit(self):
473
        """Test submitting into biosample"""
474
475
        # NOTE that I'm calling the function directly, without delay
476
        # (AsyncResult). I've patched the time consuming task
477
        # Remeber that submission_id will be biosample.models.Submission.id
478
        res = self.my_task.run(usi_submission_id=self.usi_submission_id)
479
480
        self.common_test(res, "Waiting for biosample validation", SUBMITTED)
481
482
    def test_issues_with_api(self):
483
        """Test errors with submission API"""
484
485
        # Set a side effect on the patched methods
486
        # so that they raise the errors we want.
487
        self.mock_add.side_effect = ConnectionError()
488
489
        # call task. No retries with issues at EBI
490
        res = self.my_task.run(usi_submission_id=self.usi_submission_id)
491
492
        # this is the message I want
493
        message = "Errors in EBI API endpoints. Please try again later"
494
495
        # assert anyway a success
496
        self.common_test(res, message, READY)
497
498
    def test_token_expired(self):
499
        """Testing token expiring during a submission"""
500
501
        # simulating a token expiring during a submission
502
        self.mock_add.side_effect = RuntimeError("Your token is expired")
503
504
        # calling task
505
        res = self.my_task.run(usi_submission_id=self.usi_submission_id)
506
507
        message = (
508
            "Your token is expired: please submit again to resume submission")
509
510
        # assert anyway a success
511
        self.common_test(res, message, READY)
512
513
    def test_unmanaged(self):
514
        """Testing unmanaged Exception"""
515
516
        # simulating a token expiring during a submission
517
        self.mock_add.side_effect = Exception("Unmanaged")
518
519
        # calling task
520
        res = self.my_task.run(usi_submission_id=self.usi_submission_id)
521
522
        message = "Exception: Unmanaged"
523
524
        # assert anyway a success
525
        self.common_test(res, message, ERROR)
526
527
528
class SubmissionCompleteTaskTestCase(
529
        SubmissionFeaturesMixin, TaskFailureMixin, BaseMixin, TestCase):
530
531
    """Test class for SubmissionCompleteTask"""
532
533
    def setUp(self):
534
        # call Mixin method
535
        super().setUp()
536
537
        # setting tasks
538
        self.my_task = SubmissionCompleteTask()
539
540
        # these will be the tasks arguments, indipendently by status etc
541
        self.my_tasks_args = ([("success", 1), ("success", 2)], )
542
543
        # update statuses for biosample.models.Submission
544
        USISubmission.objects.update(
545
            status=SUBMITTED,
546
            message='Waiting for biosample validation')
547
548
    def common_check(self, status, message):
549
        """Common check for tests"""
550
551
        # update an object
552
        usi_submission = USISubmission.objects.get(pk=1)
553
        usi_submission.status = status
554
        usi_submission.message = message
555
        usi_submission.save()
556
557
        # calling task
558
        result = self.my_task.run(
559
            *self.my_tasks_args,
560
            uid_submission_id=self.submission_id)
561
562
        # assert a success with data uploading
563
        self.assertEqual(result, "success")
564
565
        # check status and messages
566
        self.submission_obj.refresh_from_db()
567
        self.assertEqual(self.submission_obj.status, status)
568
        self.assertEqual(self.submission_obj.message, message)
569
570
        # calling a WebSocketMixin method
571
        self.check_message(
572
            STATUSES.get_value_display(status),
573
            message)
574
575
    def test_submission_complete(self):
576
        """test no issues after a submission"""
577
578
        self.common_check(
579
            SUBMITTED,
580
            'Waiting for biosample validation')
581
582
    def test_error_api_endpoint(self):
583
        """test issues with API endpoint"""
584
585
        status = READY
586
        message = "Errors in EBI API endpoints. Please try again later"
587
588
        self.common_check(
589
            status,
590
            message)
591
592
    def test_token_expired(self):
593
        """test a token expired during submission"""
594
595
        status = READY
596
        message = (
597
            "Your token is expired: please submit again to resume "
598
            "submission")
599
600
        self.common_check(
601
            status,
602
            message)
603
604
    def test_unmanaged_exception(self):
605
        """test an unmanaged exception"""
606
607
        status = ERROR
608
        message = "Exception: Unmanaged"
609
610
        self.common_check(
611
            status,
612
            message)
613
614
    def test_error_has_higher_priority(self):
615
        # update an object with a ready status
616
        usi_submission = USISubmission.objects.get(pk=2)
617
        usi_submission.status = READY
618
        usi_submission.message = "a message"
619
        usi_submission.save()
620
621
        # then raise a generi error
622
        status = ERROR
623
        message = "Exception: Unmanaged"
624
625
        self.common_check(
626
            status,
627
            message)
628