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

  A

Complexity

Conditions 1

Size

Total Lines 36
Code Lines 20

Duplication

Lines 36
Ratio 100 %

Importance

Changes 0
Metric Value
eloc 20
dl 36
loc 36
rs 9.4
c 0
b 0
f 0
cc 1
nop 1
1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3
"""
4
Created on Tue Oct  9 14:51:13 2018
5
6
@author: Paolo Cozzi <[email protected]>
7
"""
8
9
import redis
10
11
from pytest import raises
12
from collections import Counter
13
from unittest.mock import patch, Mock, PropertyMock
14
from datetime import timedelta
15
16
from celery.exceptions import Retry
17
18
from django.test import TestCase
19
from django.conf import settings
20
from django.core import mail
21
from django.utils import timezone
22
23
from common.constants import (
24
    LOADED, ERROR, READY, NEED_REVISION, SUBMITTED, COMPLETED)
25
from common.tests import WebSocketMixin
26
from image_app.models import Submission, Name
27
28
from ..tasks import SubmitTask, FetchStatusTask
29
from ..models import ManagedTeam
30
31
from .common import TaskFailureMixin, generate_token
32
33
34 View Code Duplication
class RedisMixin():
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
35
    """A class to setup a test token in redis database"""
36
37
    # this will be the token key in redis database
38
    submission_key = "token:submission:1:test"
39
40
    @classmethod
41
    def setUpClass(cls):
42
        # calling my base class setup
43
        super().setUpClass()
44
45
        cls.redis = redis.StrictRedis(
46
            host=settings.REDIS_HOST,
47
            port=settings.REDIS_PORT,
48
            db=settings.REDIS_DB)
49
50
        # generate a token
51
        token = generate_token(domains=['subs.test-team-1'])
52
53
        # write token to database
54
        cls.redis.set(cls.submission_key, token, ex=3600)
55
56
    @classmethod
57
    def tearDownClass(cls):
58
        if cls.redis.exists(cls.submission_key):
59
            cls.redis.delete(cls.submission_key)
60
61
        super().tearDownClass()
62
63
64
class SubmitTestCase(TaskFailureMixin, RedisMixin, WebSocketMixin, TestCase):
65
66
    def setUp(self):
67
        # call Mixin method
68
        super().setUp()
69
70
        # setting tasks
71
        self.my_task = SubmitTask()
72
73
    def test_submit(self):
74
        """Test submitting into biosample"""
75
76
        # NOTE that I'm calling the function directly, without delay
77
        # (AsyncResult). I've patched the time consuming task
78
        res = self.my_task.run(submission_id=self.submission_id)
79
80
        # assert a success with data uploading
81
        self.assertEqual(res, "success")
82
83
        # check submission status and message
84
        self.submission_obj.refresh_from_db()
85
86
        # check submission.state changed
87
        self.assertEqual(self.submission_obj.status, SUBMITTED)
88
        self.assertEqual(
89
            self.submission_obj.message,
90
            "Waiting for biosample validation")
91
        self.assertEqual(
92
            self.submission_obj.biosample_submission_id,
93
            "new-submission")
94
95
        # check name status changed
96
        qs = Name.objects.filter(status=SUBMITTED)
97
        self.assertEqual(len(qs), self.n_to_submit)
98
99
        # assert called mock objects
100
        self.assertTrue(self.mock_root.called)
101
        self.assertTrue(self.my_root.get_team_by_name.called)
102
        self.assertTrue(self.my_team.create_submission.called)
103
        self.assertFalse(self.my_root.get_submission_by_name.called)
104
        self.assertEqual(
105
            self.new_submission.create_sample.call_count, self.n_to_submit)
106
        self.assertFalse(self.new_submission.propertymock.called)
107
108
        message = 'Submitted'
109
        notification_message = 'Waiting for biosample validation'
110
111
        # calling a WebSocketMixin method
112
        self.check_message(message, notification_message)
113
114
    # http://docs.celeryproject.org/en/latest/userguide/testing.html#tasks-and-unit-tests
115
    @patch("biosample.tasks.SubmitTask.retry")
116
    @patch("biosample.tasks.SubmitTask.submit_biosample")
117
    def test_submit_retry(self, my_submit, my_retry):
118
        """Test submissions with retry"""
119
120
        # Set a side effect on the patched methods
121
        # so that they raise the errors we want.
122
        my_retry.side_effect = Retry()
123
        my_submit.side_effect = Exception()
124
125
        with raises(Retry):
126
            self.my_task.run(submission_id=1)
127
128
        self.assertTrue(my_submit.called)
