Test Failed
Push — master ( dba1ae...723a1d )
by Antonio
04:50 queued 13s
created

build.tests.unit.test_main   C

Complexity

Total Complexity 51

Size/Duplication

Total Lines 1619
Duplicated Lines 10.99 %

Importance

Changes 0
Metric Value
eloc 1185
dl 178
loc 1619
rs 6.78
c 0
b 0
f 0
wmc 51

45 Methods

Rating   Name   Duplication   Size   Complexity  
B TestMain.test_update_circuit_invalid_json() 64 64 1
A TestMain.test_list_no_circuits_stored() 0 12 1
B TestMain.test_create_schedule() 0 48 1
A TestMain.test_circuit_with_invalid_id() 13 13 1
A TestMain._uni_from_dict_side_effect() 0 5 1
B TestMain._add_storehouse_schedule_data() 0 86 1
B TestMain.test_load_circuits_by_interface() 0 187 1
A TestMain.test_handle_link_down() 0 12 1
B TestMain.test_update_schedule() 0 69 1
A TestMain.test_list_with_archived_circuits_stored_1() 13 13 1
A TestMain.test_circuit_with_valid_id() 12 12 1
A TestMain.test_list_with_archived_circuits_stored_2() 13 13 1
A TestMain.test_create_a_circuit_case_4() 0 35 1
B TestMain.test_delete_schedule() 0 55 1
B TestMain.test_update_circuit() 0 114 2
A TestMain.test_evc_from_dict_paths() 0 47 1
A TestMain.get_app_test_client() 0 5 1
B TestMain.test_create_circuit_already_enabled() 0 57 1
B TestMain.test_create_a_circuit_case_1() 0 77 1
A TestMain.test_create_a_circuit_case_3() 0 13 1
A TestMain.get_napp_urls() 0 24 4
A TestMain.test_list_schedules_from_storehouse() 0 30 1
A TestMain.test_verify_api_urls() 0 48 1
A TestMain.test_list_without_circuits() 0 7 1
A TestMain.test_delete_schedule_archived() 0 35 1
A TestMain.test_list_schedules__no_data_stored() 0 14 1
B TestMain.test_update_circuit_invalid_path() 0 74 1
A TestMain.test_get_specific_schedule_from_storehouse() 0 19 1
A TestMain.test_create_circuit_case_5() 0 39 1
A TestMain.test_get_specific_schedules_from_storehouse_not_found() 0 13 1
A TestMain.test_handle_link_up() 0 12 1
A TestMain.test_redeploy_evc_deleted() 0 10 1
A TestMain.test_get_event_listeners() 0 10 2
A TestMain.test_add_metadata() 0 15 1
A TestMain.test_update_schedule_archived() 0 45 1
A TestMain.test_list_schedules__no_data() 0 7 1
A TestMain.test_delete_no_evc() 0 8 1
A TestMain.test_list_with_circuits_stored() 0 13 1
A TestMain.test_evc_from_dict() 0 40 1
A TestMain.test_redeploy_evc_disabled() 0 10 1
A TestMain.test_evc_from_dict_links() 0 46 1
B TestMain.test_update_evc_no_json_mime() 63 63 1
A TestMain.test_create_a_circuit_case_2() 0 11 1
A TestMain.setUp() 0 17 2
A TestMain.test_redeploy_evc() 0 10 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like build.tests.unit.test_main often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
"""Module to test the main napp file."""
2
import json
3
from unittest import TestCase
4
from unittest.mock import MagicMock, PropertyMock, call, create_autospec, patch
5
6
from kytos.core.events import KytosEvent
7
from kytos.core.interface import UNI, Interface
8
from napps.kytos.mef_eline.exceptions import InvalidPath
9
from napps.kytos.mef_eline.models import EVC
10
from tests.helpers import get_controller_mock, get_uni_mocked
11
12
13
# pylint: disable=too-many-public-methods, too-many-lines
14
class TestMain(TestCase):
15
    """Test the Main class."""
16
17
    def setUp(self):
18
        """Execute steps before each tests.
19
20
        Set the server_name_url_url from kytos/mef_eline
21
        """
22
        self.server_name_url = 'http://localhost:8181/api/kytos/mef_eline'
23
24
        # The decorator run_on_thread is patched, so methods that listen
25
        # for events do not run on threads while tested.
26
        # Decorators have to be patched before the methods that are
27
        # decorated with them are imported.
28
        patch('kytos.core.helpers.run_on_thread', lambda x: x).start()
29
        # pylint: disable=import-outside-toplevel
30
        from napps.kytos.mef_eline.main import Main
31
32
        self.addCleanup(patch.stopall)
33
        self.napp = Main(get_controller_mock())
34
35
    def test_get_event_listeners(self):
36
        """Verify all event listeners registered."""
37
        expected_events = ['kytos/core.shutdown',
38
                           'kytos/core.shutdown.kytos/mef_eline',
39
                           'kytos/topology.link_up',
40
                           'kytos/topology.link_down']
41
        actual_events = self.napp.listeners()
42
43
        for _event in expected_events:
44
            self.assertIn(_event, actual_events, '%s' % _event)
45
46
    def test_verify_api_urls(self):
47
        """Verify all APIs registered."""
48
        expected_urls = [
49
            ({}, {'POST', 'OPTIONS'},
50
             '/api/kytos/mef_eline/v2/evc/'),
51
52
            ({}, {'OPTIONS', 'HEAD', 'GET'},
53
             '/api/kytos/mef_eline/v2/evc/'),
54
55
            ({'circuit_id': '[circuit_id]'}, {'OPTIONS', 'DELETE'},
56
             '/api/kytos/mef_eline/v2/evc/<circuit_id>'),
57
58
            ({'circuit_id': '[circuit_id]'}, {'OPTIONS', 'HEAD', 'GET'},
59
             '/api/kytos/mef_eline/v2/evc/<circuit_id>'),
60
61
            ({'circuit_id': '[circuit_id]'}, {'OPTIONS', 'PATCH'},
62
             '/api/kytos/mef_eline/v2/evc/<circuit_id>'),
63
64
            ({'circuit_id': '[circuit_id]'}, {'OPTIONS', 'HEAD', 'GET'},
65
             '/api/kytos/mef_eline/v2/evc/<circuit_id>/metadata'),
66
67
            ({'circuit_id': '[circuit_id]'}, {'OPTIONS', 'POST'},
68
             '/api/kytos/mef_eline/v2/evc/<circuit_id>/metadata'),
69
70
            ({'circuit_id': '[circuit_id]', 'key': '[key]'},
71
             {'OPTIONS', 'DELETE'},
72
             '/api/kytos/mef_eline/v2/evc/<circuit_id>/metadata/<key>'),
73
74
            ({'circuit_id': '[circuit_id]'}, {'OPTIONS', 'PATCH'},
75
             '/api/kytos/mef_eline/v2/evc/<circuit_id>/redeploy'),
76
77
            ({}, {'OPTIONS', 'GET', 'HEAD'},
78
             '/api/kytos/mef_eline/v2/evc/schedule'),
79
80
            ({}, {'POST', 'OPTIONS'},
81
             '/api/kytos/mef_eline/v2/evc/schedule/'),
82
83
            ({'schedule_id': '[schedule_id]'},
84
             {'OPTIONS', 'DELETE'},
85
             '/api/kytos/mef_eline/v2/evc/schedule/<schedule_id>'),
86
87
            ({'schedule_id': '[schedule_id]'},
88
             {'OPTIONS', 'PATCH'},
89
             '/api/kytos/mef_eline/v2/evc/schedule/<schedule_id>')
90
            ]
91
92
        urls = self.get_napp_urls(self.napp)
93
        self.assertCountEqual(expected_urls, urls)
94
95
    @patch('napps.kytos.mef_eline.main.Main._uni_from_dict')
96
    @patch('napps.kytos.mef_eline.models.evc.EVCBase._validate')
97
    def test_evc_from_dict(self, _validate_mock, uni_from_dict_mock):
98
        """
99
        Test the helper method that create an EVN from dict.
100
101
        Verify object creation with circuit data and schedule data.
102
        """
103
        _validate_mock.return_value = True
104
        uni_from_dict_mock.side_effect = ['uni_a', 'uni_z']
105
        payload = {
106
            "name": "my evc1",
107
            "uni_a": {
108
                "interface_id": "00:00:00:00:00:00:00:01:1",
109
                "tag": {
110
                    "tag_type": 1,
111
                    "value": 80
112
                }
113
            },
114
            "uni_z": {
115
                "interface_id": "00:00:00:00:00:00:00:02:2",
116
                "tag": {
117
                    "tag_type": 1,
118
                    "value": 1
119
                }
120
            },
121
            "circuit_scheduler": [{
122
                "frequency": "* * * * *",
123
                "action": "create"
124
            }],
125
            "queue_id": 5
126
        }
127
        # pylint: disable=protected-access
128
        evc_response = self.napp._evc_from_dict(payload)
129
        self.assertIsNotNone(evc_response)
130
        self.assertIsNotNone(evc_response.uni_a)
131
        self.assertIsNotNone(evc_response.uni_z)
132
        self.assertIsNotNone(evc_response.circuit_scheduler)
