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

TestMain.test_update_circuit()   B

Complexity

Conditions 1

Size

Total Lines 135
Code Lines 98

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 45
CRAP Score 1.006

Importance

Changes 0
Metric Value
cc 1
eloc 98
nop 12
dl 0
loc 135
ccs 45
cts 55
cp 0.8182
crap 1.006
rs 7.0654
c 0
b 0
f 0

How to fix   Long Method    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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