TestMain.test_create_circuit_case_5()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 38
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 1.0156

Importance

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