Passed
Pull Request — master (#224)
by
unknown
03:21
created

TestMain.test_evc_from_dict()   A

Complexity

Conditions 1

Size

Total Lines 33
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

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