Passed
Pull Request — master (#320)
by Vinicius
07:35 queued 03:38
created

build.tests.unit.test_main   F

Complexity

Total Complexity 77

Size/Duplication

Total Lines 2137
Duplicated Lines 9.17 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 1554
dl 196
loc 2137
ccs 971
cts 971
cp 1
rs 1.04
c 0
b 0
f 0
wmc 77

70 Methods

Rating   Name   Duplication   Size   Complexity  
A TestMain.test_get_metadata() 0 12 1
B TestMain.test_update_circuit_invalid_json() 64 64 1
B TestMain._add_mongodb_schedule_data() 0 52 1
A TestMain.test_list_no_circuits_stored() 0 9 1
B TestMain.test_create_schedule() 0 57 1
A TestMain.test_circuit_with_invalid_id() 0 7 1
A TestMain._uni_from_dict_side_effect() 0 5 1
A TestMain.test_uni_from_dict() 0 27 2
B TestMain.test_handle_link_down() 0 133 1
B TestMain.test_update_schedule() 0 64 1
A TestMain.test_circuit_with_valid_id() 0 9 1
A TestMain.test_execute_consistency_wait_for() 0 33 1
A TestMain.test_update_no_schedule() 0 10 1
A TestMain.test_add_metadata_malformed_json() 0 11 1
A TestMain.test_get_specific_schedules_from_mongodb_not_found() 0 13 1
A TestMain.test_create_a_circuit_case_4() 0 36 1
A TestMain.setup_method() 0 18 2
B TestMain.test_delete_archived_evc() 0 67 1
B TestMain.test_delete_schedule() 0 55 1
B TestMain.test_update_circuit() 0 152 1
A TestMain.test_list_schedules_from_mongodb() 0 52 1
A TestMain.test_add_bulk_metadata_no_circuits() 0 13 1
A TestMain.test_evc_from_dict_paths() 0 49 1
A TestMain.test_delete_metadata() 0 11 1
B TestMain.test_create_circuit_already_enabled() 0 58 1
A TestMain.test_uni_from_dict_non_existent_intf() 0 8 2
B TestMain.test_create_a_circuit_case_1() 0 88 1
A TestMain.test_create_a_circuit_case_3() 0 12 1
A TestMain.test_link_from_dict_non_existent_intf() 0 9 2
A TestMain.test_list_with_archived_circuits_all() 0 14 1
B TestMain.test_execute_consistency() 0 44 1
A TestMain.test_list_without_circuits() 0 8 1
A TestMain.test_delete_schedule_archived() 0 39 1
A TestMain.test_add_bulk_metadata_no_id() 0 13 1
A TestMain.test_list_schedules__no_data_stored() 0 9 1
B TestMain.test_update_circuit_invalid_path() 0 76 1
A TestMain.test_add_metadata_no_body() 0 8 1
A TestMain.test_create_circuit_case_5() 0 31 1
A TestMain.test_handle_link_up() 0 14 1
A TestMain.test_redeploy_evc_deleted() 0 8 1
A TestMain.test_add_bulk_metadata() 23 23 1
A TestMain.test_get_event_listeners() 0 12 2
A TestMain.test_execute() 0 19 1
A TestMain.test_create_a_circuit_invalid_queue_id() 0 22 1
A TestMain.test_get_specific_schedule_from_mongodb() 0 23 1
A TestMain.test_add_metadata() 0 15 1
A TestMain.test_add_metadata_no_evc() 0 10 1
A TestMain.test_delete_schedule_not_found() 0 7 1
A TestMain.test_handle_evc_deployed() 0 13 1
A TestMain.test_delete_no_evc() 0 8 1
A TestMain.test_list_with_circuits_stored() 14 14 1
A TestMain.test_update_schedule_archived() 0 45 1
A TestMain.test_add_metadata_wrong_content_type() 0 10 1
A TestMain.test_evc_from_dict() 0 33 1
A TestMain.test_list_with_archived_circuits_archived() 17 17 1
A TestMain.test_get_evcs_by_svc_level() 0 19 3
A TestMain.test_redeploy_evc_disabled() 0 8 1
A TestMain.test_evc_from_dict_links() 0 53 1
B TestMain.test_update_evc_no_json_mime() 57 57 1
A TestMain.test_create_a_circuit_case_2() 0 8 1
A TestMain.test_delete_bulk_metadata() 21 21 1
A TestMain.test_get_circuit_not_found() 0 6 1
A TestMain.test_load_all_evcs() 0 15 1
A TestMain.test_redeploy_evc() 0 8 1
A TestMain.test_handle_flow_mod_error() 0 11 1
B TestMain.test_create_schedule_invalid_request() 0 69 1
A TestMain.test_load_evc() 0 27 1
A TestMain.test_delete_metadata_no_evc() 0 8 1
B TestMain.test_handle_evc_affected_by_link_down() 0 57 1
A TestMain.test_handle_flow_delete() 0 11 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like build.tests.unit.test_main often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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