SubmissionTest.test_finalize_not_ready()   A
last analyzed

Complexity

Conditions 2

Size

Total Lines 15
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 11
nop 1
dl 0
loc 15
rs 9.85
c 0
b 0
f 0
1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3
"""
4
Created on Thu Jul 12 14:23:09 2018
5
6
@author: Paolo Cozzi <[email protected]>
7
"""
8
9
import os
10
import json
11
import types
12
13
from collections import defaultdict
14
from unittest.mock import patch, Mock
15
from unittest import TestCase
16
17
from pyUSIrest.auth import Auth
18
from pyUSIrest.client import Document
19
from pyUSIrest.exceptions import NotReadyError, USIDataError
20
from pyUSIrest.usi import Submission, Sample
21
22
from .common import DATA_PATH
23
from .test_auth import generate_token
24
25
26
class SubmissionTest(TestCase):
27
    @classmethod
28
    def setup_class(cls):
29
        cls.mock_get_patcher = patch('requests.Session.get')
30
        cls.mock_get = cls.mock_get_patcher.start()
31
32
        cls.mock_post_patcher = patch('requests.Session.post')
33
        cls.mock_post = cls.mock_post_patcher.start()
34
35
        cls.mock_put_patcher = patch('requests.Session.put')
36
        cls.mock_put = cls.mock_put_patcher.start()
37
38
        cls.mock_patch_patcher = patch('requests.Session.patch')
39
        cls.mock_patch = cls.mock_patch_patcher.start()
40
41
        cls.mock_delete_patcher = patch('requests.Session.delete')
42
        cls.mock_delete = cls.mock_delete_patcher.start()
43
44
    @classmethod
45
    def teardown_class(cls):
46
        cls.mock_get_patcher.stop()
47
        cls.mock_post_patcher.stop()
48
        cls.mock_put_patcher.stop()
49
        cls.mock_patch_patcher.stop()
50
        cls.mock_delete_patcher.stop()
51
52
    def setUp(self):
53
        self.auth = Auth(token=generate_token())
54
55
        with open(os.path.join(DATA_PATH, "newSubmission.json")) as handle:
56
            data = json.load(handle)
57
58
        self.submission = Submission(self.auth, data=data)
59
60
        with open(os.path.join(DATA_PATH, "contents.json")) as handle:
61
            self.content = json.load(handle)
62
63
        # defining samples
64
        self.sample1 = {
65
            'alias': 'animal_1',
66
            'title': 'animal_title',
67
            'releaseDate': '2018-07-13',
68
            'taxonId': 9940,
69
            'taxon': 'Ovis aries',
70
            'attributes': {
71
                'material': [
72
                    {'value': 'organism',
73
                     'terms': [
74
                        {'url': 'http://purl.obolibrary.org/obo/OBI_0100026'}
75
                     ]}
76
                ],
77
                'project': [{'value': 'test'}]
78
            },
79
            'sampleRelationships': []}
80
81
        self.sample2 = {
82
            'alias': 'sample_1',
83
            'title': 'sample_title',
84
            'taxonId': 9940,
85
            'taxon': 'Ovis aries',
86
            'description': 'a description',
87
            'attributes': {
88
                'material': [
89
                    {'value': 'specimen from organism',
90
                     'terms': [
91
                        {'url': 'http://purl.obolibrary.org/obo/OBI_0001479'}
92
                     ]}
93
                ],
94
                'project': [{'value': 'test'}]
95
            },
96
            'sampleRelationships': [{
97
                'alias': 'animal_1',
98
                'relationshipNature': 'derived from'}]
99
            }
100
101
    def test_str(self):
102
        with open(os.path.join(DATA_PATH, "submissionStatus1.json")) as handle:
103
            data = json.load(handle)
104
105
        self.mock_get.return_value = Mock()
106
        self.mock_get.return_value.json.return_value = data
107
        self.mock_get.return_value.status_code = 200
108
109
        test = self.submission.__str__()