129
        self.assertTrue(my_retry.called)
130
131
    @patch("biosample.tasks.SubmitTask.retry")
132
    @patch("biosample.tasks.SubmitTask.submit_biosample")
133
    def test_issues_with_api(self, my_submit, my_retry):
134
        """Test errors with submission API"""
135
136
        # Set a side effect on the patched methods
137
        # so that they raise the errors we want.
138
        my_retry.side_effect = Retry()
139
        my_submit.side_effect = ConnectionError()
140
141
        # call task. No retries with issues at EBI
142
        res = self.my_task.run(submission_id=1)
143
144
        # assert a success with validation taks
145
        self.assertEqual(res, "success")
146
147
        # check submission status and message
148
        self.submission_obj.refresh_from_db()
149
150
        # this is the message I want
151
        message = "Errors in EBI API endpoints. Please try again later"
152
153
        # check submission.status NOT changed
154
        self.assertEqual(self.submission_obj.status, READY)
155
        self.assertIn(
156
            message,
157
            self.submission_obj.message)
158
159
        # test email sent
160
        self.assertEqual(len(mail.outbox), 1)
161
162
        # read email
163
        email = mail.outbox[0]
164
165
        self.assertEqual(
166
            "Error in biosample submission 1",
167
            email.subject)
168
169
        self.assertTrue(my_submit.called)
170
        self.assertFalse(my_retry.called)
171
172
        message = 'Ready'
173
        notification_message = (
174
            'Errors in EBI API endpoints. '
175
            'Please try again later')
176
177
        # calling a WebSocketMixin method
178
        self.check_message(message, notification_message)
179
180
    def test_submit_recover(self):
181
        """Test submission recovering"""
182
183
        # update submission object
184
        self.submission_obj.biosample_submission_id = "test-submission"
185
        self.submission_obj.save()
186
187
        # set one name as uploaded
188
        name = Name.objects.get(name='ANIMAL:::ID:::132713')
189
        name.status = SUBMITTED
190
        name.save()
191
192
        # calling submit
193
        res = self.my_task.run(submission_id=self.submission_id)
194
195
        # assert a success with data uploading
196
        self.assertEqual(res, "success")
197
198
        # reload submission
199
        self.submission_obj.refresh_from_db()
200
201
        # check submission.state changed
202
        self.assertEqual(self.submission_obj.status, SUBMITTED)
203
        self.assertEqual(
204
            self.submission_obj.message,
205
            "Waiting for biosample validation")
206
207
        # check name status changed
208
        qs = Name.objects.filter(status=SUBMITTED)
209
        self.assertEqual(len(qs), self.n_to_submit)
210
211
        # assert called mock objects
212
        self.assertTrue(self.mock_root.called)
213
        self.assertFalse(self.my_root.get_team_by_name.called)
214
        self.assertFalse(self.my_team.create_submission.called)
215
        self.assertTrue(self.my_root.get_submission_by_name.called)
216
        # I'm testing submission recover with 1 sample already sent, so:
217
        self.assertEqual(
218
            self.my_submission.create_sample.call_count, self.n_to_submit-1)
219
        self.assertTrue(self.my_submission.propertymock.called)
220
221
        message = 'Submitted'
222
        notification_message = 'Waiting for biosample validation'
223
224
        # calling a WebSocketMixin method
225
        self.check_message(message, notification_message)
226
227
    def test_submit_no_recover(self):
228
        """Test submission recovering with a closed submission"""
229
230
        # update submission status
231
        self.my_submission.propertymock = PropertyMock(
232
            return_value='Completed')
233
        type(self.my_submission).status = self.my_submission.propertymock
234
235
        # update submission object
236
        self.submission_obj.biosample_submission_id = "test-submission"
237
        self.submission_obj.save()
238
239
        # calling submit
240
        res = self.my_task.run(submission_id=self.submission_id)
241
242
        # assert a success with data uploading
243
        self.assertEqual(res, "success")
244
245
        # reload submission
246
        self.submission_obj.refresh_from_db()
247
248
        # check submission.state changed
249
        self.assertEqual(
250
            self.submission_obj.biosample_submission_id,
251
            "new-submission")
252
        self.assertEqual(self.submission_obj.status, SUBMITTED)
253
        self.assertEqual(
254
            self.submission_obj.message,
255
            "Waiting for biosample validation")
256
257
        # check name status changed
258
        qs = Name.objects.filter(status=SUBMITTED)
259
        self.assertEqual(len(qs), self.n_to_submit)
260
261
        # assert called mock objects
262
        self.assertTrue(self.mock_root.called)
