Passed
Pull Request — master (#40)
by Paolo
01:16
created

excel.tests.test_helpers   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 307
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 15
eloc 146
dl 0
loc 307
rs 10
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A ExcelTemplateReaderTestCase.setUp() 0 11 1
A UploadTemplateTestCase.test_upload_template_errors_with_sex() 0 13 1
A UploadTemplateTestCase.test_upload_template_errors_with_accuracies() 0 13 1
A ExcelTemplateReaderTestCase.check_generator() 0 3 1
A ExcelTemplateReaderTestCase.test_get_animal_records() 0 5 1
A ExcelTemplateReaderTestCase.test_check_sheets() 0 15 1
A ExcelTemplateReaderTestCase.test_check_columns() 0 7 1
A ExcelTemplateReaderTestCase.test_check_columns_issue() 0 53 1
A ExcelTemplateReaderTestCase.test_check_accuracies() 0 7 1
A ExcelTemplateReaderTestCase.test_get_sample_records() 0 5 1
A ExcelTemplateReaderTestCase.test_get_breed_records() 0 5 1
A UploadTemplateTestCase.test_upload_template_errors_with_species() 0 13 1
A ExcelTemplateReaderTestCase.test_check_accuracies_issue() 0 63 1
A ExcelMixin.test_upload_template() 0 17 1
A ExcelMixin.check_errors() 0 7 1
1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3
"""
4
Created on Tue Jul  2 10:58:42 2019
5
6
@author: Paolo Cozzi <[email protected]>
7
"""
8
9
import types
10
from collections import defaultdict
11
from unittest.mock import patch, Mock
12
13
from django.test import TestCase
14
15
from common.tests import WebSocketMixin
16
from image_app.tests.mixins import (
17
    DataSourceMixinTestCase, FileReaderMixinTestCase)
18
19
from ..helpers import ExcelTemplateReader, upload_template, TEMPLATE_COLUMNS
20
from .common import BaseExcelMixin
21
22
23
class ExcelTemplateReaderTestCase(
24
        FileReaderMixinTestCase, DataSourceMixinTestCase, BaseExcelMixin,
25
        TestCase):
26
    """Test excel class upload"""
27
28
    def setUp(self):
29
        # calling my base class setup
30
        super().setUp()
31
32
        self.maxDiff = None
33
34
        # crate a Excel Template object
35
        self.reader = ExcelTemplateReader()
36
37
        # get filenames for DataSourceMixinTestCase.dst_path
38
        self.reader.read_file(self.dst_path)
39
40
    def test_check_sheets(self):
41
        # test check sheets method
42
        status, not_found = self.reader.check_sheets()
43
44
        self.assertTrue(status)
45
        self.assertEqual(not_found, [])
46
47
        # override sheet names
48
        self.reader.sheet_names = []
49
50
        # check method
51
        status, not_found = self.reader.check_sheets()
52
53
        self.assertFalse(status)
54
        self.assertEqual(not_found, ['breed', 'animal', 'sample'])
55
56
    def test_check_columns(self):
57
        # test check sheets method
58
        status, not_found = self.reader.check_columns()
59
60
        self.assertTrue(status)
61
        self.assertIsInstance(not_found, defaultdict)
62
        self.assertEqual(len(not_found), 0)
63
64
    @patch('xlrd.open_workbook')
65
    def test_check_columns_issue(self, mock_open):
66
        """Test a file with columns issues"""
67
68
        # creating a mock excel book
69
        mock_book = Mock()
70
71
        # customizing the mock object
72
        mock_book.sheet_names.return_value = ['breed', 'animal', 'sample']
73
74
        # creating a mock sheet for breed
75
        breed_sheet = Mock()
76
        breed_sheet.nrows = 1
77
78
        # now setting rows to a fake sheet
79
        breed_sheet.row_values.return_value = []
80
81
        # creating a mock sheet for animal
82
        animal_sheet = Mock()
83
        animal_sheet.nrows = 1
84
85
        # now setting rows to a fake sheet
86
        animal_sheet.row_values.return_value = []
87
88
        # creating a mock sheet for sample
89
        sample_sheet = Mock()
90
        sample_sheet.nrows = 1
91
92
        # now setting rows to a fake sheet
93
        sample_sheet.row_values.return_value = []
94
95
        # finally setting sheet to the fabe excel object
96
        mock_book.sheet_by_name.side_effect = [
97
            breed_sheet, animal_sheet, sample_sheet]
98
99
        # returning the mock object when opening a workbook
100
        mock_open.return_value = mock_book
101
102
        # now calling methods
103
        reader = ExcelTemplateReader()
104
        reader.read_file("fake file")
105
106
        # create the reference error output
107
        reference = defaultdict(list)
108
        reference['breed'] += TEMPLATE_COLUMNS['breed']
109
        reference['animal'] += TEMPLATE_COLUMNS['animal']
110
        reference['sample'] += TEMPLATE_COLUMNS['sample']
111
112
        status, test = reader.check_columns()
113
114
        self.assertFalse(status)
115
        self.assertIsInstance(test, defaultdict)
116
        self.assertDictEqual(reference, test)
117
118
    def check_generator(self, records, length):
119
        self.assertIsInstance(records, types.GeneratorType)
120
        self.assertEqual(len(list(records)), length)
121
122
    def test_get_breed_records(self):
123
        """get_breed_records returns an iterator"""
124
125
        breeds = self.reader.get_breed_records()
126
        self.check_generator(breeds, 2)
127
128
    def test_get_animal_records(self):
129
        """get_animal_records returns an iterator"""
130
131
        animals = self.reader.get_animal_records()
132
        self.check_generator(animals, 3)
133
134
    def test_get_sample_records(self):
135
        """get_sample_records returns an iterator"""
136
137
        samples = self.reader.get_sample_records()
138
        self.check_generator(samples, 3)
139
140
    def test_check_accuracies(self):
141
        """Test check accuracies method"""
142
143
        check, not_found = self.reader.check_accuracies()
144
145
        self.assertTrue(check)
146
        self.assertEqual(len(not_found), 0)
147
148
    @patch('xlrd.open_workbook')
149
    def test_check_accuracies_issue(self, mock_open):
150
        """Checking issues with accuracy in excels data"""
151
152
        # creating a mock excel book
153
        mock_book = Mock()
154
155
        # customizing the mock object
156
        mock_book.sheet_names.return_value = ['breed', 'animal', 'sample']
157
158
        # creating a mock sheet for animal
159
        animal_sheet = Mock()
160
        animal_sheet.nrows = 2
161
162
        # creating a fake row of data
163
        fake_row = ["" for col in TEMPLATE_COLUMNS['animal']]
164
165
        # get birth location accuracy index
166
        accuracy_idx = TEMPLATE_COLUMNS['animal'].index(
167
            "Birth location accuracy")
168
169
        # set a fake accuracy item
170
        fake_row[accuracy_idx] = "Fake"
171
172
        # now setting rows to a fake sheet
173
        animal_sheet.row_values.side_effect = [
174
            TEMPLATE_COLUMNS['animal'],
175
            fake_row]
176
177
        # creating a mock sheet for sample
178
        sample_sheet = Mock()
179
        sample_sheet.nrows = 2
180
181
        # creating a fake row of data
182
        fake_row = ["" for col in TEMPLATE_COLUMNS['sample']]
183
184
        # get birth location accuracy index
185
        accuracy_idx = TEMPLATE_COLUMNS['sample'].index(
186
            "Collection place accuracy")
187
188
        # set a fake accuracy item
189
        fake_row[accuracy_idx] = "Fake"
190
191
        # now setting rows to a fake sheet
192
        sample_sheet.row_values.side_effect = [
193
            TEMPLATE_COLUMNS['sample'],
194
            fake_row]
195
196
        # finally setting sheet to the fabe excel object
197
        mock_book.sheet_by_name.side_effect = [animal_sheet, sample_sheet]
198
199
        # returning the mock object when opening a workbook
200
        mock_open.return_value = mock_book
201
202
        # now calling methods
203
        reader = ExcelTemplateReader()
204
        reader.read_file("fake file")
205
206
        # define the expected value
207
        reference = (False, set(["Fake"]))
208
        test = reader.check_accuracies()
209
210
        self.assertEqual(reference, test)
211
212
213
class ExcelMixin(DataSourceMixinTestCase, WebSocketMixin, BaseExcelMixin):
214
    """Common tests for Excel classes"""
215
216
    # define the method to upload data from. Since the function is now inside
217
    # a class it becomes a method, specifically a bound method and is supposed
218
    # to receive the self attribute by default. If we don't want to get the
219
    # self attribute, we have to declare function as a staticmetho
220
    # https://stackoverflow.com/a/35322635/4385116
221
    upload_method = staticmethod(upload_template)
222
223
    def test_upload_template(self):
224
        """Testing uploading and importing data from excel template to UID"""
225
226
        # test data loaded
227
        message = "Template import completed for submission"
228
        self.upload_datasource(message)
229
230
        # check async message called
231
        notification_message = (
232
            'Template import completed for submission: 1')
233
        validation_message = {
234
            'animals': 3, 'samples': 3,
235
            'animal_unkn': 3, 'sample_unkn': 3,
236
            'animal_issues': 0, 'sample_issues': 0}
237
238
        # check async message called using WebSocketMixin.check_message
239
        self.check_message('Loaded', notification_message, validation_message)
240
241
    def check_errors(self, my_check, message, notification_message):
242
        """Common stuff for error in excel template loading"""
243
244
        super().check_errors(my_check, message)
245
246
        # check async message called using WebSocketMixin.check_message
247
        self.check_message('Error', notification_message)
248
249
250
class UploadTemplateTestCase(ExcelMixin, TestCase):
251
    """Test uploading data for Template excel path"""
252
253
    @patch("excel.helpers.ExcelTemplateReader.check_species",
254
           return_value=[False, 'Rainbow trout'])
255
    def test_upload_template_errors_with_species(self, my_check):
256
        """Testing importing with data into UID with errors in species"""
257
258
        message = "Some species are not loaded in UID database"
259
        notification_message = (
260
            'Error in importing data: Some species '
261
            'are not loaded in UID database: Rainbow '
262
            'trout')
263
264
        # check template import fails
265
        self.check_errors(my_check, message, notification_message)
266
267
    @patch("excel.helpers.ExcelTemplateReader.check_sex",
268
           return_value=[False, 'unknown'])
269
    def test_upload_template_errors_with_sex(self, my_check):
270
        """Testing importing with data into UID with errors"""
271
272
        message = "Not all Sex terms are loaded into database"
273
        notification_message = (
274
            'Error in importing data: Not all Sex '
275
            'terms are loaded into database: check '
276
            'for unknown in your dataset')
277
278
        # check template import fails
279
        self.check_errors(my_check, message, notification_message)
280
281
    @patch("excel.helpers.ExcelTemplateReader.check_accuracies",
282
           return_value=(False, set(["Fake"])))
283
    def test_upload_template_errors_with_accuracies(self, my_check):
284
        """Testing importing with data into UID with errors"""
285
286
        message = "Not all accuracy levels are defined in database"
287
        notification_message = (
288
            "Error in importing data: Not all accuracy "
289
            "levels are defined in database: check "
290
            "for {'Fake'} in your dataset")
291
292
        # check template import fails
293
        self.check_errors(my_check, message, notification_message)
294
295
296
class ReloadTemplateTestCase(ExcelMixin, TestCase):
297
    """Simulate a template reload case. Load data as in
298
    UploadTemplateTestCase, then call test which reload the same data"""
299
300
    # override used fixtures
301
    fixtures = [
302
        'crbanim/auth',
303
        'excel/dictspecie',
304
        'excel/image_app',
305
        'excel/submission',
306
        'excel/speciesynonym'
307
    ]
308