133
        self.assertIsNotNone(evc_response.name)
134
        self.assertIsNotNone(evc_response.queue_id)
135
136
    @patch('napps.kytos.mef_eline.main.Main._uni_from_dict')
137
    @patch('napps.kytos.mef_eline.models.evc.EVCBase._validate')
138
    @patch('kytos.core.Controller.get_interface_by_id')
139
    def test_evc_from_dict_paths(self, _get_interface_by_id_mock,
140
                                 _validate_mock, uni_from_dict_mock):
141
        """
142
        Test the helper method that create an EVN from dict.
143
144
        Verify object creation with circuit data and schedule data.
145
        """
146
        _get_interface_by_id_mock.return_value = True
147
        _validate_mock.return_value = True
148
        uni_from_dict_mock.side_effect = ['uni_a', 'uni_z']
149
        payload = {
150
            "name": "my evc1",
151
            "uni_a": {
152
                "interface_id": "00:00:00:00:00:00:00:01:1",
153
                "tag": {
154
                    "tag_type": 1,
155
                    "value": 80
156
                }
157
            },
158
            "uni_z": {
159
                "interface_id": "00:00:00:00:00:00:00:02:2",
160
                "tag": {
161
                    "tag_type": 1,
162
                    "value": 1
163
                }
164
            },
165
            "current_path": [],
166
            "primary_path": [
167
                {"endpoint_a": {"interface_id": "00:00:00:00:00:00:00:01:1"},
168
                 "endpoint_b": {"interface_id": "00:00:00:00:00:00:00:02:2"}}
169
            ],
170
            "backup_path": []
171
        }
172
173
        # pylint: disable=protected-access
174
        evc_response = self.napp._evc_from_dict(payload)
175
        self.assertIsNotNone(evc_response)
176
        self.assertIsNotNone(evc_response.uni_a)
177
        self.assertIsNotNone(evc_response.uni_z)
178
        self.assertIsNotNone(evc_response.circuit_scheduler)
179
        self.assertIsNotNone(evc_response.name)
180
        self.assertEqual(len(evc_response.current_path), 0)
181
        self.assertEqual(len(evc_response.backup_path), 0)
182
        self.assertEqual(len(evc_response.primary_path), 1)
183
184
    @patch('napps.kytos.mef_eline.main.Main._uni_from_dict')
185
    @patch('napps.kytos.mef_eline.models.evc.EVCBase._validate')
186
    @patch('kytos.core.Controller.get_interface_by_id')
187
    def test_evc_from_dict_links(self, _get_interface_by_id_mock,
188
                                 _validate_mock, uni_from_dict_mock):
189
        """
190
        Test the helper method that create an EVN from dict.
191
192
        Verify object creation with circuit data and schedule data.
193
        """
194
        _get_interface_by_id_mock.return_value = True
195
        _validate_mock.return_value = True
196
        uni_from_dict_mock.side_effect = ['uni_a', 'uni_z']
197
        payload = {
198
            "name": "my evc1",
199
            "uni_a": {
200
                "interface_id": "00:00:00:00:00:00:00:01:1",
201
                "tag": {
202
                    "tag_type": 1,
203
                    "value": 80
204
                }
205
            },
206
            "uni_z": {
207
                "interface_id": "00:00:00:00:00:00:00:02:2",
208
                "tag": {
209
                    "tag_type": 1,
210
                    "value": 1
211
                }
212
            },
213
            "primary_links": [
214
                {"endpoint_a": {"interface_id": "00:00:00:00:00:00:00:01:1"},
215
                 "endpoint_b": {"interface_id": "00:00:00:00:00:00:00:02:2"}}
216
            ],
217
            "backup_links": []
218
        }
219
220
        # pylint: disable=protected-access
221
        evc_response = self.napp._evc_from_dict(payload)
222
        self.assertIsNotNone(evc_response)
223
        self.assertIsNotNone(evc_response.uni_a)
224
        self.assertIsNotNone(evc_response.uni_z)
225
        self.assertIsNotNone(evc_response.circuit_scheduler)
226
        self.assertIsNotNone(evc_response.name)
227
        self.assertEqual(len(evc_response.current_links_cache), 0)
228
        self.assertEqual(len(evc_response.backup_links), 0)
229
        self.assertEqual(len(evc_response.primary_links), 1)
230
231
    def test_list_without_circuits(self):
232
        """Test if list circuits return 'no circuit stored.'."""
233
        api = self.get_app_test_client(self.napp)
234
        url = f'{self.server_name_url}/v2/evc/'
235
        response = api.get(url)
236
        self.assertEqual(response.status_code, 200, response.data)
237
        self.assertEqual(json.loads(response.data.decode()), {})
238
239
    @patch('napps.kytos.mef_eline.storehouse.StoreHouse.get_data')
240
    def test_list_no_circuits_stored(self, storehouse_data_mock):
241
        """Test if list circuits return all circuits stored."""
242
        circuits = {}
243
        storehouse_data_mock.return_value = circuits
244
245
        api = self.get_app_test_client(self.napp)
246
        url = f'{self.server_name_url}/v2/evc/'
247
248
        response = api.get(url)
249
        expected_result = circuits
250
        self.assertEqual(json.loads(response.data), expected_result)
251
252
    @patch('napps.kytos.mef_eline.storehouse.StoreHouse.get_data')
253
    def test_list_with_circuits_stored(self, storehouse_data_mock):
254
        """Test if list circuits return all circuits stored."""
255
        circuits = {'1': {'name': 'circuit_1'},
256
                    '2': {'name': 'circuit_2'}}
257
        storehouse_data_mock.return_value = circuits
258
259
        api = self.get_app_test_client(self.napp)
260
        url = f'{self.server_name_url}/v2/evc/'
261
262
        response = api.get(url)
263
        expected_result = circuits
264
        self.assertEqual(json.loads(response.data), expected_result)
265
266 View Code Duplication
    @patch('napps.kytos.mef_eline.storehouse.StoreHouse.get_data')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
267
    def test_list_with_archived_circuits_stored_1(self, storehouse_data_mock):
268
        """Test if list circuits return only circuits not archived."""
269
        circuits = {'1': {'name': 'circuit_1'},
270
                    '2': {'name': 'circuit_2', 'archived': True}}
271
        storehouse_data_mock.return_value = circuits
272
273
        api = self.get_app_test_client(self.napp)
274
        url = f'{self.server_name_url}/v2/evc/'
275
276
        response = api.get(url)
277
        expected_result = {'1': circuits['1']}
278
        self.assertEqual(json.loads(response.data), expected_result)
279
280 View Code Duplication
    @patch('napps.kytos.mef_eline.storehouse.StoreHouse.get_data')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
281
    def test_list_with_archived_circuits_stored_2(self, storehouse_data_mock):
282
        """Test if list circuits return all circuits."""
283
        circuits = {'1': {'name': 'circuit_1'},
284
                    '2': {'name': 'circuit_2', 'archived': True}}
285
        storehouse_data_mock.return_value = circuits
286
287
        api = self.get_app_test_client(self.napp)
288
        url = f'{self.server_name_url}/v2/evc/?archived=True'
289
290
        response = api.get(url)
291
        expected_result = circuits
292
        self.assertEqual(json.loads(response.data), expected_result)
293
294 View Code Duplication
    @patch('napps.kytos.mef_eline.storehouse.StoreHouse.get_data')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
295
    def test_circuit_with_valid_id(self, storehouse_data_mock):
296
        """Test if get_circuit return the circuit attributes."""
297
        circuits = {'1': {'name': 'circuit_1'},
298
                    '2': {'name': 'circuit_2'}}
299
        storehouse_data_mock.return_value = circuits
300
301
        api = self.get_app_test_client(self.napp)
302
        url = f'{self.server_name_url}/v2/evc/1'
303
        response = api.get(url)
304
        expected_result = circuits['1']
305
        self.assertEqual(json.loads(response.data), expected_result)
306
307 View Code Duplication
    @patch('napps.kytos.mef_eline.storehouse.StoreHouse.get_data')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
308
    def test_circuit_with_invalid_id(self, storehouse_data_mock):
309
        """Test if get_circuit return invalid circuit_id."""
310
        circuits = {'1': {'name': 'circuit_1'},
311
                    '2': {'name': 'circuit_2'}}
312
        storehouse_data_mock.return_value = circuits
313
314
        api = self.get_app_test_client(self.napp)
315
        url = f'{self.server_name_url}/v2/evc/3'
316
        response = api.get(url)
317
        expected_result = 'circuit_id 3 not found'
318
        self.assertEqual(json.loads(response.data)['description'],
319
                         expected_result)
320
321
    @patch('napps.kytos.mef_eline.models.evc.EVC.deploy')
322
    @patch('napps.kytos.mef_eline.storehouse.StoreHouse.get_data')
323
    @patch('napps.kytos.mef_eline.scheduler.Scheduler.add')
324
    @patch('napps.kytos.mef_eline.main.Main._uni_from_dict')
325
    @patch('napps.kytos.mef_eline.storehouse.StoreHouse.save_evc')
326
    @patch('napps.kytos.mef_eline.main.EVC.as_dict')
327
    @patch('napps.kytos.mef_eline.models.evc.EVC._validate')
