Passed
Pull Request — master (#35)
by Paolo
01:13
created

CheckBreed.test_add_breed()   A

Complexity

Conditions 1

Size

Total Lines 21
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 14
dl 0
loc 21
rs 9.7
c 0
b 0
f 0
cc 1
nop 1
1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3
"""
4
Created on Thu Sep 20 16:01:04 2018
5
6
@author: Paolo Cozzi <[email protected]>
7
"""
8
9
# --- import
10
from unittest.mock import patch, Mock
11
12
from django.conf import settings
13
from django.core.management import call_command
14
from django.test import TestCase
15
16
from common.constants import ERROR
17
from language.models import SpecieSynonym
18
from image_app.models import (
19
    Submission, DictBreed, Name, Animal, Sample, DictSex,
20
    DictCountry, DictSpecie)
21
from common.tests import DataSourceMixinTestCase
22
23
from ..helpers import (
24
    upload_cryoweb, check_species, CryoWebImportError, cryoweb_import,
25
    check_UID)
26
from ..models import db_has_data, truncate_database, BreedsSpecies
27
28
29
class BaseTestCase():
30
    # import this file and populate database once
31
    fixtures = [
32
        'cryoweb/dictbreed',
33
        'image_app/dictcountry',
34
        'image_app/dictrole',
35
        'image_app/dictsex',
36
        'image_app/organization',
37
        'image_app/submission',
38
        'image_app/user',
39
        'language/dictspecie',
40
        'language/speciesynonym'
41
    ]
42
43
    # By default, fixtures are only loaded into the default database. If you
44
    # are using multiple databases and set multi_db=True, fixtures will be
45
    # loaded into all databases. However, this will raise problems when
46
    # managing extended user models
47
    multi_db = False
48
49
    def setUp(self):
50
        # calling my base class setup
51
        super().setUp()
52
53
        # track submission
54
        self.submission = Submission.objects.get(pk=1)
55
56
57
class CryoWebMixin(object):
58
    """Custom methods to upload cryoweb data into database for testing"""
59
60
    @classmethod
61
    def setUpClass(cls):
62
        # calling my base class setup
63
        super().setUpClass()
64
65
        # this fixture have to be loaded in a secondary (test) database,
66
        # I can't upload it using names and fixture section, so it will
67
        # be added manually using loaddata
68
        call_command(
69
            'loaddata',
70
            'cryoweb/cryoweb.json',
71
            app='cryoweb',
72
            database='cryoweb',
73
            verbosity=0)
74
75
    @classmethod
76
    def tearDownClass(cls):
77
        # truncate cryoweb database after loading
78
        if db_has_data():
79
            truncate_database()
80
81
        # calling my base class teardown class
82
        super().tearDownClass()
83
84
85
class CheckSpecie(CryoWebMixin, BaseTestCase, TestCase):
86
    def test_check_species(self):
87
        """Testing species and synonyms"""
88
89
        united_kingdom = DictCountry.objects.get(label="United Kingdom")
90
91
        # test for species synonoms for cryoweb
92
        self.assertTrue(check_species(united_kingdom))
93
94
        # now delete a synonym
95
        synonym = SpecieSynonym.objects.get(
96
            language__label='United Kingdom',
97
            word='Cattle')
98
        synonym.delete()
99
100
        self.assertFalse(check_species(united_kingdom))
101
102
    def test_no_species(self):
103
        """Test no species in cryoweb database"""
104
105
        queryset = BreedsSpecies.objects.all()
106
        queryset.delete()
107
108
        self.assertRaisesRegex(
109
            CryoWebImportError,
110
            "You have no species",
111
            check_species, "United Kingdom")
112
113
114
class CheckBreed(TestCase):
115
    # import this file and populate database once
116
    fixtures = [
117
        'image_app/dictbreed',
118
        'image_app/dictcountry',
119
        'image_app/dictrole',
120
        'image_app/dictsex',
121
        'image_app/dictspecie',
122
        'image_app/organization',
123
        'image_app/submission',
124
        'image_app/user',
125
    ]
126
127
    def test_add_breed(self):
128
        italy = DictCountry.objects.get(label="Italy")
129
        united_kingdom = DictCountry.objects.get(label="United Kingdom")
130
131
        sus = DictSpecie.objects.get(label="Sus scrofa")
132
133
        # inserting an already present breed get the object without creation
134
        breed, created = DictBreed.objects.get_or_create(
135
            supplied_breed="Bunte Bentheimer",
136
            specie=sus,
137
            country=united_kingdom)
138
139
        self.assertFalse(created)
140
141
        # inserting a breed in a different country add a record
142
        breed, created = DictBreed.objects.get_or_create(
143
            supplied_breed="Bunte Bentheimer",
144
            specie=sus,
145
            country=italy)
146
147
        self.assertTrue(created)
148
149
150
class CheckUIDTest(CryoWebMixin, BaseTestCase, TestCase):
151
    def test_empty_dictsex(self):
152
        """Empty dictsex and check that I can't proceed"""
153
154
        queryset = DictSex.objects.all()
155
        queryset.delete()
156
157
        self.assertRaisesRegex(
158
            CryoWebImportError,
159
            "You have to upload DictSex data",
160
            check_UID, self.submission)
161
162
    def test_no_synonym(self):
163
        # now delete a synonym
164
        synonym = SpecieSynonym.objects.get(
165
            language__label='United Kingdom',
166
            word='Cattle')
167
        synonym.delete()
168
169
        self.assertRaisesRegex(
170
            CryoWebImportError,
171
            "Some species haven't a synonym!",
172
            check_UID, self.submission)
173
174
    def test_check_UID(self):
175
        """testing normal behaviour"""
176
177
        self.assertTrue(check_UID(self.submission))
178
179
180
class UploadCryoweb(DataSourceMixinTestCase, BaseTestCase, TestCase):
181
    # define attribute in DataSourceMixinTestCase
182
    model = Submission
183
184
    # need to clean database after testing import. Can't use CryowebMixin
185
    # since i need to test cryoweb import
186
    def tearDown(self):
187
        """Clean test database after modifications"""
188
189
        # truncate cryoweb database after loading
190
        if db_has_data():
191
            truncate_database()
192
193
        # calling my base class teardown class
194
        super().tearDown()
195
196
    def test_database_name(self):
197
        self.assertEqual(
198
            settings.DATABASES['cryoweb']['NAME'], 'test_cryoweb')
199
200
    @patch('cryoweb.helpers.send_message_to_websocket')
201
    @patch('asyncio.get_event_loop')
202
    def test_upload_cryoweb(self, asyncio_mock, send_message_to_websocket_mock):
203
        """Testing uploading and importing data from cryoweb to UID"""
204
        tmp = asyncio_mock.return_value
205
        tmp.run_until_complete = Mock()
206
207
        self.assertTrue(upload_cryoweb(self.submission.id))
208
        self.assertTrue(db_has_data())
209
210
        # if I try again to upload cryoweb, I will get a False object and
211
        # submission message
212
        self.assertRaises(
213
            CryoWebImportError,
214
            upload_cryoweb,
215
            self.submission.id)
216
217
        # reload submission
218
        self.submission = Submission.objects.get(pk=1)
219
220
        self.assertEqual(
221
            self.submission.status,
222
            ERROR)
223
224
        self.assertIn(
225
            "Cryoweb has data",
226
            self.submission.message)
227
        self.assertEqual(asyncio_mock.call_count, 1)
228
        self.assertEqual(tmp.run_until_complete.call_count, 1)
229
        self.assertEqual(send_message_to_websocket_mock.call_count, 1)
230
        send_message_to_websocket_mock.assert_called_with(
231
            {'message': 'Error',
232
             'notification_message': 'Error in importing data: Cryoweb '
233
                                     'has data'}, 1)
234
235
    # mock subprocess.run an raise Exception. Read it and update submission
236
    # message using helpers.upload_cryoweb
237 View Code Duplication
    @patch('cryoweb.helpers.send_message_to_websocket')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
238
    @patch('asyncio.get_event_loop')
239
    def test_upload_cryoweb_errors(self, asyncio_mock,
240
                                   send_message_to_websocket_mock):
241
        """Testing errors in uploading cryoweb data"""
242
        tmp = asyncio_mock.return_value
243
        tmp.run_until_complete = Mock()
244
245
        with patch('subprocess.run') as runMock:
246
            runMock.side_effect = Exception("Test upload failed")
247
            self.assertFalse(upload_cryoweb(self.submission.id))
248
249
            # reload submission
250
            self.submission = Submission.objects.get(pk=1)
251
252
            self.assertEqual(
253
                self.submission.status,
254
                ERROR)
255
256
            self.assertIn(
257
                "Test upload failed",
258
                self.submission.message)
259
            self.assertEqual(asyncio_mock.call_count, 1)
260
            self.assertEqual(tmp.run_until_complete.call_count, 1)
261
            self.assertEqual(send_message_to_websocket_mock.call_count, 1)
262
            send_message_to_websocket_mock.assert_called_with(
263
                {'message': 'Error',
264
                 'notification_message': 'Error in importing data: Test '
265
                                         'upload failed'}, 1)
266
267
268
class CryowebImport(CryoWebMixin, BaseTestCase, TestCase):
269
    def test_database_name(self):
270
        self.assertEqual(
271
            settings.DATABASES['cryoweb']['NAME'], 'test_cryoweb')
272
273 View Code Duplication
    @patch('cryoweb.helpers.send_message_to_websocket')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
274
    @patch('asyncio.get_event_loop')
275
    def test_cryoweb_import(self, asyncio_mock, send_message_to_websocket_mock):
276
        """Import from cryoweb staging database into UID"""
277
        tmp = asyncio_mock.return_value
278
        tmp.run_until_complete = Mock()
279
280
        self.assertTrue(cryoweb_import(self.submission))
281
282
        # check breed upload
283
        queryset = DictBreed.objects.all()
284
285
        breeds = [(dictbreed.supplied_breed, dictbreed.country.label)
286
                  for dictbreed in queryset]
287
288
        self.assertEqual(len(queryset), 4)
289
        self.assertListEqual(
290
            breeds, [
291
                ('Bunte Bentheimer', 'United Kingdom'),
292
                ('Ostfriesisches Milchschaf', 'Italy'),
293
                ('Aberdeen Angus', 'Germany'),
294
                ('Ostfriesisches Milchschaf', 'Germany')],
295
            msg="Check breeds loaded")
296
297
        # check name upload (5 animal, 1 sample)
298
        queryset = Name.objects.filter(submission=self.submission)
299
        self.assertEqual(len(queryset), 6, msg='check name load')
300
301
        # check animal name
302
        queryset = Animal.objects.all()
303
        self.assertEqual(len(queryset), 3, msg="check animal load")
304
305
        queryset = Sample.objects.all()
306
        self.assertEqual(len(queryset), 1, msg="check sample load")
307
308
        self.assertEqual(asyncio_mock.call_count, 1)
309
        self.assertEqual(tmp.run_until_complete.call_count, 1)
310
        self.assertEqual(send_message_to_websocket_mock.call_count, 1)
311
        send_message_to_websocket_mock.assert_called_with(
312
            {'message': 'Loaded',
313
             'notification_message': 'Cryoweb import completed for '
314
                                     'submission: 1',
315
             'validation_message': {'animals': 3, 'samples': 1,
316
                                    'animal_unkn': 3, 'sample_unkn': 1,
317
                                    'animal_issues': 0, 'sample_issues': 0}}, 1)
318
319 View Code Duplication
    @patch('cryoweb.helpers.send_message_to_websocket')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
320
    @patch('asyncio.get_event_loop')
321
    @patch("cryoweb.helpers.check_UID", side_effect=Exception("Test message"))
322
    def test_cryoweb_import_errors(self, my_check, asyncio_mock,
323
                                   send_message_to_websocket_mock):
324
        """Testing importing with data into UID with errors"""
325
        tmp = asyncio_mock.return_value
326
        tmp.run_until_complete = Mock()
327
328
        self.assertFalse(cryoweb_import(self.submission))
329
        self.assertTrue(my_check.called)
330
331
        # reload submission
332
        self.submission = Submission.objects.get(pk=1)
333
334
        self.assertEqual(
335
            self.submission.status,
336
            ERROR)
337
338
        self.assertIn(
339
            "Test message",
340
            self.submission.message)
341
        self.assertEqual(asyncio_mock.call_count, 1)
342
        self.assertEqual(tmp.run_until_complete.call_count, 1)
343
        self.assertEqual(send_message_to_websocket_mock.call_count, 1)
344
        send_message_to_websocket_mock.assert_called_with(
345
            {'message': 'Error',
346
             'notification_message': 'Error in importing data: Test message'},
347
            1)
348
349
350
class CryowebReload(CryoWebMixin, BaseTestCase, TestCase):
351
    """Simulate a cryoweb reload case. Load data as in CryowebImport, then
352
    call test which reload the same data"""
353
354
    # override fixtures
355
    fixtures = [
356
        'cryoweb/auth',
357
        'cryoweb/dictbreed',
358
        'cryoweb/image_app',
359
        'language/dictspecie',
360
        'language/speciesynonym'
361
    ]
362
363 View Code Duplication
    @patch('cryoweb.helpers.send_message_to_websocket')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
364
    @patch('asyncio.get_event_loop')
365
    def test_cryoweb_import(
366
            self, asyncio_mock, send_message_to_websocket_mock):
367
        """Import from cryoweb staging database into UID, anche check that
368
        validationsummary is correct"""
369
370
        # stuff to simulating real time messages
371
        tmp = asyncio_mock.return_value
372
        tmp.run_until_complete = Mock()
373
374
        self.assertTrue(cryoweb_import(self.submission))
375
376
        # check breed upload
377
        queryset = DictBreed.objects.all()
378
379
        breeds = [(dictbreed.supplied_breed, dictbreed.country.label)
380
                  for dictbreed in queryset]
381
382
        self.assertEqual(len(queryset), 4)
383
        self.assertListEqual(
384
            breeds, [
385
                ('Bunte Bentheimer', 'United Kingdom'),
386
                ('Ostfriesisches Milchschaf', 'Italy'),
387
                ('Aberdeen Angus', 'Germany'),
388
                ('Ostfriesisches Milchschaf', 'Germany')],
389
            msg="Check breeds loaded")
390
391
        # check name upload (5 animal, 1 sample)
392
        queryset = Name.objects.filter(submission=self.submission)
393
        self.assertEqual(len(queryset), 6, msg='check name load')
394
395
        # check animal name
396
        queryset = Animal.objects.all()
397
        self.assertEqual(len(queryset), 3, msg="check animal load")
398
399
        queryset = Sample.objects.all()
400
        self.assertEqual(len(queryset), 1, msg="check sample load")
401
402
        self.assertEqual(asyncio_mock.call_count, 1)
403
        self.assertEqual(tmp.run_until_complete.call_count, 1)
404
        self.assertEqual(send_message_to_websocket_mock.call_count, 1)
405
        send_message_to_websocket_mock.assert_called_with(
406
            {'message': 'Loaded',
407
             'notification_message': 'Cryoweb import completed for '
408
                                     'submission: 1',
409
             'validation_message': {'animals': 3, 'samples': 1,
410
                                    'animal_unkn': 3, 'sample_unkn': 1,
411
                                    'animal_issues': 0, 'sample_issues': 0}}, 1)
412