263
        self.assertTrue(self.my_root.get_team_by_name.called)
264
        self.assertTrue(self.my_root.get_submission_by_name.called)
265
        self.assertTrue(self.my_team.create_submission.called)
266
        self.assertFalse(self.my_submission.create_sample.called)
267
        self.assertEqual(
268
            self.new_submission.create_sample.call_count, self.n_to_submit)
269
        self.assertTrue(self.my_submission.propertymock.called)
270
        self.assertFalse(self.new_submission.propertymock.called)
271
272
        message = 'Submitted'
273
        notification_message = 'Waiting for biosample validation'
274
275
        # calling a WebSocketMixin method
276
        self.check_message(message, notification_message)
277
278
    def test_submit_patch(self):
279
        """Test patching submission"""
280
281
        # creating mock samples
282
        my_samples = [
283
            Mock(**{'alias': 'IMAGEA000000001',
284
                    'title': 'a 4-year old pig organic fed'}),
285
            Mock(**{'alias': 'IMAGES000000001',
286
                    'title': 'semen collected when the animal turns to 4'}),
287
        ]
288
289
        # mocking set samples
290
        self.my_submission.get_samples.return_value = my_samples
291
292
        # update submission object
293
        self.submission_obj.biosample_submission_id = "test-submission"
294
        self.submission_obj.save()
295
296
        # calling submit
297
        res = self.my_task.run(submission_id=self.submission_id)
298
299
        # assert a success with data uploading
300
        self.assertEqual(res, "success")
301
302
        # reload submission
303
        self.submission_obj.refresh_from_db()
304
305
        # check submission.state changed
306
        self.assertEqual(self.submission_obj.status, SUBMITTED)
307
        self.assertEqual(
308
            self.submission_obj.message,
309
            "Waiting for biosample validation")
310
311
        # check name status changed
312
        qs = Name.objects.filter(status=SUBMITTED)
313
        self.assertEqual(len(qs), self.n_to_submit)
314
315
        # assert called mock objects
316
        self.assertTrue(self.mock_root.called)
317
        self.assertFalse(self.my_root.get_team_by_name.called)
318
        self.assertFalse(self.my_team.create_submission.called)
319
        self.assertTrue(self.my_root.get_submission_by_name.called)
320
        # I've patched 2 samples in this test, so:
321
        self.assertEqual(
322
            self.my_submission.create_sample.call_count, 2)
323
        self.assertTrue(self.my_submission.propertymock.called)
324
325
        # testing patch
326
        for sample in my_samples:
327
            self.assertTrue(sample.patch.called)
328
329
        message = 'Submitted'
330
        notification_message = 'Waiting for biosample validation'
331
332
        # calling a WebSocketMixin method
333
        self.check_message(message, notification_message)
334
335
    def test_token_expired(self):
336
        """Testing token expiring during a submission"""
337
338
        # simulating a token expiring during a submission
339
        self.new_submission.create_sample.side_effect = RuntimeError(
340
                "Your token is expired")
341
342
        # calling task
343
        res = self.my_task.run(submission_id=self.submission_id)
344
345
        # assert a success with data uploading
346
        self.assertEqual(res, "success")
347
348
        # check submission status and message
349
        self.submission_obj.refresh_from_db()
350
351
        # check submission.state return to ready (if it was valid before,
352
        # should be valid again, if rules are the same)
353
        self.assertEqual(self.submission_obj.status, READY)
354
        self.assertEqual(
355
            self.submission_obj.message,
356
            "Your token is expired: please submit again to resume submission")
357
        self.assertEqual(
358
            self.submission_obj.biosample_submission_id,
359
            "new-submission")
360
361
        # check name status unchanged (counts are equal to setUp name queryset)
362
        qs = Name.objects.filter(status=READY)
363
        self.assertEqual(len(qs), self.name_qs.count())
364
365
        # test email sent
366
        self.assertEqual(len(mail.outbox), 1)
367
368
        # read email
369
        email = mail.outbox[0]
370
371
        self.assertEqual(
372
            "Error in biosample submission 1",
373
            email.subject)
374
375
        # assert called mock objects
376
        self.assertTrue(self.mock_root.called)
377
        self.assertTrue(self.my_root.get_team_by_name.called)
378
        self.assertTrue(self.my_team.create_submission.called)
379
        self.assertFalse(self.my_root.get_submission_by_name.called)
380
381
        # is called once. With the first call, I got an exception and I
382
        # dont't do the second request
383
        self.assertEqual(self.new_submission.create_sample.call_count, 1)
384
        self.assertFalse(self.new_submission.propertymock.called)