328
    def test_create_a_circuit_case_1(self, *args):
329
        """Test create a new circuit."""
330
        # pylint: disable=too-many-locals
331
        (validate_mock, evc_as_dict_mock, save_evc_mock,
332
         uni_from_dict_mock, sched_add_mock, storehouse_data_mock,
333
         evc_deploy_mock) = args
334
335
        validate_mock.return_value = True
336
        save_evc_mock.return_value = True
337
        evc_deploy_mock.return_value = True
338
        uni1 = create_autospec(UNI)
339
        uni2 = create_autospec(UNI)
340
        uni1.interface = create_autospec(Interface)
341
        uni2.interface = create_autospec(Interface)
342
        uni1.interface.switch = '00:00:00:00:00:00:00:01'
343
        uni2.interface.switch = '00:00:00:00:00:00:00:02'
344
        uni_from_dict_mock.side_effect = [uni1, uni2]
345
        evc_as_dict_mock.return_value = {}
346
        sched_add_mock.return_value = True
347
        storehouse_data_mock.return_value = {}
348
349
        api = self.get_app_test_client(self.napp)
350
        url = f'{self.server_name_url}/v2/evc/'
351
        payload = {
352
                   "name": "my evc1",
353
                   "frequency": "* * * * *",
354
                   "uni_a": {
355
                     "interface_id": "00:00:00:00:00:00:00:01:1",
356
                     "tag": {
357
                       "tag_type": 1,
358
                       "value": 80
359
                     }
360
                   },
361
                   "uni_z": {
362
                     "interface_id": "00:00:00:00:00:00:00:02:2",
363
                     "tag": {
364
                       "tag_type": 1,
365
                       "value": 1
366
                     }
367
                   },
368
                   "dynamic_backup_path": True
369
                 }
370
371
        response = api.post(url, data=json.dumps(payload),
372
                            content_type='application/json')
373
        current_data = json.loads(response.data)
374
375
        # verify expected result from request
376
        self.assertEqual(201, response.status_code, response.data)
377
        self.assertIn('circuit_id', current_data)
378
379
        # verify uni called
380
        uni_from_dict_mock.called_twice()
381
        uni_from_dict_mock.assert_any_call(payload['uni_z'])
382
        uni_from_dict_mock.assert_any_call(payload['uni_a'])
383
384
        # verify validation called
385
        validate_mock.assert_called_once()
386
        validate_mock.assert_called_with(frequency='* * * * *',
387
                                         name='my evc1',
388
                                         uni_a=uni1,
389
                                         uni_z=uni2,
390
                                         dynamic_backup_path=True)
391
        # verify save method is called
392
        save_evc_mock.assert_called_once()
393
394
        # verify evc as dict is called to save in the box
395
        evc_as_dict_mock.assert_called_once()
396
        # verify add circuit in sched
397
        sched_add_mock.assert_called_once()
398
399
    @staticmethod
400
    def get_napp_urls(napp):
401
        """Return the kytos/mef_eline urls.
402
403
        The urls will be like:
404
405
        urls = [
406
            (options, methods, url)
407
        ]
408
409
        """
410
        controller = napp.controller
411
        controller.api_server.register_napp_endpoints(napp)
412
413
        urls = []
414
        for rule in controller.api_server.app.url_map.iter_rules():
415
            options = {}
416
            for arg in rule.arguments:
417
                options[arg] = "[{0}]".format(arg)
418
419
            if f'{napp.username}/{napp.name}' in str(rule):
420
                urls.append((options, rule.methods, f'{str(rule)}'))
421
422
        return urls
423
424
    @staticmethod
425
    def get_app_test_client(napp):
426
        """Return a flask api test client."""
427
        napp.controller.api_server.register_napp_endpoints(napp)
428
        return napp.controller.api_server.app.test_client()
429
430
    def test_create_a_circuit_case_2(self):
431
        """Test create a new circuit trying to send request without a json."""
432
        api = self.get_app_test_client(self.napp)
433
        url = f'{self.server_name_url}/v2/evc/'
434
435
        response = api.post(url)
436
        current_data = json.loads(response.data)
437
        expected_data = 'The request body mimetype is not application/json.'
438
439
        self.assertEqual(415, response.status_code, response.data)
440
        self.assertEqual(current_data['description'], expected_data)
441
442
    def test_create_a_circuit_case_3(self):
443
        """Test create a new circuit trying to send request with an
444
           invalid json."""
445
        api = self.get_app_test_client(self.napp)
446
        url = f'{self.server_name_url}/v2/evc/'
447
448
        response = api.post(url, data='This is an {Invalid:} JSON',
449
                            content_type='application/json')
450
        current_data = json.loads(response.data)
451
        expected_data = 'The request body is not a well-formed JSON.'
452
453
        self.assertEqual(400, response.status_code, response.data)
454
        self.assertEqual(current_data['description'], expected_data)
455
456
    @patch('napps.kytos.mef_eline.main.Main._uni_from_dict')
457
    def test_create_a_circuit_case_4(self, uni_from_dict_mock):
458
        """Test create a new circuit trying to send request with an
459
           invalid value."""
460
        # pylint: disable=too-many-locals
461
        uni_from_dict_mock.side_effect = ValueError('Could not instantiate')
462
        api = self.get_app_test_client(self.napp)
463
        url = f'{self.server_name_url}/v2/evc/'
464
465
        payload = {
466
                   "name": "my evc1",
467
                   "frequency": "* * * * *",
468
                   "uni_a": {
469
                     "interface_id": "00:00:00:00:00:00:00:01:76",
470
                     "tag": {
471
                       "tag_type": 1,
472
                       "value": 80
473
                     }
474
                   },
475
                   "uni_z": {
476
                     "interface_id": "00:00:00:00:00:00:00:02:2",
477
                     "tag": {
478
                       "tag_type": 1,
479
                       "value": 1
480
                     }
481
                   }
482
                 }
483
484
        response = api.post(url, data=json.dumps(payload),
485
                            content_type='application/json')
486
        current_data = json.loads(response.data)
487
        expected_data = 'Error creating UNI: Could not instantiate'
488
489
        self.assertEqual(400, response.status_code, response.data)
490
        self.assertEqual(current_data['description'], expected_data)
491
492
    @patch('napps.kytos.mef_eline.models.evc.EVC.deploy')
493
    @patch('napps.kytos.mef_eline.scheduler.Scheduler.add')
494
    @patch('napps.kytos.mef_eline.main.Main._uni_from_dict')
495
    @patch('napps.kytos.mef_eline.storehouse.StoreHouse.save_evc')
496
    @patch('napps.kytos.mef_eline.models.evc.EVC._validate')
497
    @patch('napps.kytos.mef_eline.main.EVC.as_dict')
498
    def test_create_circuit_already_enabled(self, *args):
499
        """Test create an already created circuit."""
500
        # pylint: disable=too-many-locals
501
        (evc_as_dict_mock, validate_mock, save_evc_mock,
502
         uni_from_dict_mock, sched_add_mock, evc_deploy_mock) = args
503
504
        validate_mock.return_value = True
505
        save_evc_mock.return_value = True
506
        sched_add_mock.return_value = True
507
        evc_deploy_mock.return_value = True
508
        uni1 = create_autospec(UNI)
509
        uni2 = create_autospec(UNI)
510
        uni1.interface = create_autospec(Interface)
511
        uni2.interface = create_autospec(Interface)
512
        uni1.interface.switch = '00:00:00:00:00:00:00:01'
513
        uni2.interface.switch = '00:00:00:00:00:00:00:02'
514
        uni_from_dict_mock.side_effect = [uni1, uni2, uni1, uni2]
515
516
        api = self.get_app_test_client(self.napp)
517
        payload = {
518
            "name": "my evc1",
519
            "uni_a": {
520
                "interface_id": "00:00:00:00:00:00:00:01:1",
521
                "tag": {
522
                    "tag_type": 1,
523
                    "value": 80
524
                }
525
            },
526
            "uni_z": {
527
                "interface_id": "00:00:00:00:00:00:00:02:2",
528
                "tag": {
529
                    "tag_type": 1,
530
                    "value": 1
531
                }
532
            },
533
            "dynamic_backup_path": True
534
        }
535
536
        evc_as_dict_mock.return_value = payload
537
        response = api.post(f'{self.server_name_url}/v2/evc/',
538
                            data=json.dumps(payload),
539
                            content_type='application/json')
540
        self.assertEqual(201, response.status_code)
541
542
        response = api.post(f'{self.server_name_url}/v2/evc/',
543
                            data=json.dumps(payload),
544
                            content_type='application/json')
545
        current_data = json.loads(response.data)
546
        expected_data = 'The EVC already exists.'
547
        self.assertEqual(current_data['description'], expected_data)
548
        self.assertEqual(409, response.status_code)
549
550
    @patch('napps.kytos.mef_eline.main.Main._uni_from_dict')
551
    def test_create_circuit_case_5(self, uni_from_dict_mock):
552
        """Test when neither primary path nor dynamic_backup_path is set."""
553
        api = self.get_app_test_client(self.napp)
554
        url = f'{self.server_name_url}/v2/evc/'
555
        uni1 = create_autospec(UNI)