110
        self.assertIsInstance(test, str)
111
112
    def create_sample(self, sample):
113
        self.mock_get.return_value = Mock()
114
        self.mock_get.return_value.json.return_value = self.content
115
        self.mock_get.return_value.status_code = 200
116
117
        with open(os.path.join(DATA_PATH, "%s.json" % (sample))) as handle:
118
            data = json.load(handle)
119
120
        self.mock_post.return_value = Mock()
121
        self.mock_post.return_value.json.return_value = data
122
        self.mock_post.return_value.status_code = 201
123
124
        return self.submission.create_sample(getattr(self, sample))
125
126
    def test_create_sample(self):
127
        sample1 = self.create_sample("sample1")
128
        self.assertIsInstance(sample1, Sample)
129
130
        sample2 = self.create_sample("sample2")
131
        self.assertIsInstance(sample2, Sample)
132
133
    def mocked_get_samples(*args, **kwargs):
134
        class MockResponse:
135
            def __init__(self, json_data, status_code):
136
                self.json_data = json_data
137
                self.status_code = status_code
138
                self.text = "MockResponse not implemented: %s" % (args[0])
139
140
            def json(self):
141
                return self.json_data
142
143
        get_samples_link = (
144
            "https://submission-test.ebi.ac.uk/api/submissions/"
145
            "c8c86558-8d3a-4ac5-8638-7aa354291d61/contents/samples")
146
147
        with open(os.path.join(DATA_PATH, "samples.json")) as handle:
148
            samples = json.load(handle)
149
150
        with open(os.path.join(DATA_PATH, "validation1.json")) as handle:
151
            validation1 = json.load(handle)
152
153
        with open(os.path.join(DATA_PATH, "validation2.json")) as handle:
154
            validation2 = json.load(handle)
155
156
        # followin content -> samples
157
        if args[0] == get_samples_link:
158
            return MockResponse(samples, 200)
159
160
        # sample1 validation result
161
        elif args[0] == (
162
                'https://submission-test.ebi.ac.uk/api/samples/90c8f449-'
163
                'b3c2-4238-a22b-fd03bc02a5d2/validationResult'):
164
            return MockResponse(validation1, 200)
165
166
        # sample1 validtation result
167
        elif args[0] == (
168
                'https://submission-test.ebi.ac.uk/api/samples/58cb010a-'
169
                '3a89-42b7-8ccd-67b6f8b6dd4c/validationResult'):
170
            return MockResponse(validation2, 200)
171
172
        return MockResponse(None, 404)
173
174
    # We patch 'requests.Session.get' with our own method. The mock object is
175
    # passed in to our test case method.
176
    @patch('requests.Session.get', side_effect=mocked_get_samples)
177
    def test_get_samples(self, mock_get):
178
        samples = self.submission.get_samples(status='Complete')
179
180
        # samples is now a generator
181
        self.assertIsInstance(samples, types.GeneratorType)
182
183
        # convert it into a list
184
        samples = list(samples)
185
        self.assertEqual(len(samples), 2)
186
187
    def mocked_get_empty_samples(*args, **kwargs):
188
        """Simulate a submission with no samples at all"""
189
190
        class MockResponse:
191
            def __init__(self, json_data, status_code):
192
                self.json_data = json_data
193
                self.status_code = status_code
194
                self.text = "MockResponse not implemented: %s" % (args[0])
195
196
            def json(self):
197
                return self.json_data
198
199
        get_samples_link = (
200
            "https://submission-test.ebi.ac.uk/api/submissions/"
201
            "c8c86558-8d3a-4ac5-8638-7aa354291d61/contents/samples")
202
203
        with open(os.path.join(DATA_PATH, "empty_samples.json")) as handle:
204
            samples = json.load(handle)
205
206
        # followin content -> samples
207
        if args[0] == get_samples_link:
208
            return MockResponse(samples, 200)
209
210
        # default response
211
        return MockResponse(None, 404)
