Passed
Pull Request — master (#101)
by Italo Valcy
14:49 queued 12:13
created

build.tests.unit.test_main   C

Complexity

Total Complexity 52

Size/Duplication

Total Lines 1637
Duplicated Lines 10.87 %

Importance

Changes 0
Metric Value
eloc 1200
dl 178
loc 1637
rs 6.24
c 0
b 0
f 0
wmc 52

46 Methods

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