556
        uni2 = create_autospec(UNI)
557
        uni1.interface = create_autospec(Interface)
558
        uni2.interface = create_autospec(Interface)
559
        uni1.interface.switch = '00:00:00:00:00:00:00:01'
560
        uni2.interface.switch = '00:00:00:00:00:00:00:02'
561
        uni_from_dict_mock.side_effect = [uni1, uni2, uni1, uni2]
562
563
        payload = {
564
                   "name": "my evc1",
565
                   "frequency": "* * * * *",
566
                   "uni_a": {
567
                     "interface_id": "00:00:00:00:00:00:00:01:1",
568
                     "tag": {
569
                       "tag_type": 1,
570
                       "value": 80
571
                     }
572
                   },
573
                   "uni_z": {
574
                     "interface_id": "00:00:00:00:00:00:00:02:2",
575
                     "tag": {
576
                       "tag_type": 1,
577
                       "value": 1
578
                     }
579
                   }
580
                 }
581
582
        response = api.post(url, data=json.dumps(payload),
583
                            content_type='application/json')
584
        current_data = json.loads(response.data)
585
        expected = 'The EVC must have a primary path or allow dynamic paths.'
586
587
        self.assertEqual(400, response.status_code, response.data)
588
        self.assertEqual(current_data['description'], expected)
589
590
    def test_load_circuits_by_interface(self):
591
        """Test if existing circuits are correctly loaded to the cache."""
592
        stored_circuits = {
593
            "182f5bac84074017a262a2321195dbb4": {
594
                "active": False,
595
                "archived": True,
596
                "backup_links": [],
597
                "backup_path": [],
598
                "bandwidth": 0,
599
                "circuit_scheduler": [
600
                    {
601
                        "action": "create",
602
                        "frequency": "*/3 * * * *",
603
                        "id": "db7f8a301e2b4ff69a2ad9a6267430e2"
604
                    },
605
                    {
606
                        "action": "remove",
607
                        "frequency": "2-59/3 * * * *",
608
                        "id": "b8a8bbe85bc144b0afc65181e4c069a1"
609
                    }
610
                ],
611
                "creation_time": "2019-08-09T19:25:06",
612
                "current_path": [],
613
                "dynamic_backup_path": True,
614
                "enabled": False,
615
                "end_date": "2018-12-29T15:16:50",
616
                "id": "182f5bac84074017a262a2321195dbb4",
617
                "name": "Teste2",
618
                "owner": None,
619
                "primary_links": [],
620
                "primary_path": [],
621
                "priority": 0,
622
                "request_time": "2019-08-09T19:25:06",
623
                "start_date": "2019-08-09T19:25:06",
624
                "uni_a": {
625
                    "interface_id": "00:00:00:00:00:00:00:03:12",
626
                    "tag": {
627
                        "tag_type": 1,
628
                        "value": 321
629
                    }
630
                },
631
                "uni_z": {
632
                    "interface_id": "00:00:00:00:00:00:00:06:11",
633
                    "tag": {
634
                        "tag_type": 1,
635
                        "value": 612
636
                    }
637
                }
638
            },
639
            "65c4582cc8f249c2a5947ef500c19e37": {
640
                "active": False,
641
                "archived": False,
642
                "backup_links": [],
643
                "backup_path": [],
644
                "bandwidth": 0,
645
                "circuit_scheduler": [
646
                    {
647
                        "action": "create",
648
                        "frequency": "*/3 * * * *",
649
                        "id": "0939dedf66ce431f85beb53daf578d73"
650
                    },
651
                    {
652
                        "action": "remove",
653
                        "frequency": "2-59/3 * * * *",
654
                        "id": "6cdcab31a11f44708e23776b4dad7893"
655
                    }
656
                ],
657
                "creation_time": "2019-07-22T16:01:24",
658
                "current_path": [],
659
                "dynamic_backup_path": True,
660
                "enabled": False,
661
                "end_date": "2018-12-29T15:16:50",
662
                "id": "65c4582cc8f249c2a5947ef500c19e37",
663
                "name": "Teste2",
664
                "owner": None,
665
                "primary_links": [],
666
                "primary_path": [
667
                    {
668
                        "active": False,
669
                        "enabled": True,
670
                        "endpoint_a": {
671
                            "active": False,
672
                            "enabled": True,
673
                            "id": "00:00:00:00:00:00:00:03:3",
674
                            "link": "0e2b5d7bc858b9f38db11b69",
675
                            "mac": "ae:6e:d3:96:83:5a",
676
                            "metadata": {},
677
                            "name": "s3-eth3",
678
                            "nni": True,
679
                            "port_number": 3,
680
                            "speed": 1250000000.0,
681
                            "switch": "00:00:00:00:00:00:00:03",
682
                            "type": "interface",
683
                            "uni": False
684
                        },
685
                        "endpoint_b": {
686
                            "active": False,
687
                            "enabled": True,
688
                            "id": "00:00:00:00:00:00:00:05:2",
689
                            "link": "0e2b5d7bc858b9f38db11b69",
690
                            "mac": "de:eb:d0:b0:14:cf",
691
                            "metadata": {},
692
                            "name": "s5-eth2",
693
                            "nni": True,
694
                            "port_number": 2,
695
                            "speed": 1250000000.0,
696
                            "switch": "00:00:00:00:00:00:00:05",
697
                            "type": "interface",
698
                            "uni": False
699
                        },
700
                        "id": "0e2b5d7bc858b9f38db11b69",
701
                        "metadata": {}
702
                    },
703
                    {
704
                        "active": False,
705
                        "enabled": True,
706
                        "endpoint_a": {
707
                            "active": False,
708
                            "enabled": True,
709
                            "id": "00:00:00:00:00:00:00:05:4",
710
                            "link": "53bd36ff55a5aa2029bd5d50",
711
                            "mac": "6e:c2:ea:c4:18:12",
712
                            "metadata": {},
713
                            "name": "s5-eth4",
714
                            "nni": True,
715
                            "port_number": 4,
716
                            "speed": 1250000000.0,
717
                            "switch": "00:00:00:00:00:00:00:05",
718
                            "type": "interface",
719
                            "uni": False
720
                        },
721
                        "endpoint_b": {
722
                            "active": False,
723
                            "enabled": True,
724
                            "id": "00:00:00:00:00:00:00:06:2",
725
                            "link": "53bd36ff55a5aa2029bd5d50",
726
                            "mac": "5a:25:7b:7c:0d:ac",
727
                            "metadata": {},
728
                            "name": "s6-eth2",
729
                            "nni": True,
730
                            "port_number": 2,
731
                            "speed": 1250000000.0,
732
                            "switch": "00:00:00:00:00:00:00:06",
733
                            "type": "interface",
734
                            "uni": False
735
                        },
736
                        "id": "53bd36ff55a5aa2029bd5d50",
737
                        "metadata": {}
738
                    }
739
                ],
740
                "priority": 0,
741
                "request_time": "2019-07-22T16:01:24",
742
                "start_date": "2019-07-22T16:01:24",
743
                "uni_a": {
744
                    "interface_id": "00:00:00:00:00:00:00:03:12",
745
                    "tag": {
746
                        "tag_type": 1,
747
                        "value": 321
748
                    }
749
                },
750
                "uni_z": {
751
                    "interface_id": "00:00:00:00:00:00:00:06:11",
752
                    "tag": {
753
                        "tag_type": 1,
754
                        "value": 612
755
                    }
756
                }
757
            }
758
        }
759
760
        expected_result = {
761
            '00:00:00:00:00:00:00:03:12':
762
                {'65c4582cc8f249c2a5947ef500c19e37'},
763
            '00:00:00:00:00:00:00:06:11':
764
                {'65c4582cc8f249c2a5947ef500c19e37'},
765
            '00:00:00:00:00:00:00:03:3':
766
                {'65c4582cc8f249c2a5947ef500c19e37'},
767
            '00:00:00:00:00:00:00:05:2':
768
                {'65c4582cc8f249c2a5947ef500c19e37'},
769
            '00:00:00:00:00:00:00:05:4':
770
                {'65c4582cc8f249c2a5947ef500c19e37'},
771
            '00:00:00:00:00:00:00:06:2':
772
                {'65c4582cc8f249c2a5947ef500c19e37'}
773
        }
774
        self.napp.load_circuits_by_interface(stored_circuits)
775
        # pylint: disable=protected-access
776
        self.assertEqual(self.napp._circuits_by_interface, expected_result)
777
778
    def test_redeploy_evc(self):
779
        """Test endpoint to redeploy an EVC."""
780
        evc1 = MagicMock()
781
        evc1.is_enabled.return_value = True
782
        self.napp.circuits = {'1': evc1,
783
                              '2': MagicMock()}
784
        api = self.get_app_test_client(self.napp)
785
        url = f'{self.server_name_url}/v2/evc/1/redeploy'
786
        response = api.patch(url)
787
        self.assertEqual(response.status_code, 202, response.data)
788
789
    def test_redeploy_evc_disabled(self):
790
        """Test endpoint to redeploy an EVC."""
791
        evc1 = MagicMock()
792
        evc1.is_enabled.return_value = False
793
        self.napp.circuits = {'1': evc1,
794
                              '2': MagicMock()}
