Passed
Push — master ( 71c496...12cb0f )
by Aldo
03:10 queued 15s
created

build.tests.unit.test_main.test_on_table_enabled()   A

Complexity

Conditions 1

Size

Total Lines 31
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 1

Importance

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