385
386
        message = 'Ready'
387
        notification_message = (
388
            'Your token is expired: please submit '
389
            'again to resume submission')
390
391
        # calling a WebSocketMixin method
392
        self.check_message(message, notification_message)
393
394
395
class UpdateSubmissionTestCase(
396
        TaskFailureMixin, RedisMixin, WebSocketMixin, TestCase):
397
    """Test a submission for an already completed submission which
398
    receives one update, is valid and need to be updated in biosample"""
399
400
    def setUp(self):
401
        # call Mixin method
402
        super().setUp()
403
404
        # get all name objects and set status to completed
405
        self.name_qs.update(status=COMPLETED)
406
407
        # Now get first animal and set status to ready. Take also its sample
408
        # and assign a fake biosample id
409
        self.animal_name = Name.objects.get(pk=3)
410
        self.sample_name = Name.objects.get(pk=4)
411
412
        # update name objects. In this case, animal was modified
413
        self.animal_name.status = READY
414
        self.animal_name.save()
415
416
        # sample is supposed to be submitted with success
417
        self.sample_name.status = COMPLETED
418
        self.sample_name.biosample_id = "FAKES123456"
419
        self.sample_name.save()
420
421
        # setting tasks
422
        self.my_task = SubmitTask()
423
424
    def test_submit(self):
425
        """Test submitting into biosample"""
426
427
        res = self.my_task.run(submission_id=self.submission_id)
428
429
        # assert a success with data uploading
430
        self.assertEqual(res, "success")
431
432
        # check submission status and message
433
        self.submission_obj.refresh_from_db()
434
435
        # check submission.state changed
436
        self.assertEqual(self.submission_obj.status, SUBMITTED)
437
        self.assertEqual(
438
            self.submission_obj.message,
439
            "Waiting for biosample validation")
440
        self.assertEqual(
441
            self.submission_obj.biosample_submission_id,
442
            "new-submission")
443
444
        # check name status changed for animal
445
        self.animal_name.refresh_from_db()
446
        self.assertEqual(self.animal_name.status, SUBMITTED)
447
448
        # check that sample status is unchanged
449
        self.sample_name.refresh_from_db()
450
        self.assertEqual(self.sample_name.status, COMPLETED)
451
452
        # assert that all statuses (except one) remain unchanged
453
        qs = Name.objects.filter(status=COMPLETED)
454
        self.assertEqual(qs.count(), self.name_qs.count()-1)
455
456
        # assert called mock objects
457
        self.assertTrue(self.mock_root.called)
458
        self.assertTrue(self.my_root.get_team_by_name.called)
459
        self.assertTrue(self.my_team.create_submission.called)
460
        self.assertFalse(self.my_root.get_submission_by_name.called)
461
        self.assertEqual(self.new_submission.create_sample.call_count, 1)
462
        self.assertFalse(self.new_submission.propertymock.called)
463
464
        message = 'Submitted'
465
        notification_message = 'Waiting for biosample validation'
466
467
        # calling a WebSocketMixin method
468
        self.check_message(message, notification_message)
469
470
471
class FetchMixin():
472
    """Mixin for fetching status"""
473
474
    fixtures = [
475
        'biosample/account',
476
        'biosample/managedteam',
477
        'image_app/dictcountry',
478
        'image_app/dictrole',
479
        'image_app/organization',
480
        'image_app/submission',
481
        'image_app/user'
482
    ]
483
484
    @classmethod
485
    def setUpClass(cls):
486
        # calling my base class setup
487
        super().setUpClass()
488
489
        unmanaged = ManagedTeam.objects.get(pk=2)
490
        unmanaged.delete()
491
492
        # starting mocked objects
493
        cls.mock_root_patcher = patch('pyUSIrest.client.Root')
494
        cls.mock_root = cls.mock_root_patcher.start()
495
496
        cls.mock_auth_patcher = patch('biosample.helpers.Auth')
497
        cls.mock_auth = cls.mock_auth_patcher.start()
498
499
    @classmethod
500
    def tearDownClass(cls):
501
        cls.mock_root_patcher.stop()
502
        cls.mock_auth_patcher.stop()
503
504
        # calling base method
505
        super().tearDownClass()
506
507
    def setUp(self):
508
        # calling my base setup
509
        super().setUp()
510
511
        # get a submission object
512
        self.submission_obj = Submission.objects.get(pk=1)
513
514
        # set a status which I can fetch_status
515
        self.submission_obj.status = SUBMITTED
516
        self.submission_obj.biosample_submission_id = "test-fetch"
517
        self.submission_obj.save()