795
        api = self.get_app_test_client(self.napp)
796
        url = f'{self.server_name_url}/v2/evc/1/redeploy'
797
        response = api.patch(url)
798
        self.assertEqual(response.status_code, 409, response.data)
799
800
    def test_redeploy_evc_deleted(self):
801
        """Test endpoint to redeploy an EVC."""
802
        evc1 = MagicMock()
803
        evc1.is_enabled.return_value = True
804
        self.napp.circuits = {'1': evc1,
805
                              '2': MagicMock()}
806
        api = self.get_app_test_client(self.napp)
807
        url = f'{self.server_name_url}/v2/evc/3/redeploy'
808
        response = api.patch(url)
809
        self.assertEqual(response.status_code, 404, response.data)
810
811
    def test_list_schedules__no_data(self):
812
        """Test list of schedules."""
813
        api = self.get_app_test_client(self.napp)
814
        url = f'{self.server_name_url}/v2/evc/schedule'
815
        response = api.get(url)
816
        self.assertEqual(response.status_code, 200, response.data)
817
        self.assertEqual(json.loads(response.data.decode()), {})
818
819
    @patch('napps.kytos.mef_eline.storehouse.StoreHouse.get_data')
820
    def test_list_schedules__no_data_stored(self, storehouse_data_mock):
821
        """Test if list circuits return all circuits stored."""
822
        circuits = {}
823
        storehouse_data_mock.return_value = circuits
824
825
        api = self.get_app_test_client(self.napp)
826
        url = f'{self.server_name_url}/v2/evc/schedule'
827
828
        response = api.get(url)
829
        expected_result = circuits
830
831
        self.assertEqual(response.status_code, 200, response.data)
832
        self.assertEqual(json.loads(response.data), expected_result)
833
834
    # pylint: disable=no-self-use
835
    def _add_storehouse_schedule_data(self, storehouse_data_mock):
836
        """Add schedule data to storehouse mock object."""
837
        circuits = {}
838
        payload_1 = {
839
            "id": "aa:aa:aa",
840
            "name": "my evc1",
841
            "uni_a": {
842
                "interface_id": "00:00:00:00:00:00:00:01:1",
843
                "tag": {
844
                    "tag_type": 1,
845
                    "value": 80
846
                }
847
            },
848
            "uni_z": {
849
                "interface_id": "00:00:00:00:00:00:00:02:2",
850
                "tag": {
851
                    "tag_type": 1,
852
                    "value": 1
853
                }
854
            },
855
            "circuit_scheduler": [
856
                {
857
                    "id": "1",
858
                    "frequency": "* * * * *",
859
                    "action": "create"
860
                },
861
                {
862
                    "id": "2",
863
                    "frequency": "1 * * * *",
864
                    "action": "remove"
865
                }
866
            ]
867
        }
868
        circuits.update({"aa:aa:aa": payload_1})
869
        payload_2 = {
870
            "id": "bb:bb:bb",
871
            "name": "my second evc2",
872
            "uni_a": {
873
                "interface_id": "00:00:00:00:00:00:00:01:2",
874
                "tag": {
875
                    "tag_type": 1,
876
                    "value": 90
877
                }
878
            },
879
            "uni_z": {
880
                "interface_id": "00:00:00:00:00:00:00:03:2",
881
                "tag": {
882
                    "tag_type": 1,
883
                    "value": 100
884
                }
885
            },
886
            "circuit_scheduler": [
887
                {
888
                    "id": "3",
889
                    "frequency": "1 * * * *",
890
                    "action": "create"
891
                },
892
                {
893
                    "id": "4",
894
                    "frequency": "2 * * * *",
895
                    "action": "remove"
896
                }
897
            ]
898
        }
899
        circuits.update({"bb:bb:bb": payload_2})
900
        payload_3 = {
901
            "id": "cc:cc:cc",
902
            "name": "my third evc3",
903
            "uni_a": {
904
                "interface_id": "00:00:00:00:00:00:00:03:1",
905
                "tag": {
906
                    "tag_type": 1,
907
                    "value": 90
908
                }
909
            },
910
            "uni_z": {
911
                "interface_id": "00:00:00:00:00:00:00:04:2",
912
                "tag": {
913
                    "tag_type": 1,
914
                    "value": 100
915
                }
916
            }
917
        }
918
        circuits.update({"cc:cc:cc": payload_3})
919
        # Add one circuit to the storehouse.
920
        storehouse_data_mock.return_value = circuits
921
922
    @patch('napps.kytos.mef_eline.storehouse.StoreHouse.get_data')
923
    def test_list_schedules_from_storehouse(self, storehouse_data_mock):
924
        """Test if list circuits return specific circuits stored."""
925
        self._add_storehouse_schedule_data(storehouse_data_mock)
926
927
        api = self.get_app_test_client(self.napp)
928
        url = f'{self.server_name_url}/v2/evc/schedule'
929
930
        # Call URL
931
        response = api.get(url)
932
        # Expected JSON data from response
933
        expected = [{'circuit_id': 'aa:aa:aa',
934
                     'schedule': {'action': 'create',
935
                                  'frequency': '* * * * *', 'id': '1'},
936
                     'schedule_id': '1'},
937
                    {'circuit_id': 'aa:aa:aa',
938
                     'schedule': {'action': 'remove',
939
                                  'frequency': '1 * * * *', 'id': '2'},
940
                     'schedule_id': '2'},
941
                    {'circuit_id': 'bb:bb:bb',
942
                     'schedule': {'action': 'create',
943
                                  'frequency': '1 * * * *', 'id': '3'},
944
                     'schedule_id': '3'},
945
                    {'circuit_id': 'bb:bb:bb',
946
                     'schedule': {'action': 'remove',
947
                                  'frequency': '2 * * * *', 'id': '4'},
948
                     'schedule_id': '4'}]
949
950
        self.assertEqual(response.status_code, 200, response.data)
951
        self.assertEqual(expected, json.loads(response.data))
952
953
    @patch('napps.kytos.mef_eline.storehouse.StoreHouse.get_data')
954
    def test_get_specific_schedule_from_storehouse(self, storehouse_data_mock):
955
        """Test get schedules from a circuit."""
956
        self._add_storehouse_schedule_data(storehouse_data_mock)
957
958
        requested_circuit_id = "bb:bb:bb"
959
        api = self.get_app_test_client(self.napp)
960
        url = f'{self.server_name_url}/v2/evc/{requested_circuit_id}'
961
962
        # Call URL
963
        response = api.get(url)
964
965
        # Expected JSON data from response
966
        expected = [{'action': 'create', 'frequency': '1 * * * *', 'id': '3'},
967
                    {'action': 'remove', 'frequency': '2 * * * *', 'id': '4'}]
968
969
        self.assertEqual(response.status_code, 200)
970
        self.assertEqual(expected,
971
                         json.loads(response.data)["circuit_scheduler"])
972
973
    def test_get_specific_schedules_from_storehouse_not_found(self):
974
        """Test get specific schedule ID that does not exist."""
975
        requested_id = "blah"
976
        api = self.get_app_test_client(self.napp)
977
        url = f'{self.server_name_url}/v2/evc/{requested_id}'
978
979
        # Call URL
980
        response = api.get(url)
981
982
        expected = 'circuit_id blah not found'
983
        # Assert response not found
984
        self.assertEqual(response.status_code, 404, response.data)
985
        self.assertEqual(expected, json.loads(response.data)['description'])
986
987
    def _uni_from_dict_side_effect(self, uni_dict):
988
        interface_id = uni_dict.get("interface_id")
989
        tag_dict = uni_dict.get("tag")
990
        interface = Interface(interface_id, "0", "switch")
991
        return UNI(interface, tag_dict)
992
993
    @patch('apscheduler.schedulers.background.BackgroundScheduler.add_job')
994
    @patch('napps.kytos.mef_eline.storehouse.StoreHouse.get_data')
995
    @patch('napps.kytos.mef_eline.scheduler.Scheduler.add')
996
    @patch('napps.kytos.mef_eline.main.Main._uni_from_dict')
997
    @patch('napps.kytos.mef_eline.storehouse.StoreHouse.save_evc')
998
    @patch('napps.kytos.mef_eline.main.EVC.as_dict')
999
    @patch('napps.kytos.mef_eline.models.evc.EVC._validate')
1000
    def test_create_schedule(self, *args):  # pylint: disable=too-many-locals
1001
        """Test create a circuit schedule."""
1002
        (validate_mock, evc_as_dict_mock, save_evc_mock,
1003
         uni_from_dict_mock, sched_add_mock, storehouse_data_mock,
1004
         scheduler_add_job_mock) = args
1005
1006
        validate_mock.return_value = True
1007
        save_evc_mock.return_value = True
1008
        uni_from_dict_mock.side_effect = self._uni_from_dict_side_effect
1009
        evc_as_dict_mock.return_value = {}
1010
        sched_add_mock.return_value = True
1011
        storehouse_data_mock.return_value = {}
1012
1013
        self._add_storehouse_schedule_data(storehouse_data_mock)
1014
1015
        requested_id = "bb:bb:bb"
1016
        api = self.get_app_test_client(self.napp)
