Passed
Pull Request — master (#298)
by
unknown
07:15
created

TestMain.test_add_bulk_metadata()   A

Complexity

Conditions 1

Size

Total Lines 25
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 22
nop 1
dl 0
loc 25
rs 9.352
c 0
b 0
f 0
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 kytos.lib.helpers import get_controller_mock
9
from napps.kytos.mef_eline.exceptions import InvalidPath
10
from napps.kytos.mef_eline.models import EVC
11
from napps.kytos.mef_eline.tests.helpers import get_uni_mocked
12
13
14
# pylint: disable=too-many-public-methods, too-many-lines
15
class TestMain(TestCase):
16
    """Test the Main class."""
17
18
    def setUp(self):
19
        """Execute steps before each tests.
20
21
        Set the server_name_url_url from kytos/mef_eline
22
        """
23
        self.server_name_url = "http://localhost:8181/api/kytos/mef_eline"
24
25
        # The decorator run_on_thread is patched, so methods that listen
26
        # for events do not run on threads while tested.
27
        # Decorators have to be patched before the methods that are
28
        # decorated with them are imported.
29
        patch("kytos.core.helpers.run_on_thread", lambda x: x).start()
30
        # pylint: disable=import-outside-toplevel
31
        from napps.kytos.mef_eline.main import Main
32
        Main.get_eline_controller = MagicMock()
33
        self.addCleanup(patch.stopall)
34
        self.napp = Main(get_controller_mock())
35
36
    def test_get_event_listeners(self):
37
        """Verify all event listeners registered."""
38
        expected_events = [
39
            "kytos/core.shutdown",
40
            "kytos/core.shutdown.kytos/mef_eline",
41
            "kytos/topology.link_up",
42
            "kytos/topology.link_down",
43
        ]
44
        actual_events = self.napp.listeners()
45
46
        for _event in expected_events:
47
            self.assertIn(_event, actual_events, _event)
48
49
    def test_verify_api_urls(self):
50
        """Verify all APIs registered."""
51
        expected_urls = [
52
            ({}, {"POST", "OPTIONS"}, "/api/kytos/mef_eline/v2/evc/"),
53
            ({}, {"OPTIONS", "HEAD", "GET"}, "/api/kytos/mef_eline/v2/evc/"),
54
            (
55
                {"circuit_id": "[circuit_id]"},
56
                {"OPTIONS", "DELETE"},
57
                "/api/kytos/mef_eline/v2/evc/<circuit_id>",
58
            ),
59
            (
60
                {"circuit_id": "[circuit_id]"},
61
                {"OPTIONS", "HEAD", "GET"},
62
                "/api/kytos/mef_eline/v2/evc/<circuit_id>",
63
            ),
64
            (
65
                {"circuit_id": "[circuit_id]"},
66
                {"OPTIONS", "PATCH"},
67
                "/api/kytos/mef_eline/v2/evc/<circuit_id>",
68
            ),
69
            (
70
                {"circuit_id": "[circuit_id]"},
71
                {"OPTIONS", "HEAD", "GET"},
72
                "/api/kytos/mef_eline/v2/evc/<circuit_id>/metadata",
73
            ),
74
            (
75
                {"circuit_id": "[circuit_id]"},
76
                {"OPTIONS", "POST"},
77
                "/api/kytos/mef_eline/v2/evc/<circuit_id>/metadata",
78
            ),
79
            (
80
                {"circuit_id": "[circuit_id]", "key": "[key]"},
81
                {"OPTIONS", "DELETE"},
82
                "/api/kytos/mef_eline/v2/evc/<circuit_id>/metadata/<key>",
83
            ),
84
            (
85
                {"circuit_id": "[circuit_id]"},
86
                {"OPTIONS", "PATCH"},
87
                "/api/kytos/mef_eline/v2/evc/<circuit_id>/redeploy",
88
            ),
89
            (
90
                {},
91
                {"OPTIONS", "DELETE"},
92
                "/api/kytos/mef_eline/v2/evc/metadata",
93
            ),
94
            (
95
                {},
96
                {"OPTIONS", "POST"},
97
                "/api/kytos/mef_eline/v2/evc/metadata",
98
            ),
99
            (
100
                {},
101
                {"OPTIONS", "GET", "HEAD"},
102
                "/api/kytos/mef_eline/v2/evc/schedule",
103
            ),
104
            ({}, {"POST", "OPTIONS"}, "/api/kytos/mef_eline/v2/evc/schedule/"),
105
            (
106
                {"schedule_id": "[schedule_id]"},
107
                {"OPTIONS", "DELETE"},
108
                "/api/kytos/mef_eline/v2/evc/schedule/<schedule_id>",
109
            ),
110
            (
111
                {"schedule_id": "[schedule_id]"},
112
                {"OPTIONS", "PATCH"},
113
                "/api/kytos/mef_eline/v2/evc/schedule/<schedule_id>",
114
            ),
115
        ]
116
        urls = self.get_napp_urls(self.napp)
117
        self.assertEqual(len(expected_urls), len(urls))
118
119
    @patch('napps.kytos.mef_eline.main.log')
120
    @patch('napps.kytos.mef_eline.main.Main.execute_consistency')
121
    def test_execute(self, mock_execute_consistency, mock_log):
122
        """Test execute."""
123
        self.napp.execution_rounds = 0
124
        self.napp.execute()
125
        mock_execute_consistency.assert_called()
126
        self.assertEqual(mock_log.debug.call_count, 2)
127
128
        # Test locked should return
129
        mock_execute_consistency.call_count = 0
130
        mock_log.info.call_count = 0
131
        # pylint: disable=protected-access
132
        self.napp._lock = MagicMock()
133
        self.napp._lock.locked.return_value = True
134
        # pylint: enable=protected-access
135
        self.napp.execute()
136
        mock_execute_consistency.assert_not_called()
137
        mock_log.info.assert_not_called()
138
139
    @patch('napps.kytos.mef_eline.main.settings')
140
    @patch('napps.kytos.mef_eline.main.Main._load_evc')
141
    @patch("napps.kytos.mef_eline.controllers.ELineController.upsert_evc")
142
    @patch("napps.kytos.mef_eline.models.evc.EVCDeploy.check_list_traces")
143
    def test_execute_consistency(self, mock_check_list_traces, *args):
144
        """Test execute_consistency."""
145
        (mongo_controller_upsert_mock, mock_load_evc, mock_settings) = args
146
147
        stored_circuits = {'1': {'name': 'circuit_1'},
148
                           '2': {'name': 'circuit_2'},
149
                           '3': {'name': 'circuit_3'}}
150
        mongo_controller_upsert_mock.return_value = True
151
        self.napp.mongo_controller.get_circuits.return_value = {
152
            "circuits": stored_circuits
153
        }
154
155
        mock_settings.WAIT_FOR_OLD_PATH = -1
156
        evc1 = MagicMock(id=1, service_level=0, creation_time=1)
157
        evc1.is_enabled.return_value = True
158
        evc1.is_active.return_value = False
159
        evc1.lock.locked.return_value = False
160
        evc1.has_recent_removed_flow.return_value = False
161
        evc1.is_recent_updated.return_value = False
162
        evc1.execution_rounds = 0
163
        evc2 = MagicMock(id=2, service_level=7, creation_time=1)
164
        evc2.is_enabled.return_value = True
165
        evc2.is_active.return_value = False
166
        evc2.lock.locked.return_value = False
167
        evc2.has_recent_removed_flow.return_value = False
168
        evc2.is_recent_updated.return_value = False
169
        evc2.execution_rounds = 0
170
        self.napp.circuits = {'1': evc1, '2': evc2}
171
        assert self.napp.get_evcs_by_svc_level() == [evc2, evc1]
172
173
        mock_check_list_traces.return_value = {
174
                                                1: True,
175
                                                2: False
176
                                            }
177
178
        self.napp.execute_consistency()
179
        self.assertEqual(evc1.activate.call_count, 1)
180
        self.assertEqual(evc1.sync.call_count, 1)
181
        self.assertEqual(evc2.deploy.call_count, 1)
182
        mock_load_evc.assert_called_with(stored_circuits['3'])
183
184
    @patch('napps.kytos.mef_eline.main.settings')
185
    @patch('napps.kytos.mef_eline.main.Main._load_evc')
186
    @patch("napps.kytos.mef_eline.controllers.ELineController.upsert_evc")
187
    @patch("napps.kytos.mef_eline.models.evc.EVCDeploy.check_list_traces")
188
    def test_execute_consistency_wait_for(self, mock_check_list_traces, *args):
189
        """Test execute and wait for setting."""
190
        (mongo_controller_upsert_mock, _, mock_settings) = args
191
192
        stored_circuits = {'1': {'name': 'circuit_1'}}
193
        mongo_controller_upsert_mock.return_value = True
194
        self.napp.mongo_controller.get_circuits.return_value = {
195
            "circuits": stored_circuits
196
        }
197
198
        mock_settings.WAIT_FOR_OLD_PATH = -1
199
        evc1 = MagicMock(id=1, service_level=0, creation_time=1)
200
        evc1.is_enabled.return_value = True
201
        evc1.is_active.return_value = False
202
        evc1.lock.locked.return_value = False
203
        evc1.has_recent_removed_flow.return_value = False
204
        evc1.is_recent_updated.return_value = False
205
        evc1.execution_rounds = 0
206
        evc1.deploy.call_count = 0
207
        self.napp.circuits = {'1': evc1}
208
        assert self.napp.get_evcs_by_svc_level() == [evc1]
209
        mock_settings.WAIT_FOR_OLD_PATH = 1
210
211
        mock_check_list_traces.return_value = {1: False}
212
213
        self.napp.execute_consistency()
214
        self.assertEqual(evc1.deploy.call_count, 0)
215
        self.napp.execute_consistency()
216
        self.assertEqual(evc1.deploy.call_count, 1)
217
218
    @patch('napps.kytos.mef_eline.main.Main._uni_from_dict')
219
    @patch('napps.kytos.mef_eline.models.evc.EVCBase._validate')
220
    def test_evc_from_dict(self, _validate_mock, uni_from_dict_mock):
221
        """
222
        Test the helper method that create an EVN from dict.
223
224
        Verify object creation with circuit data and schedule data.
225
        """
226
        _validate_mock.return_value = True
227
        uni_from_dict_mock.side_effect = ["uni_a", "uni_z"]
228
        payload = {
229
            "name": "my evc1",
230
            "uni_a": {
231
                "interface_id": "00:00:00:00:00:00:00:01:1",
232
                "tag": {"tag_type": 1, "value": 80},
233
            },
234
            "uni_z": {
235
                "interface_id": "00:00:00:00:00:00:00:02:2",
236
                "tag": {"tag_type": 1, "value": 1},
237
            },
238
            "circuit_scheduler": [
239
                {"frequency": "* * * * *", "action": "create"}
240
            ],
241
            "queue_id": 5,
242
        }
243
        # pylint: disable=protected-access
244
        evc_response = self.napp._evc_from_dict(payload)
245
        self.assertIsNotNone(evc_response)
246
        self.assertIsNotNone(evc_response.uni_a)
247
        self.assertIsNotNone(evc_response.uni_z)
248
        self.assertIsNotNone(evc_response.circuit_scheduler)
249
        self.assertIsNotNone(evc_response.name)
250
        self.assertIsNotNone(evc_response.queue_id)
251
252
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
253
    @patch("napps.kytos.mef_eline.models.evc.EVCBase._validate")
254
    @patch("kytos.core.Controller.get_interface_by_id")
255
    def test_evc_from_dict_paths(
256
        self, _get_interface_by_id_mock, _validate_mock, uni_from_dict_mock
257
    ):
258
        """
259
        Test the helper method that create an EVN from dict.
260
261
        Verify object creation with circuit data and schedule data.
262
        """
263
264
        _get_interface_by_id_mock.return_value = get_uni_mocked().interface
265
        _validate_mock.return_value = True
266
        uni_from_dict_mock.side_effect = ["uni_a", "uni_z"]
267
        payload = {
268
            "name": "my evc1",
269
            "uni_a": {
270
                "interface_id": "00:00:00:00:00:00:00:01:1",
271
                "tag": {"tag_type": 1, "value": 80},
272
            },
273
            "uni_z": {
274
                "interface_id": "00:00:00:00:00:00:00:02:2",
275
                "tag": {"tag_type": 1, "value": 1},
276
            },
277
            "current_path": [],
278
            "primary_path": [
279
                {
280
                    "endpoint_a": {
281
                        "interface_id": "00:00:00:00:00:00:00:01:1"
282
                    },
283
                    "endpoint_b": {
284
                        "interface_id": "00:00:00:00:00:00:00:02:2"
285
                    },
286
                }
287
            ],
288
            "backup_path": [],
289
        }
290
291
        # pylint: disable=protected-access
292
        evc_response = self.napp._evc_from_dict(payload)
293
        self.assertIsNotNone(evc_response)
294
        self.assertIsNotNone(evc_response.uni_a)
295
        self.assertIsNotNone(evc_response.uni_z)
296
        self.assertIsNotNone(evc_response.circuit_scheduler)
297
        self.assertIsNotNone(evc_response.name)
298
        self.assertEqual(len(evc_response.current_path), 0)
299
        self.assertEqual(len(evc_response.backup_path), 0)
300
        self.assertEqual(len(evc_response.primary_path), 1)
301
302
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
303
    @patch("napps.kytos.mef_eline.models.evc.EVCBase._validate")
304
    @patch("kytos.core.Controller.get_interface_by_id")
305
    def test_evc_from_dict_links(
306
        self, _get_interface_by_id_mock, _validate_mock, uni_from_dict_mock
307
    ):
308
        """
309
        Test the helper method that create an EVN from dict.
310
311
        Verify object creation with circuit data and schedule data.
312
        """
313
        _get_interface_by_id_mock.return_value = get_uni_mocked().interface
314
        _validate_mock.return_value = True
315
        uni_from_dict_mock.side_effect = ["uni_a", "uni_z"]
316
        payload = {
317
            "name": "my evc1",
318
            "uni_a": {
319
                "interface_id": "00:00:00:00:00:00:00:01:1",
320
                "tag": {"tag_type": 1, "value": 80},
321
            },
322
            "uni_z": {
323
                "interface_id": "00:00:00:00:00:00:00:02:2",
324
                "tag": {"tag_type": 1, "value": 1},
325
            },
326
            "primary_links": [
327
                {
328
                    "endpoint_a": {
329
                        "interface_id": "00:00:00:00:00:00:00:01:1"
330
                    },
331
                    "endpoint_b": {
332
                        "interface_id": "00:00:00:00:00:00:00:02:2"
333
                    },
334
                    "metadata": {
335
                        "s_vlan": {
336
                            "tag_type": 1,
337
                            "value": 100
338
                        }
339
                    },
340
                }
341
            ],
342
            "backup_links": [],
343
        }
344
345
        # pylint: disable=protected-access
346
        evc_response = self.napp._evc_from_dict(payload)
347
        self.assertIsNotNone(evc_response)
348
        self.assertIsNotNone(evc_response.uni_a)
349
        self.assertIsNotNone(evc_response.uni_z)
350
        self.assertIsNotNone(evc_response.circuit_scheduler)
351
        self.assertIsNotNone(evc_response.name)
352
        self.assertEqual(len(evc_response.current_links_cache), 0)
353
        self.assertEqual(len(evc_response.backup_links), 0)
354
        self.assertEqual(len(evc_response.primary_links), 1)
355
356
    def test_list_without_circuits(self):
357
        """Test if list circuits return 'no circuit stored.'."""
358
        circuits = {"circuits": {}}
359
        self.napp.mongo_controller.get_circuits.return_value = circuits
360
        api = self.get_app_test_client(self.napp)
361
        url = f"{self.server_name_url}/v2/evc/"
362
        response = api.get(url)
363
        self.assertEqual(response.status_code, 200, response.data)
364
        self.assertEqual(json.loads(response.data.decode()), {})
365
366
    def test_list_no_circuits_stored(self):
367
        """Test if list circuits return all circuits stored."""
368
        circuits = {"circuits": {}}
369
        self.napp.mongo_controller.get_circuits.return_value = circuits
370
371
        api = self.get_app_test_client(self.napp)
372
        url = f"{self.server_name_url}/v2/evc/"
373
374
        response = api.get(url)
375
        expected_result = circuits["circuits"]
376
        self.assertEqual(json.loads(response.data), expected_result)
377
378 View Code Duplication
    def test_list_with_circuits_stored(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
379
        """Test if list circuits return all circuits stored."""
380
        circuits = {
381
            'circuits':
382
            {"1": {"name": "circuit_1"}, "2": {"name": "circuit_2"}}
383
        }
384
        get_circuits = self.napp.mongo_controller.get_circuits
385
        get_circuits.return_value = circuits
386
387
        api = self.get_app_test_client(self.napp)
388
        url = f"{self.server_name_url}/v2/evc/"
389
390
        response = api.get(url)
391
        expected_result = circuits["circuits"]
392
        get_circuits.assert_called_with(archived=False)
393
        self.assertEqual(json.loads(response.data), expected_result)
394
395 View Code Duplication
    def test_list_with_archived_circuits_archived(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
396
        """Test if list circuits only archived circuits."""
397
        circuits = {
398
            'circuits':
399
            {
400
                "1": {"name": "circuit_1", "archived": True},
401
            }
402
        }
403
        get_circuits = self.napp.mongo_controller.get_circuits
404
        get_circuits.return_value = circuits
405
406
        api = self.get_app_test_client(self.napp)
407
        url = f"{self.server_name_url}/v2/evc/?archived=true"
408
409
        response = api.get(url)
410
        get_circuits.assert_called_with(archived=True)
411
        expected_result = {"1": circuits["circuits"]["1"]}
412
        self.assertEqual(json.loads(response.data), expected_result)
413
414
    def test_list_with_archived_circuits_all(self):
415
        """Test if list circuits return all circuits."""
416
        circuits = {
417
            'circuits': {
418
                "1": {"name": "circuit_1"},
419
                "2": {"name": "circuit_2", "archived": True},
420
            }
421
        }
422
        self.napp.mongo_controller.get_circuits.return_value = circuits
423
424
        api = self.get_app_test_client(self.napp)
425
        url = f"{self.server_name_url}/v2/evc/?archived=null"
426
427
        response = api.get(url)
428
        expected_result = circuits["circuits"]
429
        self.assertEqual(json.loads(response.data), expected_result)
430
431
    def test_circuit_with_valid_id(self):
432
        """Test if get_circuit return the circuit attributes."""
433
        circuit = {"name": "circuit_1"}
434
        self.napp.mongo_controller.get_circuit.return_value = circuit
435
436
        api = self.get_app_test_client(self.napp)
437
        url = f"{self.server_name_url}/v2/evc/1"
438
        response = api.get(url)
439
        expected_result = circuit
440
        self.assertEqual(json.loads(response.data), expected_result)
441
442
    def test_circuit_with_invalid_id(self):
443
        """Test if get_circuit return invalid circuit_id."""
444
        self.napp.mongo_controller.get_circuit.return_value = None
445
        api = self.get_app_test_client(self.napp)
446
        url = f"{self.server_name_url}/v2/evc/3"
447
        response = api.get(url)
448
        expected_result = "circuit_id 3 not found"
449
        self.assertEqual(
450
            json.loads(response.data)["description"], expected_result
451
        )
452
453
    @patch("napps.kytos.mef_eline.models.evc.EVC.deploy")
454
    @patch("napps.kytos.mef_eline.scheduler.Scheduler.add")
455
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
456
    @patch("napps.kytos.mef_eline.controllers.ELineController.upsert_evc")
457
    @patch("napps.kytos.mef_eline.main.EVC.as_dict")
458
    @patch("napps.kytos.mef_eline.models.evc.EVC._validate")
459
    def test_create_a_circuit_case_1(self, *args):
460
        """Test create a new circuit."""
461
        # pylint: disable=too-many-locals
462
        (
463
            validate_mock,
464
            evc_as_dict_mock,
465
            mongo_controller_upsert_mock,
466
            uni_from_dict_mock,
467
            sched_add_mock,
468
            evc_deploy_mock,
469
        ) = args
470
471
        validate_mock.return_value = True
472
        mongo_controller_upsert_mock.return_value = True
473
        evc_deploy_mock.return_value = True
474
        uni1 = create_autospec(UNI)
475
        uni2 = create_autospec(UNI)
476
        uni1.interface = create_autospec(Interface)
477
        uni2.interface = create_autospec(Interface)
478
        uni1.interface.switch = "00:00:00:00:00:00:00:01"
479
        uni2.interface.switch = "00:00:00:00:00:00:00:02"
480
        uni_from_dict_mock.side_effect = [uni1, uni2]
481
        evc_as_dict_mock.return_value = {}
482
        sched_add_mock.return_value = True
483
        self.napp.mongo_controller.get_circuits.return_value = {}
484
485
        api = self.get_app_test_client(self.napp)
486
        url = f"{self.server_name_url}/v2/evc/"
487
        payload = {
488
            "name": "my evc1",
489
            "frequency": "* * * * *",
490
            "uni_a": {
491
                "interface_id": "00:00:00:00:00:00:00:01:1",
492
                "tag": {"tag_type": 1, "value": 80},
493
            },
494
            "uni_z": {
495
                "interface_id": "00:00:00:00:00:00:00:02:2",
496
                "tag": {"tag_type": 1, "value": 1},
497
            },
498
            "dynamic_backup_path": True,
499
            "primary_constraints": {
500
                "spf_max_path_cost": 8,
501
                "mandatory_metrics": {
502
                    "ownership": "red"
503
                }
504
            },
505
            "secondary_constraints": {
506
                "mandatory_metrics": {
507
                    "ownership": "blue"
508
                }
509
            }
510
        }
511
512
        response = api.post(
513
            url, data=json.dumps(payload), content_type="application/json"
514
        )
515
        current_data = json.loads(response.data)
516
517
        # verify expected result from request
518
        self.assertEqual(201, response.status_code, response.data)
519
        self.assertIn("circuit_id", current_data)
520
521
        # verify uni called
522
        uni_from_dict_mock.called_twice()
523
        uni_from_dict_mock.assert_any_call(payload["uni_z"])
524
        uni_from_dict_mock.assert_any_call(payload["uni_a"])
525
526
        # verify validation called
527
        validate_mock.assert_called_once()
528
        validate_mock.assert_called_with(
529
            frequency="* * * * *",
530
            name="my evc1",
531
            uni_a=uni1,
532
            uni_z=uni2,
533
            dynamic_backup_path=True,
534
            primary_constraints=payload["primary_constraints"],
535
            secondary_constraints=payload["secondary_constraints"],
536
        )
537
        # verify save method is called
538
        mongo_controller_upsert_mock.assert_called_once()
539
540
        # verify evc as dict is called to save in the box
541
        evc_as_dict_mock.assert_called()
542
        # verify add circuit in sched
543
        sched_add_mock.assert_called_once()
544
545
    @staticmethod
546
    def get_napp_urls(napp):
547
        """Return the kytos/mef_eline urls.
548
549
        The urls will be like:
550
551
        urls = [
552
            (options, methods, url)
553
        ]
554
555
        """
556
        controller = napp.controller
557
        controller.api_server.register_napp_endpoints(napp)
558
559
        urls = []
560
        for rule in controller.api_server.app.url_map.iter_rules():
561
            options = {}
562
            for arg in rule.arguments:
563
                options[arg] = f"[{0}]".format(arg)
564
565
            if f"{napp.username}/{napp.name}" in str(rule):
566
                urls.append((options, rule.methods, f"{str(rule)}"))
567
568
        return urls
569
570
    @staticmethod
571
    def get_app_test_client(napp):
572
        """Return a flask api test client."""
573
        napp.controller.api_server.register_napp_endpoints(napp)
574
        return napp.controller.api_server.app.test_client()
575
576
    def test_create_a_circuit_case_2(self):
577
        """Test create a new circuit trying to send request without a json."""
578
        api = self.get_app_test_client(self.napp)
579
        url = f"{self.server_name_url}/v2/evc/"
580
581
        response = api.post(url)
582
        current_data = json.loads(response.data)
583
        expected_message = "The request body mimetype is not application/json."
584
        expected_data = expected_message
585
        self.assertEqual(415, response.status_code, response.data)
586
        self.assertEqual(current_data["description"], expected_data)
587
588
    def test_create_a_circuit_case_3(self):
589
        """Test create a new circuit trying to send request with an
590
        invalid json."""
591
        api = self.get_app_test_client(self.napp)
592
        url = f"{self.server_name_url}/v2/evc/"
593
594
        response = api.post(
595
            url,
596
            data="This is an {Invalid:} JSON",
597
            content_type="application/json",
598
        )
599
        current_data = json.loads(response.data)
600
        expected_data = "The request body is not a well-formed JSON."
601
602
        self.assertEqual(400, response.status_code, response.data)
603
        self.assertEqual(current_data["description"], expected_data)
604
605
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
606
    @patch("napps.kytos.mef_eline.controllers.ELineController.upsert_evc")
607
    def test_create_a_circuit_case_4(
608
        self,
609
        mongo_controller_upsert_mock,
610
        uni_from_dict_mock
611
    ):
612
        """Test create a new circuit trying to send request with an
613
        invalid value."""
614
        # pylint: disable=too-many-locals
615
        uni_from_dict_mock.side_effect = ValueError("Could not instantiate")
616
        mongo_controller_upsert_mock.return_value = True
617
        api = self.get_app_test_client(self.napp)
618
        url = f"{self.server_name_url}/v2/evc/"
619
620
        payload = {
621
            "name": "my evc1",
622
            "frequency": "* * * * *",
623
            "uni_a": {
624
                "interface_id": "00:00:00:00:00:00:00:01:76",
625
                "tag": {"tag_type": 1, "value": 80},
626
            },
627
            "uni_z": {
628
                "interface_id": "00:00:00:00:00:00:00:02:2",
629
                "tag": {"tag_type": 1, "value": 1},
630
            },
631
        }
632
633
        response = api.post(
634
            url, data=json.dumps(payload), content_type="application/json"
635
        )
636
        current_data = json.loads(response.data)
637
        expected_data = "Error creating UNI: Invalid value"
638
        self.assertEqual(400, response.status_code, response.data)
639
        self.assertEqual(current_data["description"], expected_data)
640
641
        payload["name"] = 1
642
        response = api.post(
643
            url, data=json.dumps(payload), content_type="application/json"
644
        )
645
        self.assertEqual(400, response.status_code, response.data)
646
647
    def test_create_a_circuit_invalid_queue_id(self):
648
        """Test create a new circuit with invalid queue_id."""
649
        api = self.get_app_test_client(self.napp)
650
        url = f"{self.server_name_url}/v2/evc/"
651
652
        payload = {
653
            "name": "my evc1",
654
            "queue_id": 8,
655
            "uni_a": {
656
                "interface_id": "00:00:00:00:00:00:00:01:76",
657
                "tag": {"tag_type": 1, "value": 80},
658
            },
659
            "uni_z": {
660
                "interface_id": "00:00:00:00:00:00:00:02:2",
661
                "tag": {"tag_type": 1, "value": 1},
662
            },
663
        }
664
        response = api.post(
665
            url,
666
            data=json.dumps(payload),
667
            content_type="application/json",
668
        )
669
        current_data = json.loads(response.data)
670
        expected_data = "8 is greater than the maximum of 7"
671
672
        assert response.status_code == 400
673
        assert expected_data in current_data["description"], expected_data
674
675
    @patch("napps.kytos.mef_eline.models.evc.EVC.deploy")
676
    @patch("napps.kytos.mef_eline.scheduler.Scheduler.add")
677
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
678
    @patch("napps.kytos.mef_eline.controllers.ELineController.upsert_evc")
679
    @patch("napps.kytos.mef_eline.models.evc.EVC._validate")
680
    @patch("napps.kytos.mef_eline.main.EVC.as_dict")
681
    def test_create_circuit_already_enabled(self, *args):
682
        """Test create an already created circuit."""
683
        # pylint: disable=too-many-locals
684
        (
685
            evc_as_dict_mock,
686
            validate_mock,
687
            mongo_controller_upsert_mock,
688
            uni_from_dict_mock,
689
            sched_add_mock,
690
            evc_deploy_mock,
691
        ) = args
692
693
        validate_mock.return_value = True
694
        mongo_controller_upsert_mock.return_value = True
695
        sched_add_mock.return_value = True
696
        evc_deploy_mock.return_value = True
697
        uni1 = create_autospec(UNI)
698
        uni2 = create_autospec(UNI)
699
        uni1.interface = create_autospec(Interface)
700
        uni2.interface = create_autospec(Interface)
701
        uni1.interface.switch = "00:00:00:00:00:00:00:01"
702
        uni2.interface.switch = "00:00:00:00:00:00:00:02"
703
        uni_from_dict_mock.side_effect = [uni1, uni2, uni1, uni2]
704
705
        api = self.get_app_test_client(self.napp)
706
        payload = {
707
            "name": "my evc1",
708
            "uni_a": {
709
                "interface_id": "00:00:00:00:00:00:00:01:1",
710
                "tag": {"tag_type": 1, "value": 80},
711
            },
712
            "uni_z": {
713
                "interface_id": "00:00:00:00:00:00:00:02:2",
714
                "tag": {"tag_type": 1, "value": 1},
715
            },
716
            "dynamic_backup_path": True,
717
        }
718
719
        evc_as_dict_mock.return_value = payload
720
        response = api.post(
721
            f"{self.server_name_url}/v2/evc/",
722
            data=json.dumps(payload),
723
            content_type="application/json",
724
        )
725
        self.assertEqual(201, response.status_code)
726
727
        response = api.post(
728
            f"{self.server_name_url}/v2/evc/",
729
            data=json.dumps(payload),
730
            content_type="application/json",
731
        )
732
        current_data = json.loads(response.data)
733
        expected_data = "The EVC already exists."
734
        self.assertEqual(current_data["description"], expected_data)
735
        self.assertEqual(409, response.status_code)
736
737
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
738
    def test_create_circuit_case_5(self, uni_from_dict_mock):
739
        """Test when neither primary path nor dynamic_backup_path is set."""
740
        api = self.get_app_test_client(self.napp)
741
        url = f"{self.server_name_url}/v2/evc/"
742
        uni1 = create_autospec(UNI)
743
        uni2 = create_autospec(UNI)
744
        uni1.interface = create_autospec(Interface)
745
        uni2.interface = create_autospec(Interface)
746
        uni1.interface.switch = "00:00:00:00:00:00:00:01"
747
        uni2.interface.switch = "00:00:00:00:00:00:00:02"
748
        uni_from_dict_mock.side_effect = [uni1, uni2, uni1, uni2]
749
750
        payload = {
751
            "name": "my evc1",
752
            "frequency": "* * * * *",
753
            "uni_a": {
754
                "interface_id": "00:00:00:00:00:00:00:01:1",
755
                "tag": {"tag_type": 1, "value": 80},
756
            },
757
            "uni_z": {
758
                "interface_id": "00:00:00:00:00:00:00:02:2",
759
                "tag": {"tag_type": 1, "value": 1},
760
            },
761
        }
762
763
        response = api.post(
764
            url, data=json.dumps(payload), content_type="application/json"
765
        )
766
        current_data = json.loads(response.data)
767
        expected_data = "The EVC must have a primary path "
768
        expected_data += "or allow dynamic paths."
769
        self.assertEqual(400, response.status_code, response.data)
770
        self.assertEqual(current_data["description"], expected_data)
771
772
    def test_redeploy_evc(self):
773
        """Test endpoint to redeploy an EVC."""
774
        evc1 = MagicMock()
775
        evc1.is_enabled.return_value = True
776
        self.napp.circuits = {"1": evc1, "2": MagicMock()}
777
        api = self.get_app_test_client(self.napp)
778
        url = f"{self.server_name_url}/v2/evc/1/redeploy"
779
        response = api.patch(url)
780
        self.assertEqual(response.status_code, 202, response.data)
781
782
    def test_redeploy_evc_disabled(self):
783
        """Test endpoint to redeploy an EVC."""
784
        evc1 = MagicMock()
785
        evc1.is_enabled.return_value = False
786
        self.napp.circuits = {"1": evc1, "2": MagicMock()}
787
        api = self.get_app_test_client(self.napp)
788
        url = f"{self.server_name_url}/v2/evc/1/redeploy"
789
        response = api.patch(url)
790
        self.assertEqual(response.status_code, 409, response.data)
791
792
    def test_redeploy_evc_deleted(self):
793
        """Test endpoint to redeploy an EVC."""
794
        evc1 = MagicMock()
795
        evc1.is_enabled.return_value = True
796
        self.napp.circuits = {"1": evc1, "2": MagicMock()}
797
        api = self.get_app_test_client(self.napp)
798
        url = f"{self.server_name_url}/v2/evc/3/redeploy"
799
        response = api.patch(url)
800
        self.assertEqual(response.status_code, 404, response.data)
801
802
    def test_list_schedules__no_data_stored(self):
803
        """Test if list circuits return all circuits stored."""
804
        self.napp.mongo_controller.get_circuits.return_value = {"circuits": {}}
805
806
        api = self.get_app_test_client(self.napp)
807
        url = f"{self.server_name_url}/v2/evc/schedule"
808
809
        response = api.get(url)
810
        expected_result = {}
811
812
        self.assertEqual(response.status_code, 200, response.data)
813
        self.assertEqual(json.loads(response.data), expected_result)
814
815
    def _add_mongodb_schedule_data(self, data_mock):
816
        """Add schedule data to mongodb mock object."""
817
        circuits = {"circuits": {}}
818
        payload_1 = {
819
            "id": "aa:aa:aa",
820
            "name": "my evc1",
821
            "uni_a": {
822
                "interface_id": "00:00:00:00:00:00:00:01:1",
823
                "tag": {"tag_type": 1, "value": 80},
824
            },
825
            "uni_z": {
826
                "interface_id": "00:00:00:00:00:00:00:02:2",
827
                "tag": {"tag_type": 1, "value": 1},
828
            },
829
            "circuit_scheduler": [
830
                {"id": "1", "frequency": "* * * * *", "action": "create"},
831
                {"id": "2", "frequency": "1 * * * *", "action": "remove"},
832
            ],
833
        }
834
        circuits["circuits"].update({"aa:aa:aa": payload_1})
835
        payload_2 = {
836
            "id": "bb:bb:bb",
837
            "name": "my second evc2",
838
            "uni_a": {
839
                "interface_id": "00:00:00:00:00:00:00:01:2",
840
                "tag": {"tag_type": 1, "value": 90},
841
            },
842
            "uni_z": {
843
                "interface_id": "00:00:00:00:00:00:00:03:2",
844
                "tag": {"tag_type": 1, "value": 100},
845
            },
846
            "circuit_scheduler": [
847
                {"id": "3", "frequency": "1 * * * *", "action": "create"},
848
                {"id": "4", "frequency": "2 * * * *", "action": "remove"},
849
            ],
850
        }
851
        circuits["circuits"].update({"bb:bb:bb": payload_2})
852
        payload_3 = {
853
            "id": "cc:cc:cc",
854
            "name": "my third evc3",
855
            "uni_a": {
856
                "interface_id": "00:00:00:00:00:00:00:03:1",
857
                "tag": {"tag_type": 1, "value": 90},
858
            },
859
            "uni_z": {
860
                "interface_id": "00:00:00:00:00:00:00:04:2",
861
                "tag": {"tag_type": 1, "value": 100},
862
            },
863
        }
864
        circuits["circuits"].update({"cc:cc:cc": payload_3})
865
        # Add one circuit to the mongodb.
866
        data_mock.return_value = circuits
867
868
    def test_list_schedules_from_mongodb(self):
869
        """Test if list circuits return specific circuits stored."""
870
        self._add_mongodb_schedule_data(
871
            self.napp.mongo_controller.get_circuits
872
        )
873
874
        api = self.get_app_test_client(self.napp)
875
        url = f"{self.server_name_url}/v2/evc/schedule"
876
877
        # Call URL
878
        response = api.get(url)
879
        # Expected JSON data from response
880
        expected = [
881
            {
882
                "circuit_id": "aa:aa:aa",
883
                "schedule": {
884
                    "action": "create",
885
                    "frequency": "* * * * *",
886
                    "id": "1",
887
                },
888
                "schedule_id": "1",
889
            },
890
            {
891
                "circuit_id": "aa:aa:aa",
892
                "schedule": {
893
                    "action": "remove",
894
                    "frequency": "1 * * * *",
895
                    "id": "2",
896
                },
897
                "schedule_id": "2",
898
            },
899
            {
900
                "circuit_id": "bb:bb:bb",
901
                "schedule": {
902
                    "action": "create",
903
                    "frequency": "1 * * * *",
904
                    "id": "3",
905
                },
906
                "schedule_id": "3",
907
            },
908
            {
909
                "circuit_id": "bb:bb:bb",
910
                "schedule": {
911
                    "action": "remove",
912
                    "frequency": "2 * * * *",
913
                    "id": "4",
914
                },
915
                "schedule_id": "4",
916
            },
917
        ]
918
919
        self.assertEqual(response.status_code, 200, response.data)
920
        self.assertEqual(expected, json.loads(response.data))
921
922
    def test_get_specific_schedule_from_mongodb(self):
923
        """Test get schedules from a circuit."""
924
        self._add_mongodb_schedule_data(
925
            self.napp.mongo_controller.get_circuits
926
        )
927
928
        requested_circuit_id = "bb:bb:bb"
929
        evc = self.napp.mongo_controller.get_circuits()
930
        evc = evc["circuits"][requested_circuit_id]
931
        self.napp.mongo_controller.get_circuit.return_value = evc
932
        api = self.get_app_test_client(self.napp)
933
        url = f"{self.server_name_url}/v2/evc/{requested_circuit_id}"
934
935
        # Call URL
936
        response = api.get(url)
937
938
        # Expected JSON data from response
939
        expected = [
940
            {"action": "create", "frequency": "1 * * * *", "id": "3"},
941
            {"action": "remove", "frequency": "2 * * * *", "id": "4"},
942
        ]
943
944
        self.assertEqual(response.status_code, 200)
945
        self.assertEqual(
946
            expected, json.loads(response.data)["circuit_scheduler"]
947
        )
948
949
    def test_get_specific_schedules_from_mongodb_not_found(self):
950
        """Test get specific schedule ID that does not exist."""
951
        requested_id = "blah"
952
        api = self.get_app_test_client(self.napp)
953
        self.napp.mongo_controller.get_circuit.return_value = None
954
        url = f"{self.server_name_url}/v2/evc/{requested_id}"
955
956
        # Call URL
957
        response = api.get(url)
958
959
        expected = "circuit_id blah not found"
960
        # Assert response not found
961
        self.assertEqual(response.status_code, 404, response.data)
962
        self.assertEqual(expected, json.loads(response.data)["description"])
963
964
    def _uni_from_dict_side_effect(self, uni_dict):
965
        interface_id = uni_dict.get("interface_id")
966
        tag_dict = uni_dict.get("tag")
967
        interface = Interface(interface_id, "0", MagicMock(id="1"))
968
        return UNI(interface, tag_dict)
969
970
    @patch("apscheduler.schedulers.background.BackgroundScheduler.add_job")
971
    @patch("napps.kytos.mef_eline.scheduler.Scheduler.add")
972
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
973
    @patch("napps.kytos.mef_eline.controllers.ELineController.upsert_evc")
974
    @patch("napps.kytos.mef_eline.main.EVC.as_dict")
975
    @patch("napps.kytos.mef_eline.models.evc.EVC._validate")
976
    def test_create_schedule(self, *args):  # pylint: disable=too-many-locals
977
        """Test create a circuit schedule."""
978
        (
979
            validate_mock,
980
            evc_as_dict_mock,
981
            mongo_controller_upsert_mock,
982
            uni_from_dict_mock,
983
            sched_add_mock,
984
            scheduler_add_job_mock,
985
        ) = args
986
987
        validate_mock.return_value = True
988
        mongo_controller_upsert_mock.return_value = True
989
        uni_from_dict_mock.side_effect = self._uni_from_dict_side_effect
990
        evc_as_dict_mock.return_value = {}
991
        sched_add_mock.return_value = True
992
993
        self._add_mongodb_schedule_data(
994
            self.napp.mongo_controller.get_circuits
995
        )
996
997
        requested_id = "bb:bb:bb"
998
        api = self.get_app_test_client(self.napp)
999
        url = f"{self.server_name_url}/v2/evc/schedule/"
1000
1001
        payload = {
1002
            "circuit_id": requested_id,
1003
            "schedule": {"frequency": "1 * * * *", "action": "create"},
1004
            "metadata": {"metadata1": "test_data"},
1005
        }
1006
1007
        # Call URL
1008
        response = api.post(
1009
            url, data=json.dumps(payload), content_type="application/json"
1010
        )
1011
1012
        response_json = json.loads(response.data)
1013
1014
        self.assertEqual(response.status_code, 201, response.data)
1015
        scheduler_add_job_mock.assert_called_once()
1016
        mongo_controller_upsert_mock.assert_called_once()
1017
        self.assertEqual(
1018
            payload["schedule"]["frequency"], response_json["frequency"]
1019
        )
1020
        self.assertEqual(
1021
            payload["schedule"]["action"], response_json["action"]
1022
        )
1023
        self.assertIsNotNone(response_json["id"])
1024
1025
        # Case 2: there is no schedule
1026
        payload = {
1027
              "circuit_id": "cc:cc:cc",
1028
              "schedule": {
1029
                "frequency": "1 * * * *",
1030
                "action": "create"
1031
              }
1032
            }
1033
        response = api.post(url, data=json.dumps(payload),
1034
                            content_type='application/json')
1035
        self.assertEqual(response.status_code, 201)
1036
1037
    def test_create_schedule_invalid_request(self):
1038
        """Test create schedule API with invalid request."""
1039
        evc1 = MagicMock()
1040
        self.napp.circuits = {'bb:bb:bb': evc1}
1041
        api = self.get_app_test_client(self.napp)
1042
        url = f'{self.server_name_url}/v2/evc/schedule/'
1043
1044
        # case 1: empty post
1045
        response = api.post(url, data="")
1046
        self.assertEqual(response.status_code, 415)
1047
1048
        # case 2: content-type not specified
1049
        payload = {
1050
            "circuit_id": "bb:bb:bb",
1051
            "schedule": {
1052
                "frequency": "1 * * * *",
1053
                "action": "create"
1054
            }
1055
        }
1056
        response = api.post(url, data=json.dumps(payload))
1057
        self.assertEqual(response.status_code, 415)
1058
1059
        # case 3: not a dictionary
1060
        payload = []
1061
        response = api.post(url, data=json.dumps(payload),
1062
                            content_type='application/json')
1063
        self.assertEqual(response.status_code, 400)
1064
1065
        # case 4: missing circuit id
1066
        payload = {
1067
            "schedule": {
1068
                "frequency": "1 * * * *",
1069
                "action": "create"
1070
            }
1071
        }
1072
        response = api.post(url, data=json.dumps(payload),
1073
                            content_type='application/json')
1074
        self.assertEqual(response.status_code, 400)
1075
1076
        # case 5: missing schedule
1077
        payload = {
1078
            "circuit_id": "bb:bb:bb"
1079
        }
1080
        response = api.post(url, data=json.dumps(payload),
1081
                            content_type='application/json')
1082
        self.assertEqual(response.status_code, 400)
1083
1084
        # case 6: invalid circuit
1085
        payload = {
1086
            "circuit_id": "xx:xx:xx",
1087
            "schedule": {
1088
                "frequency": "1 * * * *",
1089
                "action": "create"
1090
            }
1091
        }
1092
        response = api.post(url, data=json.dumps(payload),
1093
                            content_type='application/json')
1094
        self.assertEqual(response.status_code, 404)
1095
1096
        # case 7: archived or deleted evc
1097
        evc1.archived.return_value = True
1098
        payload = {
1099
            "circuit_id": "bb:bb:bb",
1100
            "schedule": {
1101
                "frequency": "1 * * * *",
1102
                "action": "create"
1103
            }
1104
        }
1105
        response = api.post(url, data=json.dumps(payload),
1106
                            content_type='application/json')
1107
        self.assertEqual(response.status_code, 403)
1108
1109
        # case 8: invalid json
1110
        response = api.post(url, data='{"test"}',
1111
                            content_type='application/json')
1112
        self.assertEqual(response.status_code, 400)
1113
1114
    @patch('apscheduler.schedulers.background.BackgroundScheduler.remove_job')
1115
    @patch('napps.kytos.mef_eline.scheduler.Scheduler.add')
1116
    @patch('napps.kytos.mef_eline.main.Main._uni_from_dict')
1117
    @patch("napps.kytos.mef_eline.controllers.ELineController.upsert_evc")
1118
    @patch('napps.kytos.mef_eline.main.EVC.as_dict')
1119
    @patch('napps.kytos.mef_eline.models.evc.EVC._validate')
1120
    def test_update_schedule(self, *args):  # pylint: disable=too-many-locals
1121
        """Test create a circuit schedule."""
1122
        (
1123
            validate_mock,
1124
            evc_as_dict_mock,
1125
            mongo_controller_upsert_mock,
1126
            uni_from_dict_mock,
1127
            sched_add_mock,
1128
            scheduler_remove_job_mock,
1129
        ) = args
1130
1131
        mongo_payload_1 = {
1132
            "circuits": {
1133
                "aa:aa:aa": {
1134
                    "id": "aa:aa:aa",
1135
                    "name": "my evc1",
1136
                    "uni_a": {
1137
                        "interface_id": "00:00:00:00:00:00:00:01:1",
1138
                        "tag": {"tag_type": 1, "value": 80},
1139
                    },
1140
                    "uni_z": {
1141
                        "interface_id": "00:00:00:00:00:00:00:02:2",
1142
                        "tag": {"tag_type": 1, "value": 1},
1143
                    },
1144
                    "circuit_scheduler": [
1145
                        {
1146
                            "id": "1",
1147
                            "frequency": "* * * * *",
1148
                            "action": "create"
1149
                        }
1150
                    ],
1151
                }
1152
            }
1153
        }
1154
1155
        validate_mock.return_value = True
1156
        mongo_controller_upsert_mock.return_value = True
1157
        sched_add_mock.return_value = True
1158
        uni_from_dict_mock.side_effect = ["uni_a", "uni_z"]
1159
        evc_as_dict_mock.return_value = {}
1160
        self.napp.mongo_controller.get_circuits.return_value = mongo_payload_1
1161
        scheduler_remove_job_mock.return_value = True
1162
1163
        requested_schedule_id = "1"
1164
        api = self.get_app_test_client(self.napp)
1165
        url = f"{self.server_name_url}/v2/evc/schedule/{requested_schedule_id}"
1166
1167
        payload = {"frequency": "*/1 * * * *", "action": "create"}
1168
1169
        # Call URL
1170
        response = api.patch(
1171
            url, data=json.dumps(payload), content_type="application/json"
1172
        )
1173
1174
        response_json = json.loads(response.data)
1175
1176
        self.assertEqual(response.status_code, 200, response.data)
1177
        scheduler_remove_job_mock.assert_called_once()
1178
        mongo_controller_upsert_mock.assert_called_once()
1179
        self.assertEqual(payload["frequency"], response_json["frequency"])
1180
        self.assertEqual(payload["action"], response_json["action"])
1181
        self.assertIsNotNone(response_json["id"])
1182
1183
    @patch('napps.kytos.mef_eline.main.Main._find_evc_by_schedule_id')
1184
    def test_update_no_schedule(self, find_evc_by_schedule_id_mock):
1185
        """Test update a circuit schedule."""
1186
        api = self.get_app_test_client(self.napp)
1187
        url = f"{self.server_name_url}/v2/evc/schedule/1"
1188
        payload = {"frequency": "*/1 * * * *", "action": "create"}
1189
1190
        find_evc_by_schedule_id_mock.return_value = None, None
1191
1192
        response = api.patch(
1193
            url, data=json.dumps(payload), content_type="application/json"
1194
        )
1195
1196
        self.assertEqual(response.status_code, 404)
1197
1198
    @patch("napps.kytos.mef_eline.scheduler.Scheduler.add")
1199
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
1200
    @patch("napps.kytos.mef_eline.main.EVC.as_dict")
1201
    @patch("napps.kytos.mef_eline.models.evc.EVC._validate")
1202
    def test_update_schedule_archived(self, *args):
1203
        """Test create a circuit schedule."""
1204
        # pylint: disable=too-many-locals
1205
        (
1206
            validate_mock,
1207
            evc_as_dict_mock,
1208
            uni_from_dict_mock,
1209
            sched_add_mock,
1210
        ) = args
1211
1212
        mongo_payload_1 = {
1213
            "circuits": {
1214
                "aa:aa:aa": {
1215
                    "id": "aa:aa:aa",
1216
                    "name": "my evc1",
1217
                    "archived": True,
1218
                    "circuit_scheduler": [
1219
                        {
1220
                            "id": "1",
1221
                            "frequency": "* * * * *",
1222
                            "action": "create"
1223
                        }
1224
                    ],
1225
                }
1226
            }
1227
        }
1228
1229
        validate_mock.return_value = True
1230
        sched_add_mock.return_value = True
1231
        uni_from_dict_mock.side_effect = ["uni_a", "uni_z"]
1232
        evc_as_dict_mock.return_value = {}
1233
        self.napp.mongo_controller.get_circuits.return_value = mongo_payload_1
1234
1235
        requested_schedule_id = "1"
1236
        api = self.get_app_test_client(self.napp)
1237
        url = f"{self.server_name_url}/v2/evc/schedule/{requested_schedule_id}"
1238
1239
        payload = {"frequency": "*/1 * * * *", "action": "create"}
1240
1241
        # Call URL
1242
        response = api.patch(
1243
            url, data=json.dumps(payload), content_type="application/json"
1244
        )
1245
1246
        self.assertEqual(response.status_code, 403, response.data)
1247
1248
    @patch("apscheduler.schedulers.background.BackgroundScheduler.remove_job")
1249
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
1250
    @patch("napps.kytos.mef_eline.controllers.ELineController.upsert_evc")
1251
    @patch("napps.kytos.mef_eline.main.EVC.as_dict")
1252
    @patch("napps.kytos.mef_eline.models.evc.EVC._validate")
1253
    def test_delete_schedule(self, *args):
1254
        """Test create a circuit schedule."""
1255
        (
1256
            validate_mock,
1257
            evc_as_dict_mock,
1258
            mongo_controller_upsert_mock,
1259
            uni_from_dict_mock,
1260
            scheduler_remove_job_mock,
1261
        ) = args
1262
1263
        mongo_payload_1 = {
1264
            "circuits": {
1265
                "2": {
1266
                    "id": "2",
1267
                    "name": "my evc1",
1268
                    "uni_a": {
1269
                        "interface_id": "00:00:00:00:00:00:00:01:1",
1270
                        "tag": {"tag_type": 1, "value": 80},
1271
                    },
1272
                    "uni_z": {
1273
                        "interface_id": "00:00:00:00:00:00:00:02:2",
1274
                        "tag": {"tag_type": 1, "value": 1},
1275
                    },
1276
                    "circuit_scheduler": [
1277
                        {
1278
                            "id": "1",
1279
                            "frequency": "* * * * *",
1280
                            "action": "create"
1281
                        }
1282
                    ],
1283
                }
1284
            }
1285
        }
1286
        validate_mock.return_value = True
1287
        mongo_controller_upsert_mock.return_value = True
1288
        uni_from_dict_mock.side_effect = ["uni_a", "uni_z"]
1289
        evc_as_dict_mock.return_value = {}
1290
        self.napp.mongo_controller.get_circuits.return_value = mongo_payload_1
1291
        scheduler_remove_job_mock.return_value = True
1292
1293
        requested_schedule_id = "1"
1294
        api = self.get_app_test_client(self.napp)
1295
        url = f"{self.server_name_url}/v2/evc/schedule/{requested_schedule_id}"
1296
1297
        # Call URL
1298
        response = api.delete(url)
1299
1300
        self.assertEqual(response.status_code, 200, response.data)
1301
        scheduler_remove_job_mock.assert_called_once()
1302
        mongo_controller_upsert_mock.assert_called_once()
1303
        self.assertIn("Schedule removed", f"{response.data}")
1304
1305
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
1306
    @patch("napps.kytos.mef_eline.main.EVC.as_dict")
1307
    @patch("napps.kytos.mef_eline.models.evc.EVC._validate")
1308
    def test_delete_schedule_archived(self, *args):
1309
        """Test create a circuit schedule."""
1310
        (
1311
            validate_mock,
1312
            evc_as_dict_mock,
1313
            uni_from_dict_mock,
1314
        ) = args
1315
1316
        mongo_payload_1 = {
1317
            "circuits": {
1318
                "2": {
1319
                    "id": "2",
1320
                    "name": "my evc1",
1321
                    "archived": True,
1322
                    "circuit_scheduler": [
1323
                        {
1324
                            "id": "1",
1325
                            "frequency": "* * * * *",
1326
                            "action": "create"
1327
                        }
1328
                    ],
1329
                }
1330
            }
1331
        }
1332
1333
        validate_mock.return_value = True
1334
        uni_from_dict_mock.side_effect = ["uni_a", "uni_z"]
1335
        evc_as_dict_mock.return_value = {}
1336
        self.napp.mongo_controller.get_circuits.return_value = mongo_payload_1
1337
1338
        requested_schedule_id = "1"
1339
        api = self.get_app_test_client(self.napp)
1340
        url = f"{self.server_name_url}/v2/evc/schedule/{requested_schedule_id}"
1341
1342
        # Call URL
1343
        response = api.delete(url)
1344
1345
        self.assertEqual(response.status_code, 403, response.data)
1346
1347
    @patch('napps.kytos.mef_eline.main.Main._find_evc_by_schedule_id')
1348
    def test_delete_schedule_not_found(self, mock_find_evc_by_sched):
1349
        """Test delete a circuit schedule - unexisting."""
1350
        mock_find_evc_by_sched.return_value = (None, False)
1351
        api = self.get_app_test_client(self.napp)
1352
        url = f'{self.server_name_url}/v2/evc/schedule/1'
1353
        response = api.delete(url)
1354
        self.assertEqual(response.status_code, 404)
1355
1356
    def test_get_evcs_by_svc_level(self) -> None:
1357
        """Test get_evcs_by_svc_level."""
1358
        levels = [1, 2, 4, 2, 7]
1359
        evcs = {i: MagicMock(service_level=v, creation_time=1)
1360
                for i, v in enumerate(levels)}
1361
        self.napp.circuits = evcs
1362
        expected_levels = sorted(levels, reverse=True)
1363
        evcs_by_level = self.napp.get_evcs_by_svc_level()
1364
        assert evcs_by_level
1365
1366
        for evc, exp_level in zip(evcs_by_level, expected_levels):
1367
            assert evc.service_level == exp_level
1368
1369
        evcs = {i: MagicMock(service_level=1, creation_time=i)
1370
                for i in reversed(range(2))}
1371
        self.napp.circuits = evcs
1372
        evcs_by_level = self.napp.get_evcs_by_svc_level()
1373
        for i in range(2):
1374
            assert evcs_by_level[i].creation_time == i
1375
1376
    def test_get_circuit_not_found(self):
1377
        """Test /v2/evc/<circuit_id> 404."""
1378
        self.napp.mongo_controller.get_circuit.return_value = None
1379
        api = self.get_app_test_client(self.napp)
1380
        url = f'{self.server_name_url}/v2/evc/1234'
1381
        response = api.get(url)
1382
        self.assertEqual(response.status_code, 404)
1383
1384
    @patch('requests.post')
1385
    @patch('napps.kytos.mef_eline.scheduler.Scheduler.add')
1386
    @patch("napps.kytos.mef_eline.controllers.ELineController.upsert_evc")
1387
    @patch('napps.kytos.mef_eline.models.evc.EVC._validate')
1388
    @patch('kytos.core.Controller.get_interface_by_id')
1389
    @patch('napps.kytos.mef_eline.models.path.Path.is_valid')
1390
    @patch('napps.kytos.mef_eline.models.evc.EVCDeploy.deploy')
1391
    @patch('napps.kytos.mef_eline.main.Main._uni_from_dict')
1392
    @patch('napps.kytos.mef_eline.main.EVC.as_dict')
1393
    def test_update_circuit(self, *args):
1394
        """Test update a circuit circuit."""
1395
        # pylint: disable=too-many-locals,duplicate-code
1396
        (
1397
            evc_as_dict_mock,
1398
            uni_from_dict_mock,
1399
            evc_deploy,
1400
            _,
1401
            interface_by_id_mock,
1402
            _,
1403
            _,
1404
            _,
1405
            requests_mock,
1406
        ) = args
1407
1408
        interface_by_id_mock.return_value = get_uni_mocked().interface
1409
        unis = [
1410
            get_uni_mocked(switch_dpid="00:00:00:00:00:00:00:01"),
1411
            get_uni_mocked(switch_dpid="00:00:00:00:00:00:00:02"),
1412
        ]
1413
        uni_from_dict_mock.side_effect = 2 * unis
1414
1415
        response = MagicMock()
1416
        response.status_code = 201
1417
        requests_mock.return_value = response
1418
1419
        api = self.get_app_test_client(self.napp)
1420
        payloads = [
1421
            {
1422
                "name": "my evc1",
1423
                "uni_a": {
1424
                    "interface_id": "00:00:00:00:00:00:00:01:1",
1425
                    "tag": {"tag_type": 1, "value": 80},
1426
                },
1427
                "uni_z": {
1428
                    "interface_id": "00:00:00:00:00:00:00:02:2",
1429
                    "tag": {"tag_type": 1, "value": 1},
1430
                },
1431
                "dynamic_backup_path": True,
1432
            },
1433
            {
1434
                "primary_path": [
1435
                    {
1436
                        "endpoint_a": {"id": "00:00:00:00:00:00:00:01:1"},
1437
                        "endpoint_b": {"id": "00:00:00:00:00:00:00:02:2"},
1438
                    }
1439
                ]
1440
            },
1441
            {
1442
                "sb_priority": 3
1443
            },
1444
            {
1445
                # It works only with 'enable' and not with 'enabled'
1446
                "enable": True
1447
            },
1448
            {
1449
                "name": "my evc1",
1450
                "active": True,
1451
                "enable": True,
1452
                "uni_a": {
1453
                    "interface_id": "00:00:00:00:00:00:00:01:1",
1454
                    "tag": {
1455
                        "tag_type": 1,
1456
                        "value": 80
1457
                    }
1458
                },
1459
                "uni_z": {
1460
                    "interface_id": "00:00:00:00:00:00:00:02:2",
1461
                    "tag": {
1462
                        "tag_type": 1,
1463
                        "value": 1
1464
                    }
1465
                },
1466
                "priority": 3,
1467
                "bandwidth": 1000,
1468
                "dynamic_backup_path": True
1469
            }
1470
        ]
1471
1472
        evc_as_dict_mock.return_value = payloads[0]
1473
        response = api.post(
1474
            f"{self.server_name_url}/v2/evc/",
1475
            data=json.dumps(payloads[0]),
1476
            content_type="application/json",
1477
        )
1478
        self.assertEqual(201, response.status_code)
1479
1480
        evc_deploy.reset_mock()
1481
        evc_as_dict_mock.return_value = payloads[1]
1482
        current_data = json.loads(response.data)
1483
        circuit_id = current_data["circuit_id"]
1484
        response = api.patch(
1485
            f"{self.server_name_url}/v2/evc/{circuit_id}",
1486
            data=json.dumps(payloads[1]),
1487
            content_type="application/json",
1488
        )
1489
        # evc_deploy.assert_called_once()
1490
        self.assertEqual(200, response.status_code)
1491
1492
        evc_deploy.reset_mock()
1493
        evc_as_dict_mock.return_value = payloads[2]
1494
        response = api.patch(
1495
            f"{self.server_name_url}/v2/evc/{circuit_id}",
1496
            data=json.dumps(payloads[2]),
1497
            content_type="application/json",
1498
        )
1499
        evc_deploy.assert_not_called()
1500
        self.assertEqual(200, response.status_code)
1501
1502
        evc_deploy.reset_mock()
1503
        evc_as_dict_mock.return_value = payloads[3]
1504
        response = api.patch(f'{self.server_name_url}/v2/evc/{circuit_id}',
1505
                             data=json.dumps(payloads[3]),
1506
                             content_type='application/json')
1507
        evc_deploy.assert_called_once()
1508
        self.assertEqual(200, response.status_code)
1509
1510
        evc_deploy.reset_mock()
1511
        response = api.patch(f'{self.server_name_url}/v2/evc/{circuit_id}',
1512
                             data='{"priority":5,}',
1513
                             content_type='application/json')
1514
        evc_deploy.assert_not_called()
1515
        self.assertEqual(400, response.status_code)
1516
1517
        evc_deploy.reset_mock()
1518
        response = api.patch(
1519
            f"{self.server_name_url}/v2/evc/{circuit_id}",
1520
            data=json.dumps(payloads[3]),
1521
            content_type="application/json",
1522
        )
1523
        evc_deploy.assert_called_once()
1524
        self.assertEqual(200, response.status_code)
1525
1526
        response = api.patch(
1527
            f"{self.server_name_url}/v2/evc/1234",
1528
            data=json.dumps(payloads[1]),
1529
            content_type="application/json",
1530
        )
1531
        current_data = json.loads(response.data)
1532
        expected_data = "circuit_id 1234 not found"
1533
        self.assertEqual(current_data["description"], expected_data)
1534
        self.assertEqual(404, response.status_code)
1535
1536
        api.delete(f"{self.server_name_url}/v2/evc/{circuit_id}")
1537
        evc_deploy.reset_mock()
1538
        response = api.patch(
1539
            f"{self.server_name_url}/v2/evc/{circuit_id}",
1540
            data=json.dumps(payloads[1]),
1541
            content_type="application/json",
1542
        )
1543
        evc_deploy.assert_not_called()
1544
        self.assertEqual(405, response.status_code)
1545
1546 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...
1547
    @patch("napps.kytos.mef_eline.scheduler.Scheduler.add")
1548
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
1549
    @patch("napps.kytos.mef_eline.controllers.ELineController.upsert_evc")
1550
    @patch("napps.kytos.mef_eline.models.evc.EVC._validate")
1551
    @patch("napps.kytos.mef_eline.main.EVC.as_dict")
1552
    def test_update_circuit_invalid_json(self, *args):
1553
        """Test update a circuit circuit."""
1554
        # pylint: disable=too-many-locals
1555
        (
1556
            evc_as_dict_mock,
1557
            validate_mock,
1558
            mongo_controller_upsert_mock,
1559
            uni_from_dict_mock,
1560
            sched_add_mock,
1561
            evc_deploy_mock,
1562
        ) = args
1563
1564
        validate_mock.return_value = True
1565
        mongo_controller_upsert_mock.return_value = True
1566
        sched_add_mock.return_value = True
1567
        evc_deploy_mock.return_value = True
1568
        uni1 = create_autospec(UNI)
1569
        uni2 = create_autospec(UNI)
1570
        uni1.interface = create_autospec(Interface)
1571
        uni2.interface = create_autospec(Interface)
1572
        uni1.interface.switch = "00:00:00:00:00:00:00:01"
1573
        uni2.interface.switch = "00:00:00:00:00:00:00:02"
1574
        uni_from_dict_mock.side_effect = [uni1, uni2, uni1, uni2]
1575
1576
        api = self.get_app_test_client(self.napp)
1577
        payload1 = {
1578
            "name": "my evc1",
1579
            "uni_a": {
1580
                "interface_id": "00:00:00:00:00:00:00:01:1",
1581
                "tag": {"tag_type": 1, "value": 80},
1582
            },
1583
            "uni_z": {
1584
                "interface_id": "00:00:00:00:00:00:00:02:2",
1585
                "tag": {"tag_type": 1, "value": 1},
1586
            },
1587
            "dynamic_backup_path": True,
1588
        }
1589
1590
        payload2 = {
1591
            "dynamic_backup_path": False,
1592
        }
1593
1594
        evc_as_dict_mock.return_value = payload1
1595
        response = api.post(
1596
            f"{self.server_name_url}/v2/evc/",
1597
            data=json.dumps(payload1),
1598
            content_type="application/json",
1599
        )
1600
        self.assertEqual(201, response.status_code)
1601
1602
        evc_as_dict_mock.return_value = payload2
1603
        current_data = json.loads(response.data)
1604
        circuit_id = current_data["circuit_id"]
1605
        response = api.patch(
1606
            f"{self.server_name_url}/v2/evc/{circuit_id}",
1607
            data=payload2,
1608
            content_type="application/json",
1609
        )
1610
        current_data = json.loads(response.data)
1611
        expected_data = "The request body is not a well-formed JSON."
1612
        self.assertEqual(current_data["description"], expected_data)
1613
        self.assertEqual(400, response.status_code)
1614
1615
    @patch("napps.kytos.mef_eline.models.evc.EVC.deploy")
1616
    @patch("napps.kytos.mef_eline.scheduler.Scheduler.add")
1617
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
1618
    @patch("napps.kytos.mef_eline.main.Main._link_from_dict")
1619
    @patch("napps.kytos.mef_eline.controllers.ELineController.upsert_evc")
1620
    @patch("napps.kytos.mef_eline.models.evc.EVC._validate")
1621
    @patch("napps.kytos.mef_eline.main.EVC.as_dict")
1622
    @patch("napps.kytos.mef_eline.models.path.Path.is_valid")
1623
    def test_update_circuit_invalid_path(self, *args):
1624
        """Test update a circuit circuit."""
1625
        # pylint: disable=too-many-locals
1626
        (
1627
            is_valid_mock,
1628
            evc_as_dict_mock,
1629
            validate_mock,
1630
            mongo_controller_upsert_mock,
1631
            link_from_dict_mock,
1632
            uni_from_dict_mock,
1633
            sched_add_mock,
1634
            evc_deploy_mock,
1635
        ) = args
1636
1637
        is_valid_mock.side_effect = InvalidPath("error")
1638
        validate_mock.return_value = True
1639
        mongo_controller_upsert_mock.return_value = True
1640
        sched_add_mock.return_value = True
1641
        evc_deploy_mock.return_value = True
1642
        link_from_dict_mock.return_value = 1
1643
        uni1 = create_autospec(UNI)
1644
        uni2 = create_autospec(UNI)
1645
        uni1.interface = create_autospec(Interface)
1646
        uni2.interface = create_autospec(Interface)
1647
        uni1.interface.switch = "00:00:00:00:00:00:00:01"
1648
        uni2.interface.switch = "00:00:00:00:00:00:00:02"
1649
        uni_from_dict_mock.side_effect = [uni1, uni2, uni1, uni2]
1650
1651
        api = self.get_app_test_client(self.napp)
1652
        payload1 = {
1653
            "name": "my evc1",
1654
            "uni_a": {
1655
                "interface_id": "00:00:00:00:00:00:00:01:1",
1656
                "tag": {"tag_type": 1, "value": 80},
1657
            },
1658
            "uni_z": {
1659
                "interface_id": "00:00:00:00:00:00:00:02:2",
1660
                "tag": {"tag_type": 1, "value": 1},
1661
            },
1662
            "dynamic_backup_path": True,
1663
        }
1664
1665
        payload2 = {
1666
            "primary_path": [
1667
                {
1668
                    "endpoint_a": {"id": "00:00:00:00:00:00:00:01:1"},
1669
                    "endpoint_b": {"id": "00:00:00:00:00:00:00:02:2"},
1670
                }
1671
            ]
1672
        }
1673
1674
        evc_as_dict_mock.return_value = payload1
1675
        response = api.post(
1676
            f"{self.server_name_url}/v2/evc/",
1677
            data=json.dumps(payload1),
1678
            content_type="application/json",
1679
        )
1680
        self.assertEqual(201, response.status_code)
1681
1682
        evc_as_dict_mock.return_value = payload2
1683
        current_data = json.loads(response.data)
1684
        circuit_id = current_data["circuit_id"]
1685
        response = api.patch(
1686
            f"{self.server_name_url}/v2/evc/{circuit_id}",
1687
            data=json.dumps(payload2),
1688
            content_type="application/json",
1689
        )
1690
        current_data = json.loads(response.data)
1691
        expected_data = "primary_path is not a valid path: error"
1692
        self.assertEqual(400, response.status_code)
1693
        self.assertEqual(current_data["description"], expected_data)
1694
1695
    def test_link_from_dict_non_existent_intf(self):
1696
        """Test _link_from_dict non existent intf."""
1697
        self.napp.controller.get_interface_by_id = MagicMock(return_value=None)
1698
        link_dict = {
1699
            "endpoint_a": {"id": "a"},
1700
            "endpoint_b": {"id": "b"}
1701
        }
1702
        with self.assertRaises(ValueError):
1703
            self.napp._link_from_dict(link_dict)
1704
1705
    def test_uni_from_dict_non_existent_intf(self):
1706
        """Test _link_from_dict non existent intf."""
1707
        self.napp.controller.get_interface_by_id = MagicMock(return_value=None)
1708
        uni_dict = {
1709
            "interface_id": "aaa",
1710
        }
1711
        with self.assertRaises(ValueError):
1712
            self.napp._uni_from_dict(uni_dict)
1713
1714 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...
1715
    @patch("napps.kytos.mef_eline.scheduler.Scheduler.add")
1716
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
1717
    @patch("napps.kytos.mef_eline.models.evc.EVC._validate")
1718
    @patch("napps.kytos.mef_eline.controllers.ELineController.upsert_evc")
1719
    def test_update_evc_no_json_mime(self, *args):
1720
        """Test update a circuit with wrong mimetype."""
1721
        # pylint: disable=too-many-locals
1722
        (
1723
            mongo_controller_upsert_mock,
1724
            validate_mock,
1725
            uni_from_dict_mock,
1726
            sched_add_mock,
1727
            evc_deploy_mock,
1728
        ) = args
1729
1730
        validate_mock.return_value = True
1731
        sched_add_mock.return_value = True
1732
        evc_deploy_mock.return_value = True
1733
        uni1 = create_autospec(UNI)
1734
        uni2 = create_autospec(UNI)
1735
        uni1.interface = create_autospec(Interface)
1736
        uni2.interface = create_autospec(Interface)
1737
        uni1.interface.switch = "00:00:00:00:00:00:00:01"
1738
        uni2.interface.switch = "00:00:00:00:00:00:00:02"
1739
        uni_from_dict_mock.side_effect = [uni1, uni2, uni1, uni2]
1740
        mongo_controller_upsert_mock.return_value = True
1741
1742
        api = self.get_app_test_client(self.napp)
1743
        payload1 = {
1744
            "name": "my evc1",
1745
            "uni_a": {
1746
                "interface_id": "00:00:00:00:00:00:00:01:1",
1747
                "tag": {"tag_type": 1, "value": 80},
1748
            },
1749
            "uni_z": {
1750
                "interface_id": "00:00:00:00:00:00:00:02:2",
1751
                "tag": {"tag_type": 1, "value": 1},
1752
            },
1753
            "dynamic_backup_path": True,
1754
        }
1755
1756
        payload2 = {"dynamic_backup_path": False}
1757
1758
        response = api.post(
1759
            f"{self.server_name_url}/v2/evc/",
1760
            data=json.dumps(payload1),
1761
            content_type="application/json",
1762
        )
1763
        self.assertEqual(201, response.status_code)
1764
1765
        current_data = json.loads(response.data)
1766
        circuit_id = current_data["circuit_id"]
1767
        response = api.patch(
1768
            f"{self.server_name_url}/v2/evc/{circuit_id}", data=payload2
1769
        )
1770
        current_data = json.loads(response.data)
1771
        expected_data = "The request body mimetype is not application/json."
1772
        self.assertEqual(current_data["description"], expected_data)
1773
        self.assertEqual(415, response.status_code)
1774
1775
    def test_delete_no_evc(self):
1776
        """Test delete when EVC does not exist."""
1777
        api = self.get_app_test_client(self.napp)
1778
        response = api.delete(f"{self.server_name_url}/v2/evc/123")
1779
        current_data = json.loads(response.data)
1780
        expected_data = "circuit_id 123 not found"
1781
        self.assertEqual(current_data["description"], expected_data)
1782
        self.assertEqual(404, response.status_code)
1783
1784
    @patch("napps.kytos.mef_eline.models.evc.EVC.remove_current_flows")
1785
    @patch("napps.kytos.mef_eline.models.evc.EVC.deploy")
1786
    @patch("napps.kytos.mef_eline.scheduler.Scheduler.add")
1787
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
1788
    @patch("napps.kytos.mef_eline.controllers.ELineController.upsert_evc")
1789
    @patch("napps.kytos.mef_eline.models.evc.EVC._validate")
1790
    @patch("napps.kytos.mef_eline.main.EVC.as_dict")
1791
    def test_delete_archived_evc(self, *args):
1792
        """Try to delete an archived EVC"""
1793
        # pylint: disable=too-many-locals
1794
        (
1795
            evc_as_dict_mock,
1796
            validate_mock,
1797
            mongo_controller_upsert_mock,
1798
            uni_from_dict_mock,
1799
            sched_add_mock,
1800
            evc_deploy_mock,
1801
            remove_current_flows_mock
1802
        ) = args
1803
1804
        validate_mock.return_value = True
1805
        mongo_controller_upsert_mock.return_value = True
1806
        sched_add_mock.return_value = True
1807
        evc_deploy_mock.return_value = True
1808
        remove_current_flows_mock.return_value = True
1809
        uni1 = create_autospec(UNI)
1810
        uni2 = create_autospec(UNI)
1811
        uni1.interface = create_autospec(Interface)
1812
        uni2.interface = create_autospec(Interface)
1813
        uni1.interface.switch = "00:00:00:00:00:00:00:01"
1814
        uni2.interface.switch = "00:00:00:00:00:00:00:02"
1815
        uni_from_dict_mock.side_effect = [uni1, uni2, uni1, uni2]
1816
1817
        api = self.get_app_test_client(self.napp)
1818
        payload1 = {
1819
            "name": "my evc1",
1820
            "uni_a": {
1821
                "interface_id": "00:00:00:00:00:00:00:01:1",
1822
                "tag": {"tag_type": 1, "value": 80},
1823
            },
1824
            "uni_z": {
1825
                "interface_id": "00:00:00:00:00:00:00:02:2",
1826
                "tag": {"tag_type": 1, "value": 1},
1827
            },
1828
            "dynamic_backup_path": True,
1829
        }
1830
1831
        evc_as_dict_mock.return_value = payload1
1832
        response = api.post(
1833
            f"{self.server_name_url}/v2/evc/",
1834
            data=json.dumps(payload1),
1835
            content_type="application/json",
1836
        )
1837
        self.assertEqual(201, response.status_code)
1838
1839
        current_data = json.loads(response.data)
1840
        circuit_id = current_data["circuit_id"]
1841
        response = api.delete(
1842
            f"{self.server_name_url}/v2/evc/{circuit_id}"
1843
        )
1844
        self.assertEqual(200, response.status_code)
1845
1846
        response = api.delete(
1847
            f"{self.server_name_url}/v2/evc/{circuit_id}"
1848
        )
1849
        current_data = json.loads(response.data)
1850
        expected_data = f"Circuit {circuit_id} already removed"
1851
        self.assertEqual(current_data["description"], expected_data)
1852
        self.assertEqual(404, response.status_code)
1853
1854
    def test_handle_link_up(self):
1855
        """Test handle_link_up method."""
1856
        evc_mock = create_autospec(EVC)
1857
        evc_mock.service_level, evc_mock.creation_time = 0, 1
1858
        evc_mock.is_enabled = MagicMock(side_effect=[True, False, True])
1859
        evc_mock.lock = MagicMock()
1860
        type(evc_mock).archived = PropertyMock(
1861
            side_effect=[True, False, False]
1862
        )
1863
        evcs = [evc_mock, evc_mock, evc_mock]
1864
        event = KytosEvent(name="test", content={"link": "abc"})
1865
        self.napp.circuits = dict(zip(["1", "2", "3"], evcs))
1866
        self.napp.handle_link_up(event)
1867
        evc_mock.handle_link_up.assert_called_once_with("abc")
1868
1869
    @patch("time.sleep", return_value=None)
1870
    @patch("napps.kytos.mef_eline.main.settings")
1871
    @patch("napps.kytos.mef_eline.main.emit_event")
1872
    def test_handle_link_down(self, emit_event_mock, settings_mock, _):
1873
        """Test handle_link_down method."""
1874
        uni = create_autospec(UNI)
1875
        evc1 = MagicMock(id="1", service_level=0, creation_time=1,
1876
                         metadata="mock", _active="true", _enabled="true",
1877
                         uni_a=uni, uni_z=uni)
1878
        evc1.name = "name"
1879
        evc1.is_affected_by_link.return_value = True
1880
        evc1.handle_link_down.return_value = True
1881
        evc1.failover_path = None
1882
        evc2 = MagicMock(id="2", service_level=6, creation_time=1)
1883
        evc2.is_affected_by_link.return_value = False
1884
        evc3 = MagicMock(id="3", service_level=5, creation_time=1,
1885
                         metadata="mock", _active="true", _enabled="true",
1886
                         uni_a=uni, uni_z=uni)
1887
        evc3.name = "name"
1888
        evc3.is_affected_by_link.return_value = True
1889
        evc3.handle_link_down.return_value = True
1890
        evc3.failover_path = None
1891
        evc4 = MagicMock(id="4", service_level=4, creation_time=1,
1892
                         metadata="mock", _active="true", _enabled="true",
1893
                         uni_a=uni, uni_z=uni)
1894
        evc4.name = "name"
1895
        evc4.is_affected_by_link.return_value = True
1896
        evc4.is_failover_path_affected_by_link.return_value = False
1897
        evc4.failover_path = ["2"]
1898
        evc4.get_failover_flows.return_value = {
1899
            "2": ["flow1", "flow2"],
1900
            "3": ["flow3", "flow4", "flow5", "flow6"],
1901
        }
1902
        evc5 = MagicMock(id="5", service_level=7, creation_time=1)
1903
        evc5.is_affected_by_link.return_value = True
1904
        evc5.is_failover_path_affected_by_link.return_value = False
1905
        evc5.failover_path = ["3"]
1906
        evc5.get_failover_flows.return_value = {
1907
            "4": ["flow7", "flow8"],
1908
            "5": ["flow9", "flow10"],
1909
        }
1910
        link = MagicMock(id="123")
1911
        event = KytosEvent(name="test", content={"link": link})
1912
        self.napp.circuits = {"1": evc1, "2": evc2, "3": evc3, "4": evc4,
1913
                              "5": evc5}
1914
        settings_mock.BATCH_SIZE = 2
1915
        self.napp.handle_link_down(event)
1916
1917
        assert evc5.service_level > evc4.service_level
1918
        # evc5 batched flows should be sent first
1919
        emit_event_mock.assert_has_calls([
1920
            call(
1921
                self.napp.controller,
1922
                context="kytos.flow_manager",
1923
                name="flows.install",
1924
                content={
1925
                    "dpid": "4",
1926
                    "flow_dict": {"flows": ["flow7", "flow8"]},
1927
                }
1928
            ),
1929
            call(
1930
                self.napp.controller,
1931
                context="kytos.flow_manager",
1932
                name="flows.install",
1933
                content={
1934
                    "dpid": "5",
1935
                    "flow_dict": {"flows": ["flow9", "flow10"]},
1936
                }
1937
            ),
1938
            call(
1939
                self.napp.controller,
1940
                context="kytos.flow_manager",
1941
                name="flows.install",
1942
                content={
1943
                    "dpid": "2",
1944
                    "flow_dict": {"flows": ["flow1", "flow2"]},
1945
                }
1946
            ),
1947
            call(
1948
                self.napp.controller,
1949
                context="kytos.flow_manager",
1950
                name="flows.install",
1951
                content={
1952
                    "dpid": "3",
1953
                    "flow_dict": {"flows": ["flow3", "flow4"]},
1954
                }
1955
            ),
1956
            call(
1957
                self.napp.controller,
1958
                context="kytos.flow_manager",
1959
                name="flows.install",
1960
                content={
1961
                    "dpid": "3",
1962
                    "flow_dict": {"flows": ["flow5", "flow6"]},
1963
                }
1964
            ),
1965
        ])
1966
        event_name = "evc_affected_by_link_down"
1967
        assert evc3.service_level > evc1.service_level
1968
        # evc3 should be handled before evc1
1969
        emit_event_mock.assert_has_calls([
1970
            call(self.napp.controller, event_name, content={
1971
                "link_id": "123",
1972
                "evc_id": "3",
1973
                "name": "name",
1974
                "metadata": "mock",
1975
                "active": "true",
1976
                "enabled": "true",
1977
                "uni_a": uni.as_dict(),
1978
                "uni_z": uni.as_dict(),
1979
            }),
1980
            call(self.napp.controller, event_name, content={
1981
                "link_id": "123",
1982
                "evc_id": "1",
1983
                "name": "name",
1984
                "metadata": "mock",
1985
                "active": "true",
1986
                "enabled": "true",
1987
                "uni_a": uni.as_dict(),
1988
                "uni_z": uni.as_dict(),
1989
            }),
1990
        ])
1991
        evc4.sync.assert_called_once()
1992
        event_name = "redeployed_link_down"
1993
        emit_event_mock.assert_has_calls([
1994
            call(self.napp.controller, event_name, content={
1995
                "evc_id": "4",
1996
                "name": "name",
1997
                "metadata": "mock",
1998
                "active": "true",
1999
                "enabled": "true",
2000
                "uni_a": uni.as_dict(),
2001
                "uni_z": uni.as_dict(),
2002
            }),
2003
        ])
2004
2005
    @patch("napps.kytos.mef_eline.main.emit_event")
2006
    def test_handle_evc_affected_by_link_down(self, emit_event_mock):
2007
        """Test handle_evc_affected_by_link_down method."""
2008
        uni = create_autospec(UNI)
2009
        evc1 = MagicMock(
2010
            id="1",
2011
            metadata="data_mocked",
2012
            _active="true",
2013
            _enabled="false",
2014
            uni_a=uni,
2015
            uni_z=uni,
2016
        )
2017
        evc1.name = "name_mocked"
2018
        evc1.handle_link_down.return_value = True
2019
        evc2 = MagicMock(
2020
            id="2",
2021
            metadata="mocked_data",
2022
            _active="false",
2023
            _enabled="true",
2024
            uni_a=uni,
2025
            uni_z=uni,
2026
        )
2027
        evc2.name = "mocked_name"
2028
        evc2.handle_link_down.return_value = False
2029
        self.napp.circuits = {"1": evc1, "2": evc2}
2030
2031
        event = KytosEvent(name="e1", content={
2032
            "evc_id": "3",
2033
            "link_id": "1",
2034
        })
2035
        self.napp.handle_evc_affected_by_link_down(event)
2036
        emit_event_mock.assert_not_called()
2037
        event.content["evc_id"] = "1"
2038
        self.napp.handle_evc_affected_by_link_down(event)
2039
        emit_event_mock.assert_called_with(
2040
            self.napp.controller, "redeployed_link_down", content={
2041
                "evc_id": "1",
2042
                "name": "name_mocked",
2043
                "metadata": "data_mocked",
2044
                "active": "true",
2045
                "enabled": "false",
2046
                "uni_a": uni.as_dict(),
2047
                "uni_z": uni.as_dict(),
2048
            }
2049
        )
2050
2051
        event.content["evc_id"] = "2"
2052
        self.napp.handle_evc_affected_by_link_down(event)
2053
        emit_event_mock.assert_called_with(
2054
            self.napp.controller, "error_redeploy_link_down", content={
2055
                "evc_id": "2",
2056
                "name": "mocked_name",
2057
                "metadata": "mocked_data",
2058
                "active": "false",
2059
                "enabled": "true",
2060
                "uni_a": uni.as_dict(),
2061
                "uni_z": uni.as_dict(),
2062
            }
2063
        )
2064
2065
    def test_handle_evc_deployed(self):
2066
        """Test handle_evc_deployed method."""
2067
        evc = create_autospec(EVC, id="1")
2068
        evc.lock = MagicMock()
2069
        self.napp.circuits = {"1": evc}
2070
2071
        event = KytosEvent(name="e1", content={"evc_id": "2"})
2072
        self.napp.handle_evc_deployed(event)
2073
        evc.setup_failover_path.assert_not_called()
2074
2075
        event.content["evc_id"] = "1"
2076
        self.napp.handle_evc_deployed(event)
2077
        evc.setup_failover_path.assert_called()
2078
2079
    def test_add_metadata(self):
2080
        """Test method to add metadata"""
2081
        evc_mock = create_autospec(EVC)
2082
        evc_mock.metadata = {}
2083
        evc_mock.id = 1234
2084
        self.napp.circuits = {"1234": evc_mock}
2085
2086
        api = self.get_app_test_client(self.napp)
2087
        payload = {"metadata1": 1, "metadata2": 2}
2088
        response = api.post(
2089
            f"{self.server_name_url}/v2/evc/1234/metadata",
2090
            data=json.dumps(payload),
2091
            content_type="application/json",
2092
        )
2093
2094
        self.assertEqual(response.status_code, 201)
2095
        evc_mock.extend_metadata.assert_called_with(payload)
2096
2097
    def test_add_metadata_malformed_json(self):
2098
        """Test method to add metadata with a malformed json"""
2099
        api = self.get_app_test_client(self.napp)
2100
        payload = '{"metadata1": 1, "metadata2": 2,}'
2101
        response = api.post(
2102
            f"{self.server_name_url}/v2/evc/1234/metadata",
2103
            data=payload,
2104
            content_type="application/json"
2105
        )
2106
2107
        self.assertEqual(response.status_code, 400)
2108
        self.assertEqual(
2109
            response.json["description"],
2110
            "The request body is not a well-formed JSON."
2111
        )
2112
2113
    def test_add_metadata_no_body(self):
2114
        """Test method to add metadata with no body"""
2115
        api = self.get_app_test_client(self.napp)
2116
        response = api.post(
2117
            f"{self.server_name_url}/v2/evc/1234/metadata"
2118
        )
2119
        self.assertEqual(response.status_code, 400)
2120
        self.assertEqual(
2121
            response.json["description"],
2122
            "The request body is empty."
2123
        )
2124
2125
    def test_add_metadata_no_evc(self):
2126
        """Test method to add metadata with no evc"""
2127
        api = self.get_app_test_client(self.napp)
2128
        payload = {"metadata1": 1, "metadata2": 2}
2129
        response = api.post(
2130
            f"{self.server_name_url}/v2/evc/1234/metadata",
2131
            data=json.dumps(payload),
2132
            content_type="application/json",
2133
        )
2134
        self.assertEqual(response.status_code, 404)
2135
        self.assertEqual(
2136
            response.json["description"],
2137
            "circuit_id 1234 not found."
2138
        )
2139
2140
    def test_add_metadata_wrong_content_type(self):
2141
        """Test method to add metadata with wrong content type"""
2142
        api = self.get_app_test_client(self.napp)
2143
        payload = {"metadata1": 1, "metadata2": 2}
2144
        response = api.post(
2145
            f"{self.server_name_url}/v2/evc/1234/metadata",
2146
            data=json.dumps(payload),
2147
            content_type="application/xml",
2148
        )
2149
        self.assertEqual(response.status_code, 415)
2150
        self.assertEqual(
2151
            response.json["description"],
2152
            "The content type must be application/json "
2153
            "(received application/xml)."
2154
        )
2155
2156
    def test_get_metadata(self):
2157
        """Test method to get metadata"""
2158
        evc_mock = create_autospec(EVC)
2159
        evc_mock.metadata = {'metadata1': 1, 'metadata2': 2}
2160
        evc_mock.id = 1234
2161
        self.napp.circuits = {"1234": evc_mock}
2162
2163
        api = self.get_app_test_client(self.napp)
2164
        response = api.get(
2165
            f"{self.server_name_url}/v2/evc/1234/metadata",
2166
        )
2167
        self.assertEqual(response.status_code, 200)
2168
        self.assertEqual(response.json, {"metadata": evc_mock.metadata})
2169
2170
    def test_delete_metadata(self):
2171
        """Test method to delete metadata"""
2172
        evc_mock = create_autospec(EVC)
2173
        evc_mock.metadata = {'metadata1': 1, 'metadata2': 2}
2174
        evc_mock.id = 1234
2175
        self.napp.circuits = {"1234": evc_mock}
2176
2177
        api = self.get_app_test_client(self.napp)
2178
        response = api.delete(
2179
            f"{self.server_name_url}/v2/evc/1234/metadata/metadata1",
2180
        )
2181
        self.assertEqual(response.status_code, 200)
2182
2183
    def test_delete_metadata_no_evc(self):
2184
        """Test method to delete metadata with no evc"""
2185
        api = self.get_app_test_client(self.napp)
2186
        response = api.delete(
2187
            f"{self.server_name_url}/v2/evc/1234/metadata/metadata1",
2188
        )
2189
        self.assertEqual(response.status_code, 404)
2190
        self.assertEqual(
2191
            response.json["description"],
2192
            "circuit_id 1234 not found."
2193
        )
2194
2195
    @patch('napps.kytos.mef_eline.main.Main._load_evc')
2196
    def test_load_all_evcs(self, load_evc_mock):
2197
        """Test load_evcs method"""
2198
        mock_circuits = {
2199
            'circuits': {
2200
                1: 'circuit_1',
2201
                2: 'circuit_2',
2202
                3: 'circuit_3',
2203
                4: 'circuit_4'
2204
            }
2205
        }
2206
        self.napp.mongo_controller.get_circuits.return_value = mock_circuits
2207
        self.napp.circuits = {2: 'circuit_2', 3: 'circuit_3'}
2208
        self.napp.load_all_evcs()
2209
        load_evc_mock.assert_has_calls([call('circuit_1'), call('circuit_4')])
2210
2211
    @patch('napps.kytos.mef_eline.main.Main._evc_from_dict')
2212
    def test_load_evc(self, evc_from_dict_mock):
2213
        """Test _load_evc method"""
2214
        # pylint: disable=protected-access
2215
        # case 1: early return with ValueError exception
2216
        evc_from_dict_mock.side_effect = ValueError("err")
2217
        evc_dict = MagicMock()
2218
        self.assertEqual(self.napp._load_evc(evc_dict), None)
2219
2220
        # case2: archived evc
2221
        evc = MagicMock()
2222
        evc.archived = True
2223
        evc_from_dict_mock.side_effect = None
2224
        evc_from_dict_mock.return_value = evc
2225
        self.assertEqual(self.napp._load_evc(evc_dict), None)
2226
2227
        # case3: success creating
2228
        evc.archived = False
2229
        evc.id = 1
2230
        self.napp.sched = MagicMock()
2231
2232
        result = self.napp._load_evc(evc_dict)
2233
        self.assertEqual(result, evc)
2234
        evc.deactivate.assert_called()
2235
        evc.sync.assert_called()
2236
        self.napp.sched.add.assert_called_with(evc)
2237
        self.assertEqual(self.napp.circuits[1], evc)
2238
2239
    def test_handle_flow_mod_error(self):
2240
        """Test handle_flow_mod_error method"""
2241
        flow = MagicMock()
2242
        flow.cookie = 0xaa00000000000011
2243
        event = MagicMock()
2244
        event.content = {'flow': flow, 'error_command': 'add'}
2245
        evc = create_autospec(EVC)
2246
        evc.remove_current_flows = MagicMock()
2247
        self.napp.circuits = {"00000000000011": evc}
2248
        self.napp.handle_flow_mod_error(event)
2249
        evc.remove_current_flows.assert_called_once()
2250
2251
    @patch("kytos.core.Controller.get_interface_by_id")
2252
    def test_uni_from_dict(self, _get_interface_by_id_mock):
2253
        """Test _uni_from_dict method."""
2254
        # pylint: disable=protected-access
2255
        # case1: early return on empty dict
2256
        self.assertEqual(self.napp._uni_from_dict(None), False)
2257
2258
        # case2: invalid interface raises ValueError
2259
        _get_interface_by_id_mock.return_value = None
2260
        uni_dict = {
2261
            "interface_id": "00:01:1",
2262
            "tag": {"tag_type": 1, "value": 81},
2263
        }
2264
        with self.assertRaises(ValueError):
2265
            self.napp._uni_from_dict(uni_dict)
2266
2267
        # case3: success creation
2268
        uni_mock = get_uni_mocked(switch_id="00:01")
2269
        _get_interface_by_id_mock.return_value = uni_mock.interface
2270
        uni = self.napp._uni_from_dict(uni_dict)
2271
        self.assertEqual(uni, uni_mock)
2272
2273
        # case4: success creation without tag
2274
        uni_mock.user_tag = None
2275
        del uni_dict["tag"]
2276
        uni = self.napp._uni_from_dict(uni_dict)
2277
        self.assertEqual(uni, uni_mock)
2278
2279
    def test_handle_flow_delete(self):
2280
        """Test handle_flow_delete method"""
2281
        flow = MagicMock()
2282
        flow.cookie = 0xaa00000000000011
2283
        event = MagicMock()
2284
        event.content = {'flow': flow}
2285
        evc = create_autospec(EVC)
2286
        evc.set_flow_removed_at = MagicMock()
2287
        self.napp.circuits = {"00000000000011": evc}
2288
        self.napp.handle_flow_delete(event)
2289
        evc.set_flow_removed_at.assert_called_once()
2290
2291
    def test_add_bulk_metadata(self):
2292
        """Test add_bulk_metadata method"""
2293
        evc_mock = create_autospec(EVC)
2294
        evc_mock.id = 1234
2295
        self.napp.circuits = {"1234": evc_mock}
2296
        api = self.get_app_test_client(self.napp)
2297
        payload = {
2298
            "circuit_ids": ["1234"],
2299
            "metadata1": 1,
2300
            "metadata2": 2
2301
        }
2302
        response = api.post(
2303
            f"{self.server_name_url}/v2/evc/metadata",
2304
            data=json.dumps(payload),
2305
            content_type="application/json",
2306
        )
2307
        self.assertEqual(response.status_code, 201)
2308
        args = self.napp.mongo_controller.update_bulk_evc.call_args[0]
2309
        ids = payload.pop("circuit_ids")
2310
        expected = {f"metadata.{k}": v for k, v in payload.items()}
2311
        self.assertEqual(args[0], ids)
2312
        self.assertEqual(args[1], {"$set": expected})
2313
        calls = self.napp.mongo_controller.update_bulk_evc.call_count
2314
        self.assertEqual(calls, 1)
2315
        evc_mock.extend_metadata.assert_called_with(payload)
2316
2317
    def test_add_bulk_metadata_no_id(self):
2318
        """Test add_bulk_metadata with unknown evc id"""
2319
        evc_mock = create_autospec(EVC)
2320
        evc_mock.id = 1234
2321
        self.napp.circuits = {"1234": evc_mock}
2322
        api = self.get_app_test_client(self.napp)
2323
        payload = {
2324
            "circuit_ids": ["1234", "4567"]
2325
        }
2326
        response = api.post(
2327
            f"{self.server_name_url}/v2/evc/metadata",
2328
            data=json.dumps(payload),
2329
            content_type="application/json",
2330
        )
2331
        self.assertEqual(response.status_code, 404)
2332
2333
    def test_add_bulk_metadata_no_circuits(self):
2334
        """Test add_bulk_metadata without circuit_ids"""
2335
        evc_mock = create_autospec(EVC)
2336
        evc_mock.id = 1234
2337
        self.napp.circuits = {"1234": evc_mock}
2338
        api = self.get_app_test_client(self.napp)
2339
        payload = {
2340
            "metadata": "data"
2341
        }
2342
        response = api.post(
2343
            f"{self.server_name_url}/v2/evc/metadata",
2344
            data=json.dumps(payload),
2345
            content_type="application/json",
2346
        )
2347
        self.assertEqual(response.status_code, 400)
2348
2349
    def test_delete_bulk_metadata(self):
2350
        """Test delete_metadata method"""
2351
        evc_mock = create_autospec(EVC)
2352
        evc_mock.id = 1234
2353
        self.napp.circuits = {"1234": evc_mock}
2354
        api = self.get_app_test_client(self.napp)
2355
        payload = {
2356
            "circuit_ids": ["1234"],
2357
            "metadata1": 1,
2358
            "metadata2": 2
2359
        }
2360
        response = api.delete(
2361
            f"{self.server_name_url}/v2/evc/metadata/metadata1",
2362
            data=json.dumps(payload),
2363
            content_type="application/json",
2364
        )
2365
        self.assertEqual(response.status_code, 200)
2366
        args = self.napp.mongo_controller.update_bulk_evc.call_args[0]
2367
        ids = payload.pop("circuit_ids")
2368
        expected = {"metadata.metadata1": ""}
2369
        self.assertEqual(args[0], ids)
2370
        self.assertEqual(args[1], {"$unset": expected})
2371
        calls = self.napp.mongo_controller.update_bulk_evc.call_count
2372
        self.assertEqual(calls, 1)
2373
        self.assertEqual(evc_mock.remove_metadata.call_count, 1)
2374