518
519
        # set status for names, like submittask does. Only sample not unknown
520
        # are submitted
521
        self.name_qs = Name.objects.exclude(name__contains="unknown")
522
        self.name_qs.update(status=SUBMITTED)
523
524
        # count number of names in UID for such submission (exclude
525
        # unknown animals)
526
        self.n_to_submit = self.name_qs.count()
527
528
        # track submission ID
529
        self.submission_obj_id = self.submission_obj.id
530
531
        # start root object
532
        self.my_root = self.mock_root.return_value
533
534
        # define my task
535
        self.my_task = FetchStatusTask()
536
537
        # change lock_id (useful when running test during cron)
538
        self.my_task.lock_id = "test-FetchStatusTask"
539
540
    def common_tests(self, my_submission):
541
        # passing submission to Mocked Root
542
        self.my_root.get_submission_by_name.return_value = my_submission
543
544
        # NOTE that I'm calling the function directly, without delay
545
        # (AsyncResult). I've patched the time consuming task
546
        res = self.my_task.run()
547
548
        # assert a success with data uploading
549
        self.assertEqual(res, "success")
550
551
        self.assertTrue(self.mock_auth.called)
552
        self.assertTrue(self.mock_root.called)
553
        self.assertTrue(self.my_root.get_submission_by_name.called)
554
555
556
class FetchCompletedTestCase(FetchMixin, WebSocketMixin, TestCase):
557
    """a completed submission with two samples"""
558
559
    fixtures = [
560
        'biosample/account',
561
        'biosample/managedteam',
562
        'image_app/animal',
563
        'image_app/dictbreed',
564
        'image_app/dictcountry',
565
        'image_app/dictrole',
566
        'image_app/dictsex',
567
        'image_app/dictspecie',
568
        'image_app/dictstage',
569
        'image_app/dictuberon',
570
        'image_app/name',
571
        'image_app/organization',
572
        'image_app/publication',
573
        'image_app/sample',
574
        'image_app/submission',
575
        'image_app/user'
576
    ]
577
578
    def test_fetch_status(self):
579
        # a completed submission with two samples
580
        my_submission = Mock()
581
        my_submission.name = "test-fetch"
582
        my_submission.status = 'Completed'
583
584
        # Add samples
585
        my_sample1 = Mock()
586
        my_sample1.name = "test-animal"
587
        my_sample1.alias = "IMAGEA000000001"
588
        my_sample1.accession = "SAMEA0000001"
589
        my_sample2 = Mock()
590
        my_sample2.name = "test-sample"
591
        my_sample2.alias = "IMAGES000000001"
592
        my_sample2.accession = "SAMEA0000002"
593
        my_submission.get_samples.return_value = [my_sample1, my_sample2]
594
595
        # assert task and mock methods called
596
        self.common_tests(my_submission)
597
598
        # assert status for submissions
599
        self.submission_obj.refresh_from_db()
600
        self.assertEqual(self.submission_obj.status, COMPLETED)
601
602
        # check name status changed
603
        qs = Name.objects.filter(status=COMPLETED)
604
        self.assertEqual(len(qs), 2)
605
606
        # fetch two name objects
607
        name = Name.objects.get(name='ANIMAL:::ID:::132713')
608
        self.assertEqual(name.biosample_id, "SAMEA0000001")
609
610
        name = Name.objects.get(name='Siems_0722_393449')
611
        self.assertEqual(name.biosample_id, "SAMEA0000002")
612
613
        message = 'Completed'
614
        notification_message = 'Successful submission into biosample'
615
616
        # calling a WebSocketMixin method
617
        self.check_message(message, notification_message)
618
619
    def test_fetch_status_no_accession(self):
620
        # a completed submission with two samples
621
        my_submission = Mock()
622
        my_submission.name = "test-fetch"
623
        my_submission.status = 'Submitted'
624
625
        # Add samples
626
        my_sample1 = Mock()
627
        my_sample1.name = "test-animal"
628
        my_sample1.alias = "IMAGEA000000001"
629
        my_sample1.accession = None
630
        my_sample2 = Mock()
631
        my_sample2.name = "test-sample"
632
        my_sample2.alias = "IMAGES000000001"
633
        my_sample2.accession = None
634
        my_submission.get_samples.return_value = [my_sample1, my_sample2]
635
636
        # assert task and mock methods called
637
        self.common_tests(my_submission)
638
639
        # assert status for submissions
640
        self.submission_obj.refresh_from_db()
641
        self.assertEqual(self.submission_obj.status, SUBMITTED)
642
643
        # check name status changed
644
        qs = Name.objects.filter(status=SUBMITTED)