1017
        url = f'{self.server_name_url}/v2/evc/schedule/'
1018
1019
        payload = {
1020
              "circuit_id": requested_id,
1021
              "schedule": {
1022
                "frequency": "1 * * * *",
1023
                "action": "create"
1024
              }
1025
            }
1026
1027
        # Call URL
1028
        response = api.post(url, data=json.dumps(payload),
1029
                            content_type='application/json')
1030
1031
        response_json = json.loads(response.data)
1032
1033
        self.assertEqual(response.status_code, 201, response.data)
1034
        scheduler_add_job_mock.assert_called_once()
1035
        save_evc_mock.assert_called_once()
1036
        self.assertEqual(payload["schedule"]["frequency"],
1037
                         response_json["frequency"])
1038
        self.assertEqual(payload["schedule"]["action"],
1039
                         response_json["action"])
1040
        self.assertIsNotNone(response_json["id"])
1041
1042
    @patch('apscheduler.schedulers.background.BackgroundScheduler.remove_job')
1043
    @patch('napps.kytos.mef_eline.storehouse.StoreHouse.get_data')
1044
    @patch('napps.kytos.mef_eline.scheduler.Scheduler.add')
1045
    @patch('napps.kytos.mef_eline.main.Main._uni_from_dict')
1046
    @patch('napps.kytos.mef_eline.storehouse.StoreHouse.save_evc')
1047
    @patch('napps.kytos.mef_eline.main.EVC.as_dict')
1048
    @patch('napps.kytos.mef_eline.models.evc.EVC._validate')
1049
    def test_update_schedule(self, *args):  # pylint: disable=too-many-locals
1050
        """Test create a circuit schedule."""
1051
        (validate_mock, evc_as_dict_mock, save_evc_mock,
1052
         uni_from_dict_mock, sched_add_mock, storehouse_data_mock,
1053
         scheduler_remove_job_mock) = args
1054
1055
        storehouse_payload_1 = {
1056
            "aa:aa:aa": {
1057
                "id": "aa:aa:aa",
1058
                "name": "my evc1",
1059
                "uni_a": {
1060
                    "interface_id": "00:00:00:00:00:00:00:01:1",
1061
                    "tag": {
1062
                        "tag_type": 1,
1063
                        "value": 80
1064
                    }
1065
                },
1066
                "uni_z": {
1067
                    "interface_id": "00:00:00:00:00:00:00:02:2",
1068
                    "tag": {
1069
                        "tag_type": 1,
1070
                        "value": 1
1071
                    }
1072
                },
1073
                "circuit_scheduler": [{
1074
                    "id": "1",
1075
                    "frequency": "* * * * *",
1076
                    "action": "create"
1077
                }
1078
                ]
1079
            }
1080
        }
1081
1082
        validate_mock.return_value = True
1083
        save_evc_mock.return_value = True
1084
        sched_add_mock.return_value = True
1085
        uni_from_dict_mock.side_effect = ['uni_a', 'uni_z']
1086
        evc_as_dict_mock.return_value = {}
1087
        storehouse_data_mock.return_value = storehouse_payload_1
1088
        scheduler_remove_job_mock.return_value = True
1089
1090
        requested_schedule_id = "1"
1091
        api = self.get_app_test_client(self.napp)
1092
        url = f'{self.server_name_url}/v2/evc/schedule/{requested_schedule_id}'
1093
1094
        payload = {
1095
            "frequency": "*/1 * * * *",
1096
            "action": "create"
1097
        }
1098
1099
        # Call URL
1100
        response = api.patch(url, data=json.dumps(payload),
1101
                             content_type='application/json')
1102
1103
        response_json = json.loads(response.data)
1104
1105
        self.assertEqual(response.status_code, 200, response.data)
1106
        scheduler_remove_job_mock.assert_called_once()
1107
        save_evc_mock.assert_called_once()
1108
        self.assertEqual(payload["frequency"], response_json["frequency"])
1109
        self.assertEqual(payload["action"], response_json["action"])
1110
        self.assertIsNotNone(response_json["id"])
1111
1112
    @patch('napps.kytos.mef_eline.storehouse.StoreHouse.get_data')
1113
    @patch('napps.kytos.mef_eline.scheduler.Scheduler.add')
1114
    @patch('napps.kytos.mef_eline.main.Main._uni_from_dict')
1115
    @patch('napps.kytos.mef_eline.main.EVC.as_dict')
1116
    @patch('napps.kytos.mef_eline.models.evc.EVC._validate')
1117
    def test_update_schedule_archived(self, *args):
1118
        """Test create a circuit schedule."""
1119
        # pylint: disable=too-many-locals
1120
        (validate_mock, evc_as_dict_mock,
1121
         uni_from_dict_mock, sched_add_mock, storehouse_data_mock) = args
1122
1123
        storehouse_payload_1 = {
1124
            "aa:aa:aa": {
1125
                "id": "aa:aa:aa",
1126
                "name": "my evc1",
1127
                "archived": True,
1128
                "circuit_scheduler": [{
1129
                    "id": "1",
1130
                    "frequency": "* * * * *",
1131
                    "action": "create"
1132
                }
1133
                ]
1134
            }
1135
        }
1136
1137
        validate_mock.return_value = True
1138
        sched_add_mock.return_value = True
1139
        uni_from_dict_mock.side_effect = ['uni_a', 'uni_z']
1140
        evc_as_dict_mock.return_value = {}
1141
        storehouse_data_mock.return_value = storehouse_payload_1
1142
1143
        requested_schedule_id = "1"
1144
        api = self.get_app_test_client(self.napp)
1145
        url = f'{self.server_name_url}/v2/evc/schedule/{requested_schedule_id}'
1146
1147
        payload = {
1148
            "frequency": "*/1 * * * *",
1149
            "action": "create"
1150
        }
1151
1152
        # Call URL
1153
        response = api.patch(url, data=json.dumps(payload),
1154
                             content_type='application/json')
1155
1156
        self.assertEqual(response.status_code, 403, response.data)
1157
1158
    @patch('apscheduler.schedulers.background.BackgroundScheduler.remove_job')
1159
    @patch('napps.kytos.mef_eline.storehouse.StoreHouse.get_data')
1160
    @patch('napps.kytos.mef_eline.main.Main._uni_from_dict')
1161
    @patch('napps.kytos.mef_eline.storehouse.StoreHouse.save_evc')
1162
    @patch('napps.kytos.mef_eline.main.EVC.as_dict')
1163
    @patch('napps.kytos.mef_eline.models.evc.EVC._validate')
1164
    def test_delete_schedule(self, *args):
1165
        """Test create a circuit schedule."""
1166
        (validate_mock, evc_as_dict_mock, save_evc_mock,
1167
         uni_from_dict_mock, storehouse_data_mock,
1168
         scheduler_remove_job_mock) = args
1169
1170
        storehouse_payload_1 = {
1171
            "2": {
1172
                "id": "2",
1173
                "name": "my evc1",
1174
                "uni_a": {
1175
                    "interface_id": "00:00:00:00:00:00:00:01:1",
1176
                    "tag": {
1177
                        "tag_type": 1,
1178
                        "value": 80
1179
                    }
1180
                },
1181
                "uni_z": {
1182
                    "interface_id": "00:00:00:00:00:00:00:02:2",
1183
                    "tag": {
1184
                        "tag_type": 1,
1185
                        "value": 1
1186
                    }
1187
                },
1188
                "circuit_scheduler": [{
1189
                    "id": "1",
1190
                    "frequency": "* * * * *",
1191
                    "action": "create"
1192
                }]
1193
            }
1194
        }
1195
        validate_mock.return_value = True
1196
        save_evc_mock.return_value = True
1197
        uni_from_dict_mock.side_effect = ['uni_a', 'uni_z']
1198
        evc_as_dict_mock.return_value = {}
1199
        storehouse_data_mock.return_value = storehouse_payload_1
1200
        scheduler_remove_job_mock.return_value = True
1201
1202
        requested_schedule_id = "1"
1203
        api = self.get_app_test_client(self.napp)
1204
        url = f'{self.server_name_url}/v2/evc/schedule/{requested_schedule_id}'
1205
1206
        # Call URL
1207
        response = api.delete(url)
1208
1209
        self.assertEqual(response.status_code, 200, response.data)
1210
        scheduler_remove_job_mock.assert_called_once()
1211
        save_evc_mock.assert_called_once()
1212
        self.assertIn("Schedule removed", f"{response.data}")
1213
1214
    @patch('napps.kytos.mef_eline.storehouse.StoreHouse.get_data')
1215
    @patch('napps.kytos.mef_eline.main.Main._uni_from_dict')
1216
    @patch('napps.kytos.mef_eline.main.EVC.as_dict')
1217
    @patch('napps.kytos.mef_eline.models.evc.EVC._validate')
1218
    def test_delete_schedule_archived(self, *args):
1219
        """Test create a circuit schedule."""
1220
        (validate_mock, evc_as_dict_mock,
1221
         uni_from_dict_mock, storehouse_data_mock) = args
