Test Failed
Pull Request — master (#320)
by Vinicius
04:00
created

build.tests.unit.test_main.TestMain.setup_method()   A

Complexity

Conditions 2

Size

Total Lines 18
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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