645
        self.assertEqual(len(qs), self.n_to_submit)
646
647
    # http://docs.celeryproject.org/en/latest/userguide/testing.html#tasks-and-unit-tests
648
    @patch("biosample.tasks.FetchStatusTask.retry")
649
    @patch("biosample.tasks.FetchStatusTask.fetch_queryset")
650
    def test_fetch_status_retry(self, my_fetch, my_retry):
651
        """Test fetch status with retry"""
652
653
        # Set a side effect on the patched methods
654
        # so that they raise the errors we want.
655
        my_retry.side_effect = Retry()
656
        my_fetch.side_effect = ConnectionError()
657
658
        with raises(Retry):
659
            self.my_task.run()
660
661
        self.assertTrue(my_fetch.called)
662
        self.assertTrue(my_retry.called)
663
664
    # Test a non blocking instance
665
    @patch("biosample.tasks.FetchStatusTask.fetch_queryset")
666
    @patch("redis.lock.Lock.acquire", return_value=False)
667
    def test_fetch_status_nb(self, my_lock, my_fetch):
668
        """Test FetchSTatus while a lock is present"""
669
670
        res = self.my_task.run()
671
672
        # assert database is locked
673
        self.assertEqual(res, "%s already running!" % (self.my_task.name))
674
        self.assertFalse(my_fetch.called)
675
676
677
class FetchNotInDBTestCase(FetchMixin, TestCase):
678
    """A submission not in db"""
679
680
    def test_fetch_status(self):
681
        # mocking submissions. A submission not in db
682
        my_submission = Mock()
683
        my_submission.name = "not-present-in-db"
684
685
        # assert task and mock methods called
686
        self.common_tests(my_submission)
687
688
689
class FetchWithErrorsTestCase(FetchMixin, WebSocketMixin, TestCase):
690
    """Test a submission with errors for biosample"""
691
692
    fixtures = [
693
        'biosample/account',
694
        'biosample/managedteam',
695
        'image_app/animal',
696
        'image_app/dictbreed',
697
        'image_app/dictcountry',
698
        'image_app/dictrole',
699
        'image_app/dictsex',
700
        'image_app/dictspecie',
701
        'image_app/dictstage',
702
        'image_app/dictuberon',
703
        'image_app/name',
704
        'image_app/organization',
705
        'image_app/publication',
706
        'image_app/sample',
707
        'image_app/submission',
708
        'image_app/user'
709
    ]
710
711
    def setUp(self):
712
        # calling my base setup
713
        super().setUp()
714
715
        # a draft submission with errors
716
        my_submission = Mock()
717
        my_submission.name = "test-fetch"
718
        my_submission.status = 'Draft'
719
        my_submission.has_errors.return_value = Counter({True: 1, False: 1})
720
        my_submission.get_status.return_value = Counter({'Complete': 2})
721
722
        # Add samples. Suppose that first failed, second is ok
723
        my_validation_result1 = Mock()
724
        my_validation_result1.errorMessages = {
725
            'Ena': [
726
                'a sample message',
727
            ]
728
        }
729
730
        my_sample1 = Mock()
731
        my_sample1.name = "test-animal"
732
        my_sample1.alias = "IMAGEA000000001"
733
        my_sample1.has_errors.return_value = True
734
        my_sample1.get_validation_result.return_value = my_validation_result1
735
736
        # sample2 is ok
737
        my_validation_result2 = Mock()
738
        my_validation_result2.errorMessages = None
739
740
        my_sample2 = Mock()
741
        my_sample2.name = "test-sample"
742
        my_sample2.alias = "IMAGES000000001"
743
        my_sample2.has_errors.return_value = False
744
        my_sample2.get_validation_result.return_value = my_validation_result2
745
746
        # simulate that IMAGEA000000001 has errors
747
        my_submission.get_samples.return_value = [my_sample1, my_sample2]
748
749
        # track objects
750
        self.my_submission = my_submission
751
        self.my_validation_result1 = my_validation_result1
752
        self.my_validation_result2 = my_validation_result2
753
        self.my_sample1 = my_sample1
754
        self.my_sample2 = my_sample2
755
756
        # track names
757
        self.animal_name = Name.objects.get(pk=3)
758
        self.sample_name = Name.objects.get(pk=4)
759
760
    def common_tests(self):
761
        # assert task and mock methods called
762
        super().common_tests(self.my_submission)
763
764
        # assert custom mock attributes called
765
        self.assertTrue(self.my_sample1.has_errors.called)
766
        self.assertTrue(self.my_sample1.get_validation_result.called)