1222
1223
        storehouse_payload_1 = {
1224
            "2": {
1225
                "id": "2",
1226
                "name": "my evc1",
1227
                "archived": True,
1228
                "circuit_scheduler": [{
1229
                    "id": "1",
1230
                    "frequency": "* * * * *",
1231
                    "action": "create"
1232
                }]
1233
            }
1234
        }
1235
1236
        validate_mock.return_value = True
1237
        uni_from_dict_mock.side_effect = ['uni_a', 'uni_z']
1238
        evc_as_dict_mock.return_value = {}
1239
        storehouse_data_mock.return_value = storehouse_payload_1
1240
1241
        requested_schedule_id = "1"
1242
        api = self.get_app_test_client(self.napp)
1243
        url = f'{self.server_name_url}/v2/evc/schedule/{requested_schedule_id}'
1244
1245
        # Call URL
1246
        response = api.delete(url)
1247
1248
        self.assertEqual(response.status_code, 403, response.data)
1249
1250
    @patch('requests.post')
1251
    @patch('napps.kytos.mef_eline.scheduler.Scheduler.add')
1252
    @patch('napps.kytos.mef_eline.storehouse.StoreHouse.save_evc')
1253
    @patch('napps.kytos.mef_eline.models.evc.EVC._validate')
1254
    @patch('kytos.core.Controller.get_interface_by_id')
1255
    @patch('napps.kytos.mef_eline.models.path.Path.is_valid')
1256
    @patch('napps.kytos.mef_eline.models.evc.EVCDeploy.deploy')
1257
    @patch('napps.kytos.mef_eline.main.Main._uni_from_dict')
1258
    @patch('napps.kytos.mef_eline.main.EVC.as_dict')
1259
    def test_update_circuit(self, *args):
1260
        """Test update a circuit circuit."""
1261
        # pylint: disable=too-many-locals
1262
        (evc_as_dict_mock, uni_from_dict_mock, evc_deploy,
1263
         *mocks, requests_mock) = args
1264
        response = MagicMock()
1265
        response.status_code = 201
1266
        requests_mock.return_value = response
1267
1268
        for mock in mocks:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable mocks does not seem to be defined.
Loading history...
1269
            mock.return_value = True
1270
        unis = [get_uni_mocked(switch_dpid='00:00:00:00:00:00:00:01'),
1271
                get_uni_mocked(switch_dpid='00:00:00:00:00:00:00:02')]
1272
        uni_from_dict_mock.side_effect = 2 * unis
1273
1274
        api = self.get_app_test_client(self.napp)
1275
        payloads = [
1276
            {
1277
                "name": "my evc1",
1278
                "active": True,
1279
                "uni_a": {
1280
                    "interface_id": "00:00:00:00:00:00:00:01:1",
1281
                    "tag": {
1282
                        "tag_type": 1,
1283
                        "value": 80
1284
                    }
1285
                },
1286
                "uni_z": {
1287
                    "interface_id": "00:00:00:00:00:00:00:02:2",
1288
                    "tag": {
1289
                        "tag_type": 1,
1290
                        "value": 1
1291
                    }
1292
                },
1293
                "dynamic_backup_path": True
1294
            },
1295
            {
1296
                "primary_path": [
1297
                    {
1298
                        "endpoint_a": {"id": "00:00:00:00:00:00:00:01:1"},
1299
                        "endpoint_b": {"id": "00:00:00:00:00:00:00:02:2"}
1300
                    }
1301
                ]
1302
            },
1303
            {
1304
                "priority": 3
1305
            },
1306
            {
1307
                "enable": True
1308
            }
1309
        ]
1310
1311
        evc_as_dict_mock.return_value = payloads[0]
1312
        response = api.post(f'{self.server_name_url}/v2/evc/',
1313
                            data=json.dumps(payloads[0]),
1314
                            content_type='application/json')
1315
        self.assertEqual(201, response.status_code)
1316
1317
        evc_deploy.reset_mock()
1318
        evc_as_dict_mock.return_value = payloads[1]
1319
        current_data = json.loads(response.data)
1320
        circuit_id = current_data['circuit_id']
1321
        response = api.patch(f'{self.server_name_url}/v2/evc/{circuit_id}',
1322
                             data=json.dumps(payloads[1]),
1323
                             content_type='application/json')
1324
        evc_deploy.assert_called_once()
1325
        self.assertEqual(200, response.status_code)
1326
1327
        evc_deploy.reset_mock()
1328
        evc_as_dict_mock.return_value = payloads[2]
1329
        response = api.patch(f'{self.server_name_url}/v2/evc/{circuit_id}',
1330
                             data=json.dumps(payloads[2]),
1331
                             content_type='application/json')
1332
        evc_deploy.assert_not_called()
1333
        self.assertEqual(200, response.status_code)
1334
1335
        evc_deploy.reset_mock()
1336
        response = api.patch(f'{self.server_name_url}/v2/evc/{circuit_id}',
1337
                             data='{"priority":5,}',
1338
                             content_type='application/json')
1339
        evc_deploy.assert_not_called()
1340
        self.assertEqual(400, response.status_code)
1341
1342
        evc_deploy.reset_mock()
1343
        response = api.patch(f'{self.server_name_url}/v2/evc/{circuit_id}',
1344
                             data=json.dumps(payloads[3]),
1345
                             content_type='application/json')
1346
        evc_deploy.assert_called_once()
1347
        self.assertEqual(200, response.status_code)
1348
1349
        response = api.patch(f'{self.server_name_url}/v2/evc/1234',
1350
                             data=json.dumps(payloads[1]),
1351
                             content_type='application/json')
1352
        current_data = json.loads(response.data)
1353
        expected_data = 'circuit_id 1234 not found'
1354
        self.assertEqual(current_data['description'], expected_data)
1355
        self.assertEqual(404, response.status_code)
1356
1357
        api.delete(f'{self.server_name_url}/v2/evc/{circuit_id}')
1358
        evc_deploy.reset_mock()
1359
        response = api.patch(f'{self.server_name_url}/v2/evc/{circuit_id}',
1360
                             data=json.dumps(payloads[1]),
1361
                             content_type='application/json')
1362
        evc_deploy.assert_not_called()
1363
        self.assertEqual(405, response.status_code)
1364
1365 View Code Duplication
    @patch('napps.kytos.mef_eline.models.evc.EVC.deploy')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1366
    @patch('napps.kytos.mef_eline.scheduler.Scheduler.add')
1367
    @patch('napps.kytos.mef_eline.main.Main._uni_from_dict')
1368
    @patch('napps.kytos.mef_eline.storehouse.StoreHouse.save_evc')
1369
    @patch('napps.kytos.mef_eline.models.evc.EVC._validate')
1370
    @patch('napps.kytos.mef_eline.main.EVC.as_dict')
1371
    def test_update_circuit_invalid_json(self, *args):
1372
        """Test update a circuit circuit."""
1373
        # pylint: disable=too-many-locals
1374
        (evc_as_dict_mock, validate_mock, save_evc_mock,
1375
         uni_from_dict_mock, sched_add_mock, evc_deploy_mock) = args
1376
1377
        validate_mock.return_value = True
1378
        save_evc_mock.return_value = True
1379
        sched_add_mock.return_value = True
1380
        evc_deploy_mock.return_value = True
1381
        uni1 = create_autospec(UNI)
1382
        uni2 = create_autospec(UNI)
1383
        uni1.interface = create_autospec(Interface)
1384
        uni2.interface = create_autospec(Interface)
1385
        uni1.interface.switch = '00:00:00:00:00:00:00:01'
1386
        uni2.interface.switch = '00:00:00:00:00:00:00:02'
1387
        uni_from_dict_mock.side_effect = [uni1, uni2, uni1, uni2]
1388
1389
        api = self.get_app_test_client(self.napp)
1390
        payload1 = {
1391
            "name": "my evc1",
1392
            "uni_a": {
1393
                "interface_id": "00:00:00:00:00:00:00:01:1",
1394
                "tag": {
1395
                    "tag_type": 1,
1396
                    "value": 80
1397
                }
1398
            },
1399
            "uni_z": {
1400
                "interface_id": "00:00:00:00:00:00:00:02:2",
1401
                "tag": {
1402
                    "tag_type": 1,
1403
                    "value": 1
1404
                }
1405
            },
1406
            "dynamic_backup_path": True
1407
        }
1408
1409
        payload2 = {
1410
            "dynamic_backup_path": False,
1411
        }
1412
1413
        evc_as_dict_mock.return_value = payload1
1414
        response = api.post(f'{self.server_name_url}/v2/evc/',
1415
                            data=json.dumps(payload1),
1416
                            content_type='application/json')
1417
        self.assertEqual(201, response.status_code)
1418
1419
        evc_as_dict_mock.return_value = payload2
1420
        current_data = json.loads(response.data)
1421
        circuit_id = current_data['circuit_id']
1422
        response = api.patch(f'{self.server_name_url}/v2/evc/{circuit_id}',
1423
                             data=payload2,
1424
                             content_type='application/json')
1425
        current_data = json.loads(response.data)
1426
        expected_data = 'The request body is not a well-formed JSON.'
1427
        self.assertEqual(current_data['description'], expected_data)
1428
        self.assertEqual(400, response.status_code)
1429
1430
    @patch('napps.kytos.mef_eline.models.evc.EVC.deploy')