212
213
    # patch a request.get to return 0 samples for a submission
214
    @patch('requests.Session.get', side_effect=mocked_get_empty_samples)
215
    def test_get_empty_samples(self, mock_get):
216
        samples = self.submission.get_samples(status='Complete')
217
218
        self.assertRaises(StopIteration, next, samples)
219
220
    def test_get_status(self):
221
        with open(os.path.join(DATA_PATH, "validationResults.json")) as handle:
222
            data = json.load(handle)
223
224
        self.mock_get.return_value = Mock()
225
        self.mock_get.return_value.json.return_value = data
226
        self.mock_get.return_value.status_code = 200
227
228
        statuses = self.submission.get_status()
229
        self.assertEqual(statuses['Complete'], 2)
230
231
    def test_check_ready(self):
232
        with open(os.path.join(
233
                DATA_PATH, "availableSubmissionStatuses.json")) as handle:
234
            data = json.load(handle)
235
236
        self.mock_get.return_value = Mock()
237
        self.mock_get.return_value.json.return_value = data
238
        self.mock_get.return_value.status_code = 200
239
240
        check = self.submission.check_ready()
241
        self.assertTrue(check)
242
243
    def mocked_finalize(*args, **kwargs):
244
        class MockResponse:
245
            def __init__(self, json_data, status_code):
246
                self.json_data = json_data
247
                self.status_code = status_code
248
                self.text = "MockResponse not implemented: %s" % (args[0])
249
250
            def json(self):
251
                return self.json_data
252
253
        check_ready_link = (
254
            "https://submission-test.ebi.ac.uk/api/submissions/c8c86558-"
255
            "8d3a-4ac5-8638-7aa354291d61/availableSubmissionStatuses")
256
257
        with open(os.path.join(
258
                DATA_PATH, "availableSubmissionStatuses.json")) as handle:
259
            check_ready_data = json.load(handle)
260
261
        validation_link = (
262
            "https://submission-test.ebi.ac.uk/api/validationResults/search/"
263
            "by-submission?submissionId=c8c86558-8d3a-4ac5-8638-7aa354291d61")
264
265
        with open(os.path.join(DATA_PATH, "validationResults.json")) as handle:
266
            validation_data = json.load(handle)
267
268
        self_link = (
269
            "https://submission-test.ebi.ac.uk/api/submissions/"
270
            "c8c86558-8d3a-4ac5-8638-7aa354291d61")
271
272
        with open(os.path.join(DATA_PATH, "newSubmission.json")) as handle:
273
            self_data = json.load(handle)
274
275
        status_link = (
276
            "https://submission-test.ebi.ac.uk/api/submissions/c8c86558-"
277
            "8d3a-4ac5-8638-7aa354291d61/submissionStatus")
278
279
        with open(os.path.join(DATA_PATH, "submissionStatus2.json")) as handle:
280
            status_data = json.load(handle)
281
282
        if args[0] == check_ready_link:
283
            return MockResponse(check_ready_data, 200)
284
285
        elif args[0] == validation_link:
286
            return MockResponse(validation_data, 200)
287
288
        elif args[0] == self_link:
289
            return MockResponse(self_data, 200)
290
291
        elif args[0] == status_link:
292
            return MockResponse(status_data, 200)
293
294
        return MockResponse(None, 404)
295
296
    @patch('requests.Session.get', side_effect=mocked_finalize)
297
    def test_finalize(self, mock_get):
298
        self.mock_put.return_value = Mock()
299
        self.mock_put.return_value.json.return_value = {}
300
        self.mock_put.return_value.status_code = 200
301
302
        document = self.submission.finalize()
303
        self.assertIsInstance(document, Document)
304
305
    def test_finalize_not_ready(self):
306
        with open(os.path.join(
307
                DATA_PATH, "availableSubmissionStatuses.json")) as handle:
308
            data = json.load(handle)
309
310
        # remove a key from data
311
        del data['_embedded']
312
313
        self.mock_get.return_value = Mock()