767
768
        # if sample has no errors, no all methods will be called
769
        self.assertTrue(self.my_sample2.has_errors.called)
770
        self.assertFalse(self.my_sample2.get_validation_result.called)
771
772
    def test_fetch_status(self):
773
        # assert task and mock methods called
774
        self.common_tests()
775
776
        # assert submission status
777
        self.submission_obj.refresh_from_db()
778
        self.assertEqual(self.submission_obj.status, NEED_REVISION)
779
780
        # check name status changed only for animal (not sample)
781
        self.animal_name.refresh_from_db()
782
        self.assertEqual(self.animal_name.status, NEED_REVISION)
783
784
        self.sample_name.refresh_from_db()
785
        self.assertEqual(self.sample_name.status, SUBMITTED)
786
787
        message = 'Need Revision'
788
        notification_message = 'Error in biosample submission'
789
790
        # calling a WebSocketMixin method
791
        self.check_message(message, notification_message)
792
793
    def test_email_sent(self):
794
        # assert task and mock methods called
795
        self.common_tests()
796
797
        # test email sent
798
        self.assertEqual(len(mail.outbox), 1)
799
800
        # read email
801
        email = mail.outbox[0]
802
803
        self.assertEqual(
804
            "Error in biosample submission %s" % self.submission_obj_id,
805
            email.subject)
806
807
        # check for error messages in object
808
        self.assertIn("a sample message", email.body)
809
810
        message = 'Need Revision'
811
        notification_message = 'Error in biosample submission'
812
813
        # calling a WebSocketMixin method
814
        self.check_message(message, notification_message)
815
816
817
class FetchDraftTestCase(FetchMixin, TestCase):
818
    """a draft submission without errors"""
819
820
    def test_fetch_status(self):
821
        # a draft submission without errors
822
        my_submission = Mock()
823
        my_submission.name = "test-fetch"
824
        my_submission.status = 'Draft'
825
        my_submission.has_errors.return_value = Counter({False: 1})
826
        my_submission.get_status.return_value = Counter({'Complete': 1})
827
828
        # assert task and mock methods called
829
        self.common_tests(my_submission)
830
831
        # assert status for submissions
832
        self.submission_obj.refresh_from_db()
833
        self.assertEqual(self.submission_obj.status, SUBMITTED)
834
835
        # testing a finalized biosample condition
836
        self.assertTrue(my_submission.finalize.called)
837
838
    def test_fetch_status_pending(self):
839
        """Testing status with pending validation"""
840
841
        # a draft submission without errors
842
        my_submission = Mock()
843
        my_submission.name = "test-fetch"
844
        my_submission.status = 'Draft'
845
        my_submission.has_errors.return_value = Counter({False: 1})
846
        my_submission.get_status.return_value = Counter({'Pending': 1})
847
848
        # assert task and mock methods called
849
        self.common_tests(my_submission)
850
851
        # assert status for submissions
852
        self.submission_obj.refresh_from_db()
853
        self.assertEqual(self.submission_obj.status, SUBMITTED)
854
855
        # testing a not finalized biosample condition
856
        self.assertFalse(my_submission.finalize.called)
857
858
    def test_fetch_status_submitted(self):
859
        """Testing status during biosample submission"""
860
861
        # a draft submission without errors
862
        my_submission = Mock()
863
        my_submission.name = "test-fetch"
864
        my_submission.status = 'Submitted'
865
        my_submission.has_errors.return_value = Counter({False: 1})
866
        my_submission.get_status.return_value = Counter({'Complete': 1})
867
868
        # assert task and mock methods called
869
        self.common_tests(my_submission)
870
871
        # assert status for submissions
872
        self.submission_obj.refresh_from_db()
873
        self.assertEqual(self.submission_obj.status, SUBMITTED)
874
875
        # testing a not finalized biosample condition
876
        self.assertFalse(my_submission.finalize.called)
877
878
879
class FetchLongTaskTestCase(FetchMixin, WebSocketMixin, TestCase):
880
    """A submission wich remain in the same status for a long time"""
881
882
    def setUp(self):
883
        # calling my base setup
884
        super().setUp()
885
886
        # make "now" 2 months ago
887
        testtime = timezone.now() - timedelta(days=60)
888
889
        # https://devblog.kogan.com/blog/testing-auto-now-datetime-fields-in-django
890
        with patch('django.utils.timezone.now') as mock_now:
891
            mock_now.return_value = testtime
892
893
            # update submission updated time with an older date than now
894
            self.submission_obj.updated_at = testtime
895
            self.submission_obj.save()