1431
    @patch('napps.kytos.mef_eline.scheduler.Scheduler.add')
1432
    @patch('napps.kytos.mef_eline.main.Main._uni_from_dict')
1433
    @patch('napps.kytos.mef_eline.main.Main._link_from_dict')
1434
    @patch('napps.kytos.mef_eline.storehouse.StoreHouse.save_evc')
1435
    @patch('napps.kytos.mef_eline.models.evc.EVC._validate')
1436
    @patch('napps.kytos.mef_eline.main.EVC.as_dict')
1437
    @patch('napps.kytos.mef_eline.models.path.Path.is_valid')
1438
    def test_update_circuit_invalid_path(self, *args):
1439
        """Test update a circuit circuit."""
1440
        # pylint: disable=too-many-locals
1441
        (is_valid_mock, evc_as_dict_mock, validate_mock, save_evc_mock,
1442
         link_from_dict_mock, uni_from_dict_mock, sched_add_mock,
1443
         evc_deploy_mock) = args
1444
1445
        is_valid_mock.side_effect = InvalidPath('error')
1446
        validate_mock.return_value = True
1447
        save_evc_mock.return_value = True
1448
        sched_add_mock.return_value = True
1449
        evc_deploy_mock.return_value = True
1450
        link_from_dict_mock.return_value = 1
1451
        uni1 = create_autospec(UNI)
1452
        uni2 = create_autospec(UNI)
1453
        uni1.interface = create_autospec(Interface)
1454
        uni2.interface = create_autospec(Interface)
1455
        uni1.interface.switch = '00:00:00:00:00:00:00:01'
1456
        uni2.interface.switch = '00:00:00:00:00:00:00:02'
1457
        uni_from_dict_mock.side_effect = [uni1, uni2, uni1, uni2]
1458
1459
        api = self.get_app_test_client(self.napp)
1460
        payload1 = {
1461
            "name": "my evc1",
1462
            "uni_a": {
1463
                "interface_id": "00:00:00:00:00:00:00:01:1",
1464
                "tag": {
1465
                    "tag_type": 1,
1466
                    "value": 80
1467
                }
1468
            },
1469
            "uni_z": {
1470
                "interface_id": "00:00:00:00:00:00:00:02:2",
1471
                "tag": {
1472
                    "tag_type": 1,
1473
                    "value": 1
1474
                }
1475
            },
1476
            "dynamic_backup_path": True
1477
        }
1478
1479
        payload2 = {
1480
            "primary_path": [
1481
                    {
1482
                        "endpoint_a": {"id": "00:00:00:00:00:00:00:01:1"},
1483
                        "endpoint_b": {"id": "00:00:00:00:00:00:00:02:2"}
1484
                    }
1485
                ]
1486
        }
1487
1488
        evc_as_dict_mock.return_value = payload1
1489
        response = api.post(f'{self.server_name_url}/v2/evc/',
1490
                            data=json.dumps(payload1),
1491
                            content_type='application/json')
1492
        self.assertEqual(201, response.status_code)
1493
1494
        evc_as_dict_mock.return_value = payload2
1495
        current_data = json.loads(response.data)
1496
        circuit_id = current_data['circuit_id']
1497
        response = api.patch(f'{self.server_name_url}/v2/evc/{circuit_id}',
1498
                             data=json.dumps(payload2),
1499
                             content_type='application/json')
1500
        current_data = json.loads(response.data)
1501
        expected_data = 'primary_path is not a valid path: error'
1502
        self.assertEqual(400, response.status_code)
1503
        self.assertEqual(current_data['description'], expected_data)
1504
1505 View Code Duplication
    @patch('napps.kytos.mef_eline.models.evc.EVC.deploy')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1506
    @patch('napps.kytos.mef_eline.scheduler.Scheduler.add')
1507
    @patch('napps.kytos.mef_eline.main.Main._uni_from_dict')
1508
    @patch('napps.kytos.mef_eline.storehouse.StoreHouse.save_evc')
1509
    @patch('napps.kytos.mef_eline.models.evc.EVC._validate')
1510
    @patch('napps.kytos.mef_eline.main.EVC.as_dict')
1511
    def test_update_evc_no_json_mime(self, *args):
1512
        """Test update a circuit with wrong mimetype."""
1513
        # pylint: disable=too-many-locals
1514
        (evc_as_dict_mock, validate_mock, save_evc_mock,
1515
         uni_from_dict_mock, sched_add_mock, evc_deploy_mock) = args
1516
1517
        validate_mock.return_value = True
1518
        save_evc_mock.return_value = True
1519
        sched_add_mock.return_value = True
1520
        evc_deploy_mock.return_value = True
1521
        uni1 = create_autospec(UNI)
1522
        uni2 = create_autospec(UNI)
1523
        uni1.interface = create_autospec(Interface)
1524
        uni2.interface = create_autospec(Interface)
1525
        uni1.interface.switch = '00:00:00:00:00:00:00:01'
1526
        uni2.interface.switch = '00:00:00:00:00:00:00:02'
1527
        uni_from_dict_mock.side_effect = [uni1, uni2, uni1, uni2]
1528
1529
        api = self.get_app_test_client(self.napp)
1530
        payload1 = {
1531
            "name": "my evc1",
1532
            "uni_a": {
1533
                "interface_id": "00:00:00:00:00:00:00:01:1",
1534
                "tag": {
1535
                    "tag_type": 1,
1536
                    "value": 80
1537
                }
1538
            },
1539
            "uni_z": {
1540
                "interface_id": "00:00:00:00:00:00:00:02:2",
1541
                "tag": {
1542
                    "tag_type": 1,
1543
                    "value": 1
1544
                }
1545
            },
1546
            "dynamic_backup_path": True
1547
        }
1548
1549
        payload2 = {
1550
            "dynamic_backup_path": False
1551
        }
1552
1553
        evc_as_dict_mock.return_value = payload1
1554
        response = api.post(f'{self.server_name_url}/v2/evc/',
1555
                            data=json.dumps(payload1),
1556
                            content_type='application/json')
1557
        self.assertEqual(201, response.status_code)
1558
1559
        evc_as_dict_mock.return_value = payload2
1560
        current_data = json.loads(response.data)
1561
        circuit_id = current_data['circuit_id']
1562
        response = api.patch(f'{self.server_name_url}/v2/evc/{circuit_id}',
1563
                             data=payload2)
1564
        current_data = json.loads(response.data)
1565
        expected_data = 'The request body mimetype is not application/json.'
1566
        self.assertEqual(current_data['description'], expected_data)
1567
        self.assertEqual(415, response.status_code)
1568
1569
    def test_delete_no_evc(self):
1570
        """Test delete when EVC does not exist."""
1571
        api = self.get_app_test_client(self.napp)
1572
        response = api.delete(f'{self.server_name_url}/v2/evc/123')
1573
        current_data = json.loads(response.data)
1574
        expected_data = 'circuit_id 123 not found'
1575
        self.assertEqual(current_data['description'], expected_data)
1576
        self.assertEqual(404, response.status_code)
1577
1578
    def test_handle_link_up(self):
1579
        """Test handle_link_up method."""
1580
        evc_mock = create_autospec(EVC)
1581
        evc_mock.is_enabled = MagicMock(side_effect=[True, False, True])
1582
        evc_mock.lock = MagicMock()
1583
        type(evc_mock).archived = \
1584
            PropertyMock(side_effect=[True, False, False])
1585
        evcs = [evc_mock, evc_mock, evc_mock]
1586
        event = KytosEvent(name='test', content={'link': 'abc'})
1587
        self.napp.circuits = dict(zip(['1', '2', '3'], evcs))
1588
        self.napp.handle_link_up(event)
1589
        evc_mock.handle_link_up.assert_called_once_with('abc')
1590
1591
    def test_handle_link_down(self):
1592
        """Test handle_link_down method."""
1593
        evc_mock = create_autospec(EVC)
1594
        evc_mock.is_affected_by_link = \
1595
            MagicMock(side_effect=[True, False, True])
1596
        evc_mock.lock = MagicMock()
1597
        evc_mock.handle_link_down = MagicMock(side_effect=[True, True])
1598
        evcs = [evc_mock, evc_mock, evc_mock]
1599
        event = KytosEvent(name='test', content={'link': 'abc'})
1600
        self.napp.circuits = dict(zip(['1', '2', '3'], evcs))
1601
        self.napp.handle_link_down(event)
1602
        evc_mock.handle_link_down.assert_has_calls([call(), call()])
1603
1604
    def test_add_metadata(self):
1605
        """Test method to add metadata"""
1606
        evc_mock = create_autospec(EVC)
1607
        evc_mock.metadata = {}
1608
        evc_mock.id = 1234
1609
        self.napp.circuits = {'1234': evc_mock}
1610
1611
        api = self.get_app_test_client(self.napp)
1612
        payload = {'metadata1': 1, 'metadata2': 2}
1613
        response = api.post(f'{self.server_name_url}/v2/evc/1234/metadata',
1614
                            data=json.dumps(payload),
1615
                            content_type='application/json')
1616
1617
        self.assertEqual(response.status_code, 201)
1618
        evc_mock.extend_metadata.assert_called_with(payload)
1619