Passed
Pull Request — master (#491)
by Aldo
04:10
created

TestMain.test_load_evc()   A

Complexity

Conditions 1

Size

Total Lines 29
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 1

Importance

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