Passed
Pull Request — master (#298)
by
unknown
04:09
created

TestMain.test_add_bulk_metadata()   A

Complexity

Conditions 1

Size

Total Lines 25
Code Lines 22

Duplication

Lines 25
Ratio 100 %

Importance

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