Passed
Pull Request — master (#473)
by Vinicius
03:59
created

TestMain.test_circuit_with_valid_id()   A

Complexity

Conditions 1

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

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