314
        self.mock_get.return_value.json.return_value = data
315
        self.mock_get.return_value.status_code = 200
316
317
        self.assertRaises(
318
            NotReadyError,
319
            self.submission.finalize)
320
321
    def mocked_finalize_errors(*args, **kwargs):
322
        class MockResponse:
323
            def __init__(self, json_data, status_code):
324
                self.json_data = json_data
325
                self.status_code = status_code
326
                self.text = "MockResponse not implemented: %s" % (args[0])
327
328
            def json(self):
329
                return self.json_data
330
331
        # this variable will collect all replies
332
        replies = defaultdict(lambda: MockResponse(None, 404))
333
334
        # a custom function to set up replies for link
335 View Code Duplication
        def set_reply(url, filename, status=200):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
336
            # referring to the upper replies variable
337
            nonlocal replies
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable replies does not seem to be defined.
Loading history...
338
339
            # open data file
340
            with open(os.path.join(DATA_PATH, filename)) as handle:
341
                data = json.load(handle)
342
343
            # track reply to URL
344
            replies[url] = MockResponse(data, status)
345
346
        set_reply("https://submission-test.ebi.ac.uk/api/submissions/"
347
                  "c8c86558-8d3a-4ac5-8638-7aa354291d61/"
348
                  "availableSubmissionStatuses",
349
                  "availableSubmissionStatuses.json")
350
351
        set_reply("https://submission-test.ebi.ac.uk/api/"
352
                  "validationResults/search/by-submission?"
353
                  "submissionId=c8c86558-8d3a-4ac5-8638-7aa354291d61",
354
                  "validationResultsError.json")
355
356
        return replies[args[0]]
357
358
    @patch('requests.Session.get', side_effect=mocked_finalize_errors)
359
    def test_finalize_has_errors(self, my_get):
360
        self.assertRaises(
361
            USIDataError,
362
            self.submission.finalize)
363
364
    def test_delete(self):
365
        self.mock_delete.return_value = Mock()
366
        self.mock_delete.return_value.last_response = ''
367
        self.mock_delete.return_value.status_code = 204
368
369
        self.submission.delete()
370
371
372
class SampleTest(TestCase):
373
    @classmethod
374
    def setup_class(cls):
375
        cls.mock_get_patcher = patch('requests.Session.get')
376
        cls.mock_get = cls.mock_get_patcher.start()
377
378
        cls.mock_patch_patcher = patch('requests.Session.patch')
379
        cls.mock_patch = cls.mock_patch_patcher.start()
380
381
        cls.mock_delete_patcher = patch('requests.Session.delete')
382
        cls.mock_delete = cls.mock_delete_patcher.start()
383
384
    @classmethod
385
    def teardown_class(cls):
386
        cls.mock_get_patcher.stop()
387
        cls.mock_patch_patcher.stop()
388
        cls.mock_delete_patcher.stop()
389
390
    def setUp(self):
391
        self.auth = Auth(token=generate_token())
392
393
        with open(os.path.join(DATA_PATH, "sample2.json")) as handle:
394
            self.data = json.load(handle)
395
396
        self.sample = Sample(self.auth, data=self.data)
397
398
    def test_str(self):
399
        test = self.sample.__str__()
400
        self.assertIsInstance(test, str)
401
402
    def test_patch(self):
403
        self.mock_patch.return_value = Mock()
404
        self.mock_patch.return_value.json.return_value = self.data
405
        self.mock_patch.return_value.status_code = 200
406
407
        self.mock_get.return_value = Mock()
408
        self.mock_get.return_value.json.return_value = self.data
409
        self.mock_get.return_value.status_code = 200
410
411
        self.sample.patch(sample_data={'title': 'new title'})
412
413
    def test_delete(self):
414
        self.mock_delete.return_value = Mock()
415
        self.mock_delete.return_value.last_response = ''
416
        self.mock_delete.return_value.status_code = 204
417
418
        self.sample.delete()
419