896
897 View Code Duplication
    def test_error_in_submitted_status(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
898
        # a still running submission
899
        self.my_submission = Mock()
900
        self.my_submission.name = "test-fetch"
901
        self.my_submission.status = 'Submitted'
902
903
        # assert task and mock methods called
904
        self.common_tests(self.my_submission)
905
906
        # test email sent
907
        self.assertEqual(len(mail.outbox), 1)
908
909
        # read email
910
        email = mail.outbox[0]
911
912
        self.assertEqual(
913
            "Error in biosample submission %s" % self.submission_obj_id,
914
            email.subject)
915
916
        # check submission.state changed
917
        self.submission_obj.refresh_from_db()
918
919
        self.assertEqual(self.submission_obj.status, ERROR)
920
        self.assertIn(
921
            "Biosample subission {} remained with the same status".format(
922
                    self.submission_obj),
923
            self.submission_obj.message
924
            )
925
926
        message = 'Error'
927
        notification_message = (
928
            'Biosample subission Cryoweb '
929
            '(United Kingdom, test) remained with the '
930
            'same status for more than 5 days. Please '
931
            'report it to InjectTool team')
932
933
        # calling a WebSocketMixin method
934
        self.check_message(message, notification_message)
935
936 View Code Duplication
    def test_error_in_draft_status(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
937
        # a still running submission
938
        self.my_submission = Mock()
939
        self.my_submission.name = "test-fetch"
940
        self.my_submission.status = 'Draft'
941
942
        # assert task and mock methods called
943
        self.common_tests(self.my_submission)
944
945
        # test email sent
946
        self.assertEqual(len(mail.outbox), 1)
947
948
        # read email
949
        email = mail.outbox[0]
950
951
        self.assertEqual(
952
            "Error in biosample submission %s" % self.submission_obj_id,
953
            email.subject)
954
955
        # check submission.state changed
956
        self.submission_obj.refresh_from_db()
957
958
        self.assertEqual(self.submission_obj.status, ERROR)
959
        self.assertIn(
960
            "Biosample subission {} remained with the same status".format(
961
                    self.submission_obj),
962
            self.submission_obj.message
963
            )
964
965
        message = 'Error'
966
        notification_message = (
967
            'Biosample subission Cryoweb '
968
            '(United Kingdom, test) remained with the '
969
            'same status for more than 5 days. Please '
970
            'report it to InjectTool team')
971
972
        # calling a WebSocketMixin method
973
        self.check_message(message, notification_message)
974
975
976
class FetchUnsupportedStatusTestCase(FetchMixin, TestCase):
977
    """A submission object with a status I can ignore"""
978
979
    def setUp(self):
980
        # calling my base setup
981
        super().setUp()
982
983
        # a still running submission
984
        self.my_submission = Mock()
985
        self.my_submission.name = "test-fetch"
986
987
        # passing submission to Mocked Root
988
        self.my_root.get_submission_by_name.return_value = self.my_submission
989
990
    def update_status(self, status):
991
        # change status
992
        self.submission_obj.status = status
993
        self.submission_obj.save()
994
995
    # override FetchMixing methods
996
    def common_tests(self, status):
997
        # update submission status
998
        self.update_status(status)
999
1000
        # NOTE that I'm calling the function directly, without delay
1001
        # (AsyncResult). I've patched the time consuming task
1002
        res = self.my_task.run()
1003
1004
        # assert a success with data uploading
1005
        self.assertEqual(res, "success")
1006
1007
        self.assertFalse(self.mock_auth.called)
1008
        self.assertFalse(self.mock_root.called)
1009
        self.assertFalse(self.my_root.get_submission_by_name.called)
1010
        self.assertFalse(self.my_submission.follow_url.called)
1011
1012
        # assert status for submissions
1013
        self.submission_obj.refresh_from_db()
1014
        self.assertEqual(self.submission_obj.status, status)
1015
1016
    def test_loaded(self):
1017
        """Test fecth_status with a loaded submission"""
1018
1019
        # assert task and mock methods called
1020
        self.common_tests(LOADED)
1021
1022
    def test_need_revision(self):
1023
        """Test fecth_status with a need_revision submission"""
1024
1025
        # assert task and mock methods called
1026
        self.common_tests(NEED_REVISION)
1027
1028
    def test_ready(self):
1029
        """Test fecth_status with a ready submission"""
1030
1031
        # assert task and mock methods called
1032
        self.common_tests(READY)
1033
1034
    def test_completed(self):
1035
        """Test fecth_status with a completed submission"""
1036
1037
        # assert task and mock methods called
1038
        self.common_tests(COMPLETED)
1039