Passed
Pull Request — master (#90)
by Antonio
01:03
created

TestMain.test_circuit_with_valid_id()   A

Complexity

Conditions 1

Size

Total Lines 11
Code Lines 9

Duplication

Lines 11
Ratio 100 %

Importance

Changes 0
Metric Value
cc 1
eloc 9
nop 2
dl 11
loc 11
rs 9.95
c 0
b 0
f 0
1
"""Module to test the main napp file."""
2
import json
3
from unittest import TestCase
4
from unittest.mock import MagicMock, PropertyMock, call, create_autospec, patch
5
6
from kytos.core.events import KytosEvent
7
from kytos.core.interface import UNI, Interface
8
from napps.kytos.mef_eline.exceptions import InvalidPath
9
from napps.kytos.mef_eline.models import EVC
10
from napps.kytos.mef_eline.tests.helpers import (
11
    get_controller_mock,
12
    get_uni_mocked,
13
)
14
15
16
# pylint: disable=too-many-public-methods, too-many-lines
17
class TestMain(TestCase):
18
    """Test the Main class."""
19
20
    def setUp(self):
21
        """Execute steps before each tests.
22
23
        Set the server_name_url_url from kytos/mef_eline
24
        """
25
        self.server_name_url = "http://localhost:8181/api/kytos/mef_eline"
26
27
        # The decorator run_on_thread is patched, so methods that listen
28
        # for events do not run on threads while tested.
29
        # Decorators have to be patched before the methods that are
30
        # decorated with them are imported.
31
        patch("kytos.core.helpers.run_on_thread", lambda x: x).start()
32
        # pylint: disable=import-outside-toplevel
33
        from napps.kytos.mef_eline.main import Main
34
35
        self.addCleanup(patch.stopall)
36
        self.napp = Main(get_controller_mock())
37
38
    def test_get_event_listeners(self):
39
        """Verify all event listeners registered."""
40
        expected_events = [
41
            "kytos/core.shutdown",
42
            "kytos/core.shutdown.kytos/mef_eline",
43
            "kytos/topology.link_up",
44
            "kytos/topology.link_down",
45
        ]
46
        actual_events = self.napp.listeners()
47
48
        for _event in expected_events:
49
            self.assertIn(_event, actual_events, _event)
50
51
    def test_verify_api_urls(self):
52
        """Verify all APIs registered."""
53
        expected_urls = [
54
            ({}, {"POST", "OPTIONS"}, "/api/kytos/mef_eline/v2/evc/"),
55
            ({}, {"OPTIONS", "HEAD", "GET"}, "/api/kytos/mef_eline/v2/evc/"),
56
            (
57
                {"circuit_id": "[circuit_id]"},
58
                {"OPTIONS", "DELETE"},
59
                "/api/kytos/mef_eline/v2/evc/<circuit_id>",
60
            ),
61
            (
62
                {"circuit_id": "[circuit_id]"},
63
                {"OPTIONS", "HEAD", "GET"},
64
                "/api/kytos/mef_eline/v2/evc/<circuit_id>",
65
            ),
66
            (
67
                {"circuit_id": "[circuit_id]"},
68
                {"OPTIONS", "PATCH"},
69
                "/api/kytos/mef_eline/v2/evc/<circuit_id>",
70
            ),
71
            (
72
                {"circuit_id": "[circuit_id]"},
73
                {"OPTIONS", "HEAD", "GET"},
74
                "/api/kytos/mef_eline/v2/evc/<circuit_id>/metadata",
75
            ),
76
            (
77
                {"circuit_id": "[circuit_id]"},
78
                {"OPTIONS", "POST"},
79
                "/api/kytos/mef_eline/v2/evc/<circuit_id>/metadata",
80
            ),
81
            (
82
                {"circuit_id": "[circuit_id]", "key": "[key]"},
83
                {"OPTIONS", "DELETE"},
84
                "/api/kytos/mef_eline/v2/evc/<circuit_id>/metadata/<key>",
85
            ),
86
            (
87
                {"circuit_id": "[circuit_id]"},
88
                {"OPTIONS", "PATCH"},
89
                "/api/kytos/mef_eline/v2/evc/<circuit_id>/redeploy",
90
            ),
91
            (
92
                {},
93
                {"OPTIONS", "GET", "HEAD"},
94
                "/api/kytos/mef_eline/v2/evc/schedule",
95
            ),
96
            ({}, {"POST", "OPTIONS"}, "/api/kytos/mef_eline/v2/evc/schedule/"),
97
            (
98
                {"schedule_id": "[schedule_id]"},
99
                {"OPTIONS", "DELETE"},
100
                "/api/kytos/mef_eline/v2/evc/schedule/<schedule_id>",
101
            ),
102
            (
103
                {"schedule_id": "[schedule_id]"},
104
                {"OPTIONS", "PATCH"},
105
                "/api/kytos/mef_eline/v2/evc/schedule/<schedule_id>",
106
            ),
107
        ]
108
        urls = self.get_napp_urls(self.napp)
109
        self.assertEqual(len(expected_urls), len(urls))
110
111
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
112
    @patch("napps.kytos.mef_eline.models.evc.EVCBase._validate")
113
    def test_evc_from_dict(self, _validate_mock, uni_from_dict_mock):
114
        """
115
        Test the helper method that create an EVN from dict.
116
117
        Verify object creation with circuit data and schedule data.
118
        """
119
        _validate_mock.return_value = True
120
        uni_from_dict_mock.side_effect = ["uni_a", "uni_z"]
121
        payload = {
122
            "name": "my evc1",
123
            "uni_a": {
124
                "interface_id": "00:00:00:00:00:00:00:01:1",
125
                "tag": {"tag_type": 1, "value": 80},
126
            },
127
            "uni_z": {
128
                "interface_id": "00:00:00:00:00:00:00:02:2",
129
                "tag": {"tag_type": 1, "value": 1},
130
            },
131
            "circuit_scheduler": [
132
                {"frequency": "* * * * *", "action": "create"}
133
            ],
134
            "queue_id": 5,
135
        }
136
        # pylint: disable=protected-access
137
        evc_response = self.napp._evc_from_dict(payload)
138
        self.assertIsNotNone(evc_response)
139
        self.assertIsNotNone(evc_response.uni_a)
140
        self.assertIsNotNone(evc_response.uni_z)
141
        self.assertIsNotNone(evc_response.circuit_scheduler)
142
        self.assertIsNotNone(evc_response.name)
143
        self.assertIsNotNone(evc_response.queue_id)
144
145
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
146
    @patch("napps.kytos.mef_eline.models.evc.EVCBase._validate")
147
    @patch("kytos.core.Controller.get_interface_by_id")
148
    def test_evc_from_dict_paths(
149
        self, _get_interface_by_id_mock, _validate_mock, uni_from_dict_mock
150
    ):
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
        _get_interface_by_id_mock.return_value = True
157
        _validate_mock.return_value = True
158
        uni_from_dict_mock.side_effect = ["uni_a", "uni_z"]
159
        payload = {
160
            "name": "my evc1",
161
            "uni_a": {
162
                "interface_id": "00:00:00:00:00:00:00:01:1",
163
                "tag": {"tag_type": 1, "value": 80},
164
            },
165
            "uni_z": {
166
                "interface_id": "00:00:00:00:00:00:00:02:2",
167
                "tag": {"tag_type": 1, "value": 1},
168
            },
169
            "current_path": [],
170
            "primary_path": [
171
                {
172
                    "endpoint_a": {
173
                        "interface_id": "00:00:00:00:00:00:00:01:1"
174
                    },
175
                    "endpoint_b": {
176
                        "interface_id": "00:00:00:00:00:00:00:02:2"
177
                    },
178
                }
179
            ],
180
            "backup_path": [],
181
        }
182
183
        # pylint: disable=protected-access
184
        evc_response = self.napp._evc_from_dict(payload)
185
        self.assertIsNotNone(evc_response)
186
        self.assertIsNotNone(evc_response.uni_a)
187
        self.assertIsNotNone(evc_response.uni_z)
188
        self.assertIsNotNone(evc_response.circuit_scheduler)
189
        self.assertIsNotNone(evc_response.name)
190
        self.assertEqual(len(evc_response.current_path), 0)
191
        self.assertEqual(len(evc_response.backup_path), 0)
192
        self.assertEqual(len(evc_response.primary_path), 1)
193
194
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
195
    @patch("napps.kytos.mef_eline.models.evc.EVCBase._validate")
196
    @patch("kytos.core.Controller.get_interface_by_id")
197
    def test_evc_from_dict_links(
198
        self, _get_interface_by_id_mock, _validate_mock, uni_from_dict_mock
199
    ):
200
        """
201
        Test the helper method that create an EVN from dict.
202
203
        Verify object creation with circuit data and schedule data.
204
        """
205
        _get_interface_by_id_mock.return_value = True
206
        _validate_mock.return_value = True
207
        uni_from_dict_mock.side_effect = ["uni_a", "uni_z"]
208
        payload = {
209
            "name": "my evc1",
210
            "uni_a": {
211
                "interface_id": "00:00:00:00:00:00:00:01:1",
212
                "tag": {"tag_type": 1, "value": 80},
213
            },
214
            "uni_z": {
215
                "interface_id": "00:00:00:00:00:00:00:02:2",
216
                "tag": {"tag_type": 1, "value": 1},
217
            },
218
            "primary_links": [
219
                {
220
                    "endpoint_a": {
221
                        "interface_id": "00:00:00:00:00:00:00:01:1"
222
                    },
223
                    "endpoint_b": {
224
                        "interface_id": "00:00:00:00:00:00:00:02:2"
225
                    },
226
                }
227
            ],
228
            "backup_links": [],
229
        }
230
231
        # pylint: disable=protected-access
232
        evc_response = self.napp._evc_from_dict(payload)
233
        self.assertIsNotNone(evc_response)
234
        self.assertIsNotNone(evc_response.uni_a)
235
        self.assertIsNotNone(evc_response.uni_z)
236
        self.assertIsNotNone(evc_response.circuit_scheduler)
237
        self.assertIsNotNone(evc_response.name)
238
        self.assertEqual(len(evc_response.current_links_cache), 0)
239
        self.assertEqual(len(evc_response.backup_links), 0)
240
        self.assertEqual(len(evc_response.primary_links), 1)
241
242
    def test_list_without_circuits(self):
243
        """Test if list circuits return 'no circuit stored.'."""
244
        api = self.get_app_test_client(self.napp)
245
        url = f"{self.server_name_url}/v2/evc/"
246
        response = api.get(url)
247
        self.assertEqual(response.status_code, 200, response.data)
248
        self.assertEqual(json.loads(response.data.decode()), {})
249
250
    @patch("napps.kytos.mef_eline.storehouse.StoreHouse.get_data")
251
    def test_list_no_circuits_stored(self, storehouse_data_mock):
252
        """Test if list circuits return all circuits stored."""
253
        circuits = {}
254
        storehouse_data_mock.return_value = circuits
255
256
        api = self.get_app_test_client(self.napp)
257
        url = f"{self.server_name_url}/v2/evc/"
258
259
        response = api.get(url)
260
        expected_result = circuits
261
        self.assertEqual(json.loads(response.data), expected_result)
262
263
    @patch("napps.kytos.mef_eline.storehouse.StoreHouse.get_data")
264
    def test_list_with_circuits_stored(self, storehouse_data_mock):
265
        """Test if list circuits return all circuits stored."""
266
        circuits = {"1": {"name": "circuit_1"}, "2": {"name": "circuit_2"}}
267
        storehouse_data_mock.return_value = circuits
268
269
        api = self.get_app_test_client(self.napp)
270
        url = f"{self.server_name_url}/v2/evc/"
271
272
        response = api.get(url)
273
        expected_result = circuits
274
        self.assertEqual(json.loads(response.data), expected_result)
275
276 View Code Duplication
    @patch("napps.kytos.mef_eline.storehouse.StoreHouse.get_data")
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
277
    def test_list_with_archived_circuits_stored_1(self, storehouse_data_mock):
278
        """Test if list circuits return only circuits not archived."""
279
        circuits = {
280
            "1": {"name": "circuit_1"},
281
            "2": {"name": "circuit_2", "archived": True},
282
        }
283
        storehouse_data_mock.return_value = circuits
284
285
        api = self.get_app_test_client(self.napp)
286
        url = f"{self.server_name_url}/v2/evc/"
287
288
        response = api.get(url)
289
        expected_result = {"1": circuits["1"]}
290
        self.assertEqual(json.loads(response.data), expected_result)
291
292 View Code Duplication
    @patch("napps.kytos.mef_eline.storehouse.StoreHouse.get_data")
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
293
    def test_list_with_archived_circuits_stored_2(self, storehouse_data_mock):
294
        """Test if list circuits return all circuits."""
295
        circuits = {
296
            "1": {"name": "circuit_1"},
297
            "2": {"name": "circuit_2", "archived": True},
298
        }
299
        storehouse_data_mock.return_value = circuits
300
301
        api = self.get_app_test_client(self.napp)
302
        url = f"{self.server_name_url}/v2/evc/?archived=True"
303
304
        response = api.get(url)
305
        expected_result = circuits
306
        self.assertEqual(json.loads(response.data), expected_result)
307
308 View Code Duplication
    @patch("napps.kytos.mef_eline.storehouse.StoreHouse.get_data")
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
309
    def test_circuit_with_valid_id(self, storehouse_data_mock):
310
        """Test if get_circuit return the circuit attributes."""
311
        circuits = {"1": {"name": "circuit_1"}, "2": {"name": "circuit_2"}}
312
        storehouse_data_mock.return_value = circuits
313
314
        api = self.get_app_test_client(self.napp)
315
        url = f"{self.server_name_url}/v2/evc/1"
316
        response = api.get(url)
317
        expected_result = circuits["1"]
318
        self.assertEqual(json.loads(response.data), expected_result)
319
320 View Code Duplication
    @patch("napps.kytos.mef_eline.storehouse.StoreHouse.get_data")
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
321
    def test_circuit_with_invalid_id(self, storehouse_data_mock):
322
        """Test if get_circuit return invalid circuit_id."""
323
        circuits = {"1": {"name": "circuit_1"}, "2": {"name": "circuit_2"}}
324
        storehouse_data_mock.return_value = circuits
325
326
        api = self.get_app_test_client(self.napp)
327
        url = f"{self.server_name_url}/v2/evc/3"
328
        response = api.get(url)
329
        expected_result = "circuit_id 3 not found"
330
        self.assertEqual(
331
            json.loads(response.data)["description"], expected_result
332
        )
333
334
    @patch("napps.kytos.mef_eline.models.evc.EVC.deploy")
335
    @patch("napps.kytos.mef_eline.storehouse.StoreHouse.get_data")
336
    @patch("napps.kytos.mef_eline.scheduler.Scheduler.add")
337
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
338
    @patch("napps.kytos.mef_eline.storehouse.StoreHouse.save_evc")
339
    @patch("napps.kytos.mef_eline.main.EVC.as_dict")
340
    @patch("napps.kytos.mef_eline.models.evc.EVC._validate")
341
    def test_create_a_circuit_case_1(self, *args):
342
        """Test create a new circuit."""
343
        # pylint: disable=too-many-locals
344
        (
345
            validate_mock,
346
            evc_as_dict_mock,
347
            save_evc_mock,
348
            uni_from_dict_mock,
349
            sched_add_mock,
350
            storehouse_data_mock,
351
            evc_deploy_mock,
352
        ) = args
353
354
        validate_mock.return_value = True
355
        save_evc_mock.return_value = True
356
        evc_deploy_mock.return_value = True
357
        uni1 = create_autospec(UNI)
358
        uni2 = create_autospec(UNI)
359
        uni1.interface = create_autospec(Interface)
360
        uni2.interface = create_autospec(Interface)
361
        uni1.interface.switch = "00:00:00:00:00:00:00:01"
362
        uni2.interface.switch = "00:00:00:00:00:00:00:02"
363
        uni_from_dict_mock.side_effect = [uni1, uni2]
364
        evc_as_dict_mock.return_value = {}
365
        sched_add_mock.return_value = True
366
        storehouse_data_mock.return_value = {}
367
368
        api = self.get_app_test_client(self.napp)
369
        url = f"{self.server_name_url}/v2/evc/"
370
        payload = {
371
            "name": "my evc1",
372
            "frequency": "* * * * *",
373
            "uni_a": {
374
                "interface_id": "00:00:00:00:00:00:00:01:1",
375
                "tag": {"tag_type": 1, "value": 80},
376
            },
377
            "uni_z": {
378
                "interface_id": "00:00:00:00:00:00:00:02:2",
379
                "tag": {"tag_type": 1, "value": 1},
380
            },
381
            "dynamic_backup_path": True,
382
        }
383
384
        response = api.post(
385
            url, data=json.dumps(payload), content_type="application/json"
386
        )
387
        current_data = json.loads(response.data)
388
389
        # verify expected result from request
390
        self.assertEqual(201, response.status_code, response.data)
391
        self.assertIn("circuit_id", current_data)
392
393
        # verify uni called
394
        uni_from_dict_mock.called_twice()
395
        uni_from_dict_mock.assert_any_call(payload["uni_z"])
396
        uni_from_dict_mock.assert_any_call(payload["uni_a"])
397
398
        # verify validation called
399
        validate_mock.assert_called_once()
400
        validate_mock.assert_called_with(
401
            frequency="* * * * *",
402
            name="my evc1",
403
            uni_a=uni1,
404
            uni_z=uni2,
405
            dynamic_backup_path=True,
406
        )
407
        # verify save method is called
408
        save_evc_mock.assert_called_once()
409
410
        # verify evc as dict is called to save in the box
411
        evc_as_dict_mock.assert_called_once()
412
        # verify add circuit in sched
413
        sched_add_mock.assert_called_once()
414
415
    @staticmethod
416
    def get_napp_urls(napp):
417
        """Return the kytos/mef_eline urls.
418
419
        The urls will be like:
420
421
        urls = [
422
            (options, methods, url)
423
        ]
424
425
        """
426
        controller = napp.controller
427
        controller.api_server.register_napp_endpoints(napp)
428
429
        urls = []
430
        for rule in controller.api_server.app.url_map.iter_rules():
431
            options = {}
432
            for arg in rule.arguments:
433
                options[arg] = f"[{0}]".format(arg)
434
435
            if f"{napp.username}/{napp.name}" in str(rule):
436
                urls.append((options, rule.methods, f"{str(rule)}"))
437
438
        return urls
439
440
    @staticmethod
441
    def get_app_test_client(napp):
442
        """Return a flask api test client."""
443
        napp.controller.api_server.register_napp_endpoints(napp)
444
        return napp.controller.api_server.app.test_client()
445
446
    def test_create_a_circuit_case_2(self):
447
        """Test create a new circuit trying to send request without a json."""
448
        api = self.get_app_test_client(self.napp)
449
        url = f"{self.server_name_url}/v2/evc/"
450
451
        response = api.post(url)
452
        current_data = json.loads(response.data)
453
        expected_message = "The request body mimetype is not application/json."
454
        expected_data = expected_message
455
        self.assertEqual(415, response.status_code, response.data)
456
        self.assertEqual(current_data["description"], expected_data)
457
458
    def test_create_a_circuit_case_3(self):
459
        """Test create a new circuit trying to send request with an
460
        invalid json."""
461
        api = self.get_app_test_client(self.napp)
462
        url = f"{self.server_name_url}/v2/evc/"
463
464
        response = api.post(
465
            url,
466
            data="This is an {Invalid:} JSON",
467
            content_type="application/json",
468
        )
469
        current_data = json.loads(response.data)
470
        expected_data = "The request body is not a well-formed JSON."
471
472
        self.assertEqual(400, response.status_code, response.data)
473
        self.assertEqual(current_data["description"], expected_data)
474
475
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
476
    def test_create_a_circuit_case_4(self, uni_from_dict_mock):
477
        """Test create a new circuit trying to send request with an
478
        invalid value."""
479
        # pylint: disable=too-many-locals
480
        uni_from_dict_mock.side_effect = ValueError("Could not instantiate")
481
        api = self.get_app_test_client(self.napp)
482
        url = f"{self.server_name_url}/v2/evc/"
483
484
        payload = {
485
            "name": "my evc1",
486
            "frequency": "* * * * *",
487
            "uni_a": {
488
                "interface_id": "00:00:00:00:00:00:00:01:76",
489
                "tag": {"tag_type": 1, "value": 80},
490
            },
491
            "uni_z": {
492
                "interface_id": "00:00:00:00:00:00:00:02:2",
493
                "tag": {"tag_type": 1, "value": 1},
494
            },
495
        }
496
497
        response = api.post(
498
            url, data=json.dumps(payload), content_type="application/json"
499
        )
500
        current_data = json.loads(response.data)
501
        expected_data = "Error creating UNI: Invalid value"
502
        self.assertEqual(400, response.status_code, response.data)
503
        self.assertEqual(current_data["description"], expected_data)
504
505
    @patch("napps.kytos.mef_eline.models.evc.EVC.deploy")
506
    @patch("napps.kytos.mef_eline.scheduler.Scheduler.add")
507
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
508
    @patch("napps.kytos.mef_eline.storehouse.StoreHouse.save_evc")
509
    @patch("napps.kytos.mef_eline.models.evc.EVC._validate")
510
    @patch("napps.kytos.mef_eline.main.EVC.as_dict")
511
    def test_create_circuit_already_enabled(self, *args):
512
        """Test create an already created circuit."""
513
        # pylint: disable=too-many-locals
514
        (
515
            evc_as_dict_mock,
516
            validate_mock,
517
            save_evc_mock,
518
            uni_from_dict_mock,
519
            sched_add_mock,
520
            evc_deploy_mock,
521
        ) = args
522
523
        validate_mock.return_value = True
524
        save_evc_mock.return_value = True
525
        sched_add_mock.return_value = True
526
        evc_deploy_mock.return_value = True
527
        uni1 = create_autospec(UNI)
528
        uni2 = create_autospec(UNI)
529
        uni1.interface = create_autospec(Interface)
530
        uni2.interface = create_autospec(Interface)
531
        uni1.interface.switch = "00:00:00:00:00:00:00:01"
532
        uni2.interface.switch = "00:00:00:00:00:00:00:02"
533
        uni_from_dict_mock.side_effect = [uni1, uni2, uni1, uni2]
534
535
        api = self.get_app_test_client(self.napp)
536
        payload = {
537
            "name": "my evc1",
538
            "uni_a": {
539
                "interface_id": "00:00:00:00:00:00:00:01:1",
540
                "tag": {"tag_type": 1, "value": 80},
541
            },
542
            "uni_z": {
543
                "interface_id": "00:00:00:00:00:00:00:02:2",
544
                "tag": {"tag_type": 1, "value": 1},
545
            },
546
            "dynamic_backup_path": True,
547
        }
548
549
        evc_as_dict_mock.return_value = payload
550
        response = api.post(
551
            f"{self.server_name_url}/v2/evc/",
552
            data=json.dumps(payload),
553
            content_type="application/json",
554
        )
555
        self.assertEqual(201, response.status_code)
556
557
        response = api.post(
558
            f"{self.server_name_url}/v2/evc/",
559
            data=json.dumps(payload),
560
            content_type="application/json",
561
        )
562
        current_data = json.loads(response.data)
563
        expected_data = "The EVC already exists."
564
        self.assertEqual(current_data["description"], expected_data)
565
        self.assertEqual(409, response.status_code)
566
567
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
568
    def test_create_circuit_case_5(self, uni_from_dict_mock):
569
        """Test when neither primary path nor dynamic_backup_path is set."""
570
        api = self.get_app_test_client(self.napp)
571
        url = f"{self.server_name_url}/v2/evc/"
572
        uni1 = create_autospec(UNI)
573
        uni2 = create_autospec(UNI)
574
        uni1.interface = create_autospec(Interface)
575
        uni2.interface = create_autospec(Interface)
576
        uni1.interface.switch = "00:00:00:00:00:00:00:01"
577
        uni2.interface.switch = "00:00:00:00:00:00:00:02"
578
        uni_from_dict_mock.side_effect = [uni1, uni2, uni1, uni2]
579
580
        payload = {
581
            "name": "my evc1",
582
            "frequency": "* * * * *",
583
            "uni_a": {
584
                "interface_id": "00:00:00:00:00:00:00:01:1",
585
                "tag": {"tag_type": 1, "value": 80},
586
            },
587
            "uni_z": {
588
                "interface_id": "00:00:00:00:00:00:00:02:2",
589
                "tag": {"tag_type": 1, "value": 1},
590
            },
591
        }
592
593
        response = api.post(
594
            url, data=json.dumps(payload), content_type="application/json"
595
        )
596
        current_data = json.loads(response.data)
597
        expected_data = "The EVC must have a primary path "
598
        expected_data += "or allow dynamic paths."
599
        self.assertEqual(400, response.status_code, response.data)
600
        self.assertEqual(current_data["description"], expected_data)
601
602
    def test_create_circuit_case_6(self):
603
        """Test when neither primary path nor dynamic_backup_path is set."""
604
        api = self.get_app_test_client(self.napp)
605
        url = f"{self.server_name_url}/v2/evc/"
606
607
        payload = {
608
            "name": "my evc1",
609
            "frequency": "* * * * *",
610
        }
611
612
        response = api.post(
613
            url, data=json.dumps(payload), content_type="application/json"
614
        )
615
        self.assertEqual(400, response.status_code, response.data)
616
617
    def test_load_circuits_by_interface(self):
618
        """Test if existing circuits are correctly loaded to the cache."""
619
        stored_circuits = {
620
            "182f5bac84074017a262a2321195dbb4": {
621
                "active": False,
622
                "archived": True,
623
                "backup_links": [],
624
                "backup_path": [],
625
                "bandwidth": 0,
626
                "circuit_scheduler": [
627
                    {
628
                        "action": "create",
629
                        "frequency": "*/3 * * * *",
630
                        "id": "db7f8a301e2b4ff69a2ad9a6267430e2",
631
                    },
632
                    {
633
                        "action": "remove",
634
                        "frequency": "2-59/3 * * * *",
635
                        "id": "b8a8bbe85bc144b0afc65181e4c069a1",
636
                    },
637
                ],
638
                "creation_time": "2019-08-09T19:25:06",
639
                "current_path": [],
640
                "dynamic_backup_path": True,
641
                "enabled": False,
642
                "end_date": "2018-12-29T15:16:50",
643
                "id": "182f5bac84074017a262a2321195dbb4",
644
                "name": "Teste2",
645
                "owner": None,
646
                "primary_links": [],
647
                "primary_path": [],
648
                "priority": 0,
649
                "request_time": "2019-08-09T19:25:06",
650
                "start_date": "2019-08-09T19:25:06",
651
                "uni_a": {
652
                    "interface_id": "00:00:00:00:00:00:00:03:12",
653
                    "tag": {"tag_type": 1, "value": 321},
654
                },
655
                "uni_z": {
656
                    "interface_id": "00:00:00:00:00:00:00:06:11",
657
                    "tag": {"tag_type": 1, "value": 612},
658
                },
659
            },
660
            "65c4582cc8f249c2a5947ef500c19e37": {
661
                "active": False,
662
                "archived": False,
663
                "backup_links": [],
664
                "backup_path": [],
665
                "bandwidth": 0,
666
                "circuit_scheduler": [
667
                    {
668
                        "action": "create",
669
                        "frequency": "*/3 * * * *",
670
                        "id": "0939dedf66ce431f85beb53daf578d73",
671
                    },
672
                    {
673
                        "action": "remove",
674
                        "frequency": "2-59/3 * * * *",
675
                        "id": "6cdcab31a11f44708e23776b4dad7893",
676
                    },
677
                ],
678
                "creation_time": "2019-07-22T16:01:24",
679
                "current_path": [],
680
                "dynamic_backup_path": True,
681
                "enabled": False,
682
                "end_date": "2018-12-29T15:16:50",
683
                "id": "65c4582cc8f249c2a5947ef500c19e37",
684
                "name": "Teste2",
685
                "owner": None,
686
                "primary_links": [],
687
                "primary_path": [
688
                    {
689
                        "active": False,
690
                        "enabled": True,
691
                        "endpoint_a": {
692
                            "active": False,
693
                            "enabled": True,
694
                            "id": "00:00:00:00:00:00:00:03:3",
695
                            "link": "0e2b5d7bc858b9f38db11b69",
696
                            "mac": "ae:6e:d3:96:83:5a",
697
                            "metadata": {},
698
                            "name": "s3-eth3",
699
                            "nni": True,
700
                            "port_number": 3,
701
                            "speed": 1250000000.0,
702
                            "switch": "00:00:00:00:00:00:00:03",
703
                            "type": "interface",
704
                            "uni": False,
705
                        },
706
                        "endpoint_b": {
707
                            "active": False,
708
                            "enabled": True,
709
                            "id": "00:00:00:00:00:00:00:05:2",
710
                            "link": "0e2b5d7bc858b9f38db11b69",
711
                            "mac": "de:eb:d0:b0:14:cf",
712
                            "metadata": {},
713
                            "name": "s5-eth2",
714
                            "nni": True,
715
                            "port_number": 2,
716
                            "speed": 1250000000.0,
717
                            "switch": "00:00:00:00:00:00:00:05",
718
                            "type": "interface",
719
                            "uni": False,
720
                        },
721
                        "id": "0e2b5d7bc858b9f38db11b69",
722
                        "metadata": {},
723
                    },
724
                    {
725
                        "active": False,
726
                        "enabled": True,
727
                        "endpoint_a": {
728
                            "active": False,
729
                            "enabled": True,
730
                            "id": "00:00:00:00:00:00:00:05:4",
731
                            "link": "53bd36ff55a5aa2029bd5d50",
732
                            "mac": "6e:c2:ea:c4:18:12",
733
                            "metadata": {},
734
                            "name": "s5-eth4",
735
                            "nni": True,
736
                            "port_number": 4,
737
                            "speed": 1250000000.0,
738
                            "switch": "00:00:00:00:00:00:00:05",
739
                            "type": "interface",
740
                            "uni": False,
741
                        },
742
                        "endpoint_b": {
743
                            "active": False,
744
                            "enabled": True,
745
                            "id": "00:00:00:00:00:00:00:06:2",
746
                            "link": "53bd36ff55a5aa2029bd5d50",
747
                            "mac": "5a:25:7b:7c:0d:ac",
748
                            "metadata": {},
749
                            "name": "s6-eth2",
750
                            "nni": True,
751
                            "port_number": 2,
752
                            "speed": 1250000000.0,
753
                            "switch": "00:00:00:00:00:00:00:06",
754
                            "type": "interface",
755
                            "uni": False,
756
                        },
757
                        "id": "53bd36ff55a5aa2029bd5d50",
758
                        "metadata": {},
759
                    },
760
                ],
761
                "priority": 0,
762
                "request_time": "2019-07-22T16:01:24",
763
                "start_date": "2019-07-22T16:01:24",
764
                "uni_a": {
765
                    "interface_id": "00:00:00:00:00:00:00:03:12",
766
                    "tag": {"tag_type": 1, "value": 321},
767
                },
768
                "uni_z": {
769
                    "interface_id": "00:00:00:00:00:00:00:06:11",
770
                    "tag": {"tag_type": 1, "value": 612},
771
                },
772
            },
773
        }
774
775
        expected_result = {
776
            "00:00:00:00:00:00:00:03:12": {"65c4582cc8f249c2a5947ef500c19e37"},
777
            "00:00:00:00:00:00:00:06:11": {"65c4582cc8f249c2a5947ef500c19e37"},
778
            "00:00:00:00:00:00:00:03:3": {"65c4582cc8f249c2a5947ef500c19e37"},
779
            "00:00:00:00:00:00:00:05:2": {"65c4582cc8f249c2a5947ef500c19e37"},
780
            "00:00:00:00:00:00:00:05:4": {"65c4582cc8f249c2a5947ef500c19e37"},
781
            "00:00:00:00:00:00:00:06:2": {"65c4582cc8f249c2a5947ef500c19e37"},
782
        }
783
        self.napp.load_circuits_by_interface(stored_circuits)
784
        # pylint: disable=protected-access
785
        self.assertEqual(self.napp._circuits_by_interface, expected_result)
786
787
    def test_redeploy_evc(self):
788
        """Test endpoint to redeploy an EVC."""
789
        evc1 = MagicMock()
790
        evc1.is_enabled.return_value = True
791
        self.napp.circuits = {"1": evc1, "2": MagicMock()}
792
        api = self.get_app_test_client(self.napp)
793
        url = f"{self.server_name_url}/v2/evc/1/redeploy"
794
        response = api.patch(url)
795
        self.assertEqual(response.status_code, 202, response.data)
796
797
    def test_redeploy_evc_disabled(self):
798
        """Test endpoint to redeploy an EVC."""
799
        evc1 = MagicMock()
800
        evc1.is_enabled.return_value = False
801
        self.napp.circuits = {"1": evc1, "2": MagicMock()}
802
        api = self.get_app_test_client(self.napp)
803
        url = f"{self.server_name_url}/v2/evc/1/redeploy"
804
        response = api.patch(url)
805
        self.assertEqual(response.status_code, 409, response.data)
806
807
    def test_redeploy_evc_deleted(self):
808
        """Test endpoint to redeploy an EVC."""
809
        evc1 = MagicMock()
810
        evc1.is_enabled.return_value = True
811
        self.napp.circuits = {"1": evc1, "2": MagicMock()}
812
        api = self.get_app_test_client(self.napp)
813
        url = f"{self.server_name_url}/v2/evc/3/redeploy"
814
        response = api.patch(url)
815
        self.assertEqual(response.status_code, 404, response.data)
816
817
    def test_list_schedules__no_data(self):
818
        """Test list of schedules."""
819
        api = self.get_app_test_client(self.napp)
820
        url = f"{self.server_name_url}/v2/evc/schedule"
821
        response = api.get(url)
822
        self.assertEqual(response.status_code, 200, response.data)
823
        self.assertEqual(json.loads(response.data.decode()), {})
824
825
    @patch("napps.kytos.mef_eline.storehouse.StoreHouse.get_data")
826
    def test_list_schedules__no_data_stored(self, storehouse_data_mock):
827
        """Test if list circuits return all circuits stored."""
828
        circuits = {}
829
        storehouse_data_mock.return_value = circuits
830
831
        api = self.get_app_test_client(self.napp)
832
        url = f"{self.server_name_url}/v2/evc/schedule"
833
834
        response = api.get(url)
835
        expected_result = circuits
836
837
        self.assertEqual(response.status_code, 200, response.data)
838
        self.assertEqual(json.loads(response.data), expected_result)
839
840
    # pylint: disable=no-self-use
841
    def _add_storehouse_schedule_data(self, storehouse_data_mock):
842
        """Add schedule data to storehouse mock object."""
843
        circuits = {}
844
        payload_1 = {
845
            "id": "aa:aa:aa",
846
            "name": "my evc1",
847
            "uni_a": {
848
                "interface_id": "00:00:00:00:00:00:00:01:1",
849
                "tag": {"tag_type": 1, "value": 80},
850
            },
851
            "uni_z": {
852
                "interface_id": "00:00:00:00:00:00:00:02:2",
853
                "tag": {"tag_type": 1, "value": 1},
854
            },
855
            "circuit_scheduler": [
856
                {"id": "1", "frequency": "* * * * *", "action": "create"},
857
                {"id": "2", "frequency": "1 * * * *", "action": "remove"},
858
            ],
859
        }
860
        circuits.update({"aa:aa:aa": payload_1})
861
        payload_2 = {
862
            "id": "bb:bb:bb",
863
            "name": "my second evc2",
864
            "uni_a": {
865
                "interface_id": "00:00:00:00:00:00:00:01:2",
866
                "tag": {"tag_type": 1, "value": 90},
867
            },
868
            "uni_z": {
869
                "interface_id": "00:00:00:00:00:00:00:03:2",
870
                "tag": {"tag_type": 1, "value": 100},
871
            },
872
            "circuit_scheduler": [
873
                {"id": "3", "frequency": "1 * * * *", "action": "create"},
874
                {"id": "4", "frequency": "2 * * * *", "action": "remove"},
875
            ],
876
        }
877
        circuits.update({"bb:bb:bb": payload_2})
878
        payload_3 = {
879
            "id": "cc:cc:cc",
880
            "name": "my third evc3",
881
            "uni_a": {
882
                "interface_id": "00:00:00:00:00:00:00:03:1",
883
                "tag": {"tag_type": 1, "value": 90},
884
            },
885
            "uni_z": {
886
                "interface_id": "00:00:00:00:00:00:00:04:2",
887
                "tag": {"tag_type": 1, "value": 100},
888
            },
889
        }
890
        circuits.update({"cc:cc:cc": payload_3})
891
        # Add one circuit to the storehouse.
892
        storehouse_data_mock.return_value = circuits
893
894
    @patch("napps.kytos.mef_eline.storehouse.StoreHouse.get_data")
895
    def test_list_schedules_from_storehouse(self, storehouse_data_mock):
896
        """Test if list circuits return specific circuits stored."""
897
        self._add_storehouse_schedule_data(storehouse_data_mock)
898
899
        api = self.get_app_test_client(self.napp)
900
        url = f"{self.server_name_url}/v2/evc/schedule"
901
902
        # Call URL
903
        response = api.get(url)
904
        # Expected JSON data from response
905
        expected = [
906
            {
907
                "circuit_id": "aa:aa:aa",
908
                "schedule": {
909
                    "action": "create",
910
                    "frequency": "* * * * *",
911
                    "id": "1",
912
                },
913
                "schedule_id": "1",
914
            },
915
            {
916
                "circuit_id": "aa:aa:aa",
917
                "schedule": {
918
                    "action": "remove",
919
                    "frequency": "1 * * * *",
920
                    "id": "2",
921
                },
922
                "schedule_id": "2",
923
            },
924
            {
925
                "circuit_id": "bb:bb:bb",
926
                "schedule": {
927
                    "action": "create",
928
                    "frequency": "1 * * * *",
929
                    "id": "3",
930
                },
931
                "schedule_id": "3",
932
            },
933
            {
934
                "circuit_id": "bb:bb:bb",
935
                "schedule": {
936
                    "action": "remove",
937
                    "frequency": "2 * * * *",
938
                    "id": "4",
939
                },
940
                "schedule_id": "4",
941
            },
942
        ]
943
944
        self.assertEqual(response.status_code, 200, response.data)
945
        self.assertEqual(expected, json.loads(response.data))
946
947
    @patch("napps.kytos.mef_eline.storehouse.StoreHouse.get_data")
948
    def test_get_specific_schedule_from_storehouse(self, storehouse_data_mock):
949
        """Test get schedules from a circuit."""
950
        self._add_storehouse_schedule_data(storehouse_data_mock)
951
952
        requested_circuit_id = "bb:bb:bb"
953
        api = self.get_app_test_client(self.napp)
954
        url = f"{self.server_name_url}/v2/evc/{requested_circuit_id}"
955
956
        # Call URL
957
        response = api.get(url)
958
959
        # Expected JSON data from response
960
        expected = [
961
            {"action": "create", "frequency": "1 * * * *", "id": "3"},
962
            {"action": "remove", "frequency": "2 * * * *", "id": "4"},
963
        ]
964
965
        self.assertEqual(response.status_code, 200)
966
        self.assertEqual(
967
            expected, json.loads(response.data)["circuit_scheduler"]
968
        )
969
970
    def test_get_specific_schedules_from_storehouse_not_found(self):
971
        """Test get specific schedule ID that does not exist."""
972
        requested_id = "blah"
973
        api = self.get_app_test_client(self.napp)
974
        url = f"{self.server_name_url}/v2/evc/{requested_id}"
975
976
        # Call URL
977
        response = api.get(url)
978
979
        expected = "circuit_id blah not found"
980
        # Assert response not found
981
        self.assertEqual(response.status_code, 400, response.data)
982
        self.assertEqual(expected, json.loads(response.data)["description"])
983
984
    def _uni_from_dict_side_effect(self, uni_dict):
985
        interface_id = uni_dict.get("interface_id")
986
        tag_dict = uni_dict.get("tag")
987
        interface = Interface(interface_id, "0", "switch")
988
        return UNI(interface, tag_dict)
989
990
    @patch("apscheduler.schedulers.background.BackgroundScheduler.add_job")
991
    @patch("napps.kytos.mef_eline.storehouse.StoreHouse.get_data")
992
    @patch("napps.kytos.mef_eline.scheduler.Scheduler.add")
993
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
994
    @patch("napps.kytos.mef_eline.storehouse.StoreHouse.save_evc")
995
    @patch("napps.kytos.mef_eline.main.EVC.as_dict")
996
    @patch("napps.kytos.mef_eline.models.evc.EVC._validate")
997
    def test_create_schedule(self, *args):  # pylint: disable=too-many-locals
998
        """Test create a circuit schedule."""
999
        (
1000
            validate_mock,
1001
            evc_as_dict_mock,
1002
            save_evc_mock,
1003
            uni_from_dict_mock,
1004
            sched_add_mock,
1005
            storehouse_data_mock,
1006
            scheduler_add_job_mock,
1007
        ) = args
1008
1009
        validate_mock.return_value = True
1010
        save_evc_mock.return_value = True
1011
        uni_from_dict_mock.side_effect = self._uni_from_dict_side_effect
1012
        evc_as_dict_mock.return_value = {}
1013
        sched_add_mock.return_value = True
1014
        storehouse_data_mock.return_value = {}
1015
1016
        self._add_storehouse_schedule_data(storehouse_data_mock)
1017
1018
        requested_id = "bb:bb:bb"
1019
        api = self.get_app_test_client(self.napp)
1020
        url = f"{self.server_name_url}/v2/evc/schedule/"
1021
1022
        payload = {
1023
            "circuit_id": requested_id,
1024
            "schedule": {"frequency": "1 * * * *", "action": "create"},
1025
        }
1026
1027
        # Call URL
1028
        response = api.post(
1029
            url, data=json.dumps(payload), content_type="application/json"
1030
        )
1031
1032
        response_json = json.loads(response.data)
1033
1034
        self.assertEqual(response.status_code, 201, response.data)
1035
        scheduler_add_job_mock.assert_called_once()
1036
        save_evc_mock.assert_called_once()
1037
        self.assertEqual(
1038
            payload["schedule"]["frequency"], response_json["frequency"]
1039
        )
1040
        self.assertEqual(
1041
            payload["schedule"]["action"], response_json["action"]
1042
        )
1043
        self.assertIsNotNone(response_json["id"])
1044
1045
    @patch("apscheduler.schedulers.background.BackgroundScheduler.remove_job")
1046
    @patch("napps.kytos.mef_eline.storehouse.StoreHouse.get_data")
1047
    @patch("napps.kytos.mef_eline.scheduler.Scheduler.add")
1048
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
1049
    @patch("napps.kytos.mef_eline.storehouse.StoreHouse.save_evc")
1050
    @patch("napps.kytos.mef_eline.main.EVC.as_dict")
1051
    @patch("napps.kytos.mef_eline.models.evc.EVC._validate")
1052
    def test_update_schedule(self, *args):  # pylint: disable=too-many-locals
1053
        """Test create a circuit schedule."""
1054
        (
1055
            validate_mock,
1056
            evc_as_dict_mock,
1057
            save_evc_mock,
1058
            uni_from_dict_mock,
1059
            sched_add_mock,
1060
            storehouse_data_mock,
1061
            scheduler_remove_job_mock,
1062
        ) = args
1063
1064
        storehouse_payload_1 = {
1065
            "aa:aa:aa": {
1066
                "id": "aa:aa:aa",
1067
                "name": "my evc1",
1068
                "uni_a": {
1069
                    "interface_id": "00:00:00:00:00:00:00:01:1",
1070
                    "tag": {"tag_type": 1, "value": 80},
1071
                },
1072
                "uni_z": {
1073
                    "interface_id": "00:00:00:00:00:00:00:02:2",
1074
                    "tag": {"tag_type": 1, "value": 1},
1075
                },
1076
                "circuit_scheduler": [
1077
                    {"id": "1", "frequency": "* * * * *", "action": "create"}
1078
                ],
1079
            }
1080
        }
1081
1082
        validate_mock.return_value = True
1083
        save_evc_mock.return_value = True
1084
        sched_add_mock.return_value = True
1085
        uni_from_dict_mock.side_effect = ["uni_a", "uni_z"]
1086
        evc_as_dict_mock.return_value = {}
1087
        storehouse_data_mock.return_value = storehouse_payload_1
1088
        scheduler_remove_job_mock.return_value = True
1089
1090
        requested_schedule_id = "1"
1091
        api = self.get_app_test_client(self.napp)
1092
        url = f"{self.server_name_url}/v2/evc/schedule/{requested_schedule_id}"
1093
1094
        payload = {"frequency": "*/1 * * * *", "action": "create"}
1095
1096
        # Call URL
1097
        response = api.patch(
1098
            url, data=json.dumps(payload), content_type="application/json"
1099
        )
1100
1101
        response_json = json.loads(response.data)
1102
1103
        self.assertEqual(response.status_code, 200, response.data)
1104
        scheduler_remove_job_mock.assert_called_once()
1105
        save_evc_mock.assert_called_once()
1106
        self.assertEqual(payload["frequency"], response_json["frequency"])
1107
        self.assertEqual(payload["action"], response_json["action"])
1108
        self.assertIsNotNone(response_json["id"])
1109
1110
    @patch("napps.kytos.mef_eline.storehouse.StoreHouse.get_data")
1111
    @patch("napps.kytos.mef_eline.scheduler.Scheduler.add")
1112
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
1113
    @patch("napps.kytos.mef_eline.main.EVC.as_dict")
1114
    @patch("napps.kytos.mef_eline.models.evc.EVC._validate")
1115
    def test_update_schedule_archived(self, *args):
1116
        """Test create a circuit schedule."""
1117
        # pylint: disable=too-many-locals
1118
        (
1119
            validate_mock,
1120
            evc_as_dict_mock,
1121
            uni_from_dict_mock,
1122
            sched_add_mock,
1123
            storehouse_data_mock,
1124
        ) = args
1125
1126
        storehouse_payload_1 = {
1127
            "aa:aa:aa": {
1128
                "id": "aa:aa:aa",
1129
                "name": "my evc1",
1130
                "archived": True,
1131
                "circuit_scheduler": [
1132
                    {"id": "1", "frequency": "* * * * *", "action": "create"}
1133
                ],
1134
            }
1135
        }
1136
1137
        validate_mock.return_value = True
1138
        sched_add_mock.return_value = True
1139
        uni_from_dict_mock.side_effect = ["uni_a", "uni_z"]
1140
        evc_as_dict_mock.return_value = {}
1141
        storehouse_data_mock.return_value = storehouse_payload_1
1142
1143
        requested_schedule_id = "1"
1144
        api = self.get_app_test_client(self.napp)
1145
        url = f"{self.server_name_url}/v2/evc/schedule/{requested_schedule_id}"
1146
1147
        payload = {"frequency": "*/1 * * * *", "action": "create"}
1148
1149
        # Call URL
1150
        response = api.patch(
1151
            url, data=json.dumps(payload), content_type="application/json"
1152
        )
1153
1154
        self.assertEqual(response.status_code, 403, response.data)
1155
1156
    @patch("apscheduler.schedulers.background.BackgroundScheduler.remove_job")
1157
    @patch("napps.kytos.mef_eline.storehouse.StoreHouse.get_data")
1158
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
1159
    @patch("napps.kytos.mef_eline.storehouse.StoreHouse.save_evc")
1160
    @patch("napps.kytos.mef_eline.main.EVC.as_dict")
1161
    @patch("napps.kytos.mef_eline.models.evc.EVC._validate")
1162
    def test_delete_schedule(self, *args):
1163
        """Test create a circuit schedule."""
1164
        (
1165
            validate_mock,
1166
            evc_as_dict_mock,
1167
            save_evc_mock,
1168
            uni_from_dict_mock,
1169
            storehouse_data_mock,
1170
            scheduler_remove_job_mock,
1171
        ) = args
1172
1173
        storehouse_payload_1 = {
1174
            "2": {
1175
                "id": "2",
1176
                "name": "my evc1",
1177
                "uni_a": {
1178
                    "interface_id": "00:00:00:00:00:00:00:01:1",
1179
                    "tag": {"tag_type": 1, "value": 80},
1180
                },
1181
                "uni_z": {
1182
                    "interface_id": "00:00:00:00:00:00:00:02:2",
1183
                    "tag": {"tag_type": 1, "value": 1},
1184
                },
1185
                "circuit_scheduler": [
1186
                    {"id": "1", "frequency": "* * * * *", "action": "create"}
1187
                ],
1188
            }
1189
        }
1190
        validate_mock.return_value = True
1191
        save_evc_mock.return_value = True
1192
        uni_from_dict_mock.side_effect = ["uni_a", "uni_z"]
1193
        evc_as_dict_mock.return_value = {}
1194
        storehouse_data_mock.return_value = storehouse_payload_1
1195
        scheduler_remove_job_mock.return_value = True
1196
1197
        requested_schedule_id = "1"
1198
        api = self.get_app_test_client(self.napp)
1199
        url = f"{self.server_name_url}/v2/evc/schedule/{requested_schedule_id}"
1200
1201
        # Call URL
1202
        response = api.delete(url)
1203
1204
        self.assertEqual(response.status_code, 200, response.data)
1205
        scheduler_remove_job_mock.assert_called_once()
1206
        save_evc_mock.assert_called_once()
1207
        self.assertIn("Schedule removed", f"{response.data}")
1208
1209
    @patch("napps.kytos.mef_eline.storehouse.StoreHouse.get_data")
1210
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
1211
    @patch("napps.kytos.mef_eline.main.EVC.as_dict")
1212
    @patch("napps.kytos.mef_eline.models.evc.EVC._validate")
1213
    def test_delete_schedule_archived(self, *args):
1214
        """Test create a circuit schedule."""
1215
        (
1216
            validate_mock,
1217
            evc_as_dict_mock,
1218
            uni_from_dict_mock,
1219
            storehouse_data_mock,
1220
        ) = args
1221
1222
        storehouse_payload_1 = {
1223
            "2": {
1224
                "id": "2",
1225
                "name": "my evc1",
1226
                "archived": True,
1227
                "circuit_scheduler": [
1228
                    {"id": "1", "frequency": "* * * * *", "action": "create"}
1229
                ],
1230
            }
1231
        }
1232
1233
        validate_mock.return_value = True
1234
        uni_from_dict_mock.side_effect = ["uni_a", "uni_z"]
1235
        evc_as_dict_mock.return_value = {}
1236
        storehouse_data_mock.return_value = storehouse_payload_1
1237
1238
        requested_schedule_id = "1"
1239
        api = self.get_app_test_client(self.napp)
1240
        url = f"{self.server_name_url}/v2/evc/schedule/{requested_schedule_id}"
1241
1242
        # Call URL
1243
        response = api.delete(url)
1244
1245
        self.assertEqual(response.status_code, 403, response.data)
1246
1247
    @patch("requests.post")
1248
    @patch("napps.kytos.mef_eline.scheduler.Scheduler.add")
1249
    @patch("napps.kytos.mef_eline.storehouse.StoreHouse.save_evc")
1250
    @patch("napps.kytos.mef_eline.models.evc.EVC._validate")
1251
    @patch("kytos.core.Controller.get_interface_by_id")
1252
    @patch("napps.kytos.mef_eline.models.path.Path.is_valid")
1253
    @patch("napps.kytos.mef_eline.models.evc.EVCDeploy.deploy")
1254
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
1255
    @patch("napps.kytos.mef_eline.main.EVC.as_dict")
1256
    def test_update_circuit(self, *args):
1257
        """Test update a circuit circuit."""
1258
        # pylint: disable=too-many-locals,duplicate-code
1259
        (
1260
            evc_as_dict_mock,
1261
            uni_from_dict_mock,
1262
            evc_deploy,
1263
            *mocks,
1264
            requests_mock,
1265
        ) = args
1266
1267
        for mock in mocks:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable mocks does not seem to be defined.
Loading history...
1268
            mock.return_value = True
1269
        unis = [
1270
            get_uni_mocked(switch_dpid="00:00:00:00:00:00:00:01"),
1271
            get_uni_mocked(switch_dpid="00:00:00:00:00:00:00:02"),
1272
        ]
1273
        uni_from_dict_mock.side_effect = 2 * unis
1274
1275
        response = MagicMock()
1276
        response.status_code = 201
1277
        requests_mock.return_value = response
1278
1279
        api = self.get_app_test_client(self.napp)
1280
        payloads = [
1281
            {
1282
                "name": "my evc1",
1283
                "uni_a": {
1284
                    "interface_id": "00:00:00:00:00:00:00:01:1",
1285
                    "tag": {"tag_type": 1, "value": 80},
1286
                },
1287
                "uni_z": {
1288
                    "interface_id": "00:00:00:00:00:00:00:02:2",
1289
                    "tag": {"tag_type": 1, "value": 1},
1290
                },
1291
                "dynamic_backup_path": True,
1292
            },
1293
            {
1294
                "primary_path": [
1295
                    {
1296
                        "endpoint_a": {"id": "00:00:00:00:00:00:00:01:1"},
1297
                        "endpoint_b": {"id": "00:00:00:00:00:00:00:02:2"},
1298
                    }
1299
                ]
1300
            },
1301
            {"priority": 3},
1302
            {"enable": True},
1303
        ]
1304
1305
        evc_as_dict_mock.return_value = payloads[0]
1306
        response = api.post(
1307
            f"{self.server_name_url}/v2/evc/",
1308
            data=json.dumps(payloads[0]),
1309
            content_type="application/json",
1310
        )
1311
        self.assertEqual(201, response.status_code)
1312
1313
        evc_deploy.reset_mock()
1314
        evc_as_dict_mock.return_value = payloads[1]
1315
        current_data = json.loads(response.data)
1316
        circuit_id = current_data["circuit_id"]
1317
        response = api.patch(
1318
            f"{self.server_name_url}/v2/evc/{circuit_id}",
1319
            data=json.dumps(payloads[1]),
1320
            content_type="application/json",
1321
        )
1322
        # evc_deploy.assert_called_once()
1323
        self.assertEqual(200, response.status_code)
1324
1325
        evc_deploy.reset_mock()
1326
        evc_as_dict_mock.return_value = payloads[2]
1327
        response = api.patch(
1328
            f"{self.server_name_url}/v2/evc/{circuit_id}",
1329
            data=json.dumps(payloads[2]),
1330
            content_type="application/json",
1331
        )
1332
        evc_deploy.assert_not_called()
1333
        self.assertEqual(200, response.status_code)
1334
1335
        evc_deploy.reset_mock()
1336
        response = api.patch(
1337
            f"{self.server_name_url}/v2/evc/{circuit_id}",
1338
            data='{"priority":5,}',
1339
            content_type="application/json",
1340
        )
1341
        evc_deploy.assert_not_called()
1342
        self.assertEqual(400, response.status_code)
1343
1344
        evc_deploy.reset_mock()
1345
        response = api.patch(
1346
            f"{self.server_name_url}/v2/evc/{circuit_id}",
1347
            data=json.dumps(payloads[3]),
1348
            content_type="application/json",
1349
        )
1350
        evc_deploy.assert_called_once()
1351
        self.assertEqual(200, response.status_code)
1352
1353
        response = api.patch(
1354
            f"{self.server_name_url}/v2/evc/1234",
1355
            data=json.dumps(payloads[1]),
1356
            content_type="application/json",
1357
        )
1358
        current_data = json.loads(response.data)
1359
        expected_data = "circuit_id 1234 not found"
1360
        self.assertEqual(current_data["description"], expected_data)
1361
        self.assertEqual(404, response.status_code)
1362
1363
        api.delete(f"{self.server_name_url}/v2/evc/{circuit_id}")
1364
        evc_deploy.reset_mock()
1365
        response = api.patch(
1366
            f"{self.server_name_url}/v2/evc/{circuit_id}",
1367
            data=json.dumps(payloads[1]),
1368
            content_type="application/json",
1369
        )
1370
        evc_deploy.assert_not_called()
1371
        self.assertEqual(405, response.status_code)
1372
1373 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...
1374
    @patch("napps.kytos.mef_eline.scheduler.Scheduler.add")
1375
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
1376
    @patch("napps.kytos.mef_eline.storehouse.StoreHouse.save_evc")
1377
    @patch("napps.kytos.mef_eline.models.evc.EVC._validate")
1378
    @patch("napps.kytos.mef_eline.main.EVC.as_dict")
1379
    def test_update_circuit_invalid_json(self, *args):
1380
        """Test update a circuit circuit."""
1381
        # pylint: disable=too-many-locals
1382
        (
1383
            evc_as_dict_mock,
1384
            validate_mock,
1385
            save_evc_mock,
1386
            uni_from_dict_mock,
1387
            sched_add_mock,
1388
            evc_deploy_mock,
1389
        ) = args
1390
1391
        validate_mock.return_value = True
1392
        save_evc_mock.return_value = True
1393
        sched_add_mock.return_value = True
1394
        evc_deploy_mock.return_value = True
1395
        uni1 = create_autospec(UNI)
1396
        uni2 = create_autospec(UNI)
1397
        uni1.interface = create_autospec(Interface)
1398
        uni2.interface = create_autospec(Interface)
1399
        uni1.interface.switch = "00:00:00:00:00:00:00:01"
1400
        uni2.interface.switch = "00:00:00:00:00:00:00:02"
1401
        uni_from_dict_mock.side_effect = [uni1, uni2, uni1, uni2]
1402
1403
        api = self.get_app_test_client(self.napp)
1404
        payload1 = {
1405
            "name": "my evc1",
1406
            "uni_a": {
1407
                "interface_id": "00:00:00:00:00:00:00:01:1",
1408
                "tag": {"tag_type": 1, "value": 80},
1409
            },
1410
            "uni_z": {
1411
                "interface_id": "00:00:00:00:00:00:00:02:2",
1412
                "tag": {"tag_type": 1, "value": 1},
1413
            },
1414
            "dynamic_backup_path": True,
1415
        }
1416
1417
        payload2 = {
1418
            "dynamic_backup_path": False,
1419
        }
1420
1421
        evc_as_dict_mock.return_value = payload1
1422
        response = api.post(
1423
            f"{self.server_name_url}/v2/evc/",
1424
            data=json.dumps(payload1),
1425
            content_type="application/json",
1426
        )
1427
        self.assertEqual(201, response.status_code)
1428
1429
        evc_as_dict_mock.return_value = payload2
1430
        current_data = json.loads(response.data)
1431
        circuit_id = current_data["circuit_id"]
1432
        response = api.patch(
1433
            f"{self.server_name_url}/v2/evc/{circuit_id}",
1434
            data=payload2,
1435
            content_type="application/json",
1436
        )
1437
        current_data = json.loads(response.data)
1438
        expected_data = "The request body is not a well-formed JSON."
1439
        self.assertEqual(current_data["description"], expected_data)
1440
        self.assertEqual(400, response.status_code)
1441
1442
    @patch("napps.kytos.mef_eline.models.evc.EVC.deploy")
1443
    @patch("napps.kytos.mef_eline.scheduler.Scheduler.add")
1444
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
1445
    @patch("napps.kytos.mef_eline.main.Main._link_from_dict")
1446
    @patch("napps.kytos.mef_eline.storehouse.StoreHouse.save_evc")
1447
    @patch("napps.kytos.mef_eline.models.evc.EVC._validate")
1448
    @patch("napps.kytos.mef_eline.main.EVC.as_dict")
1449
    @patch("napps.kytos.mef_eline.models.path.Path.is_valid")
1450
    def test_update_circuit_invalid_path(self, *args):
1451
        """Test update a circuit circuit."""
1452
        # pylint: disable=too-many-locals
1453
        (
1454
            is_valid_mock,
1455
            evc_as_dict_mock,
1456
            validate_mock,
1457
            save_evc_mock,
1458
            link_from_dict_mock,
1459
            uni_from_dict_mock,
1460
            sched_add_mock,
1461
            evc_deploy_mock,
1462
        ) = args
1463
1464
        is_valid_mock.side_effect = InvalidPath("error")
1465
        validate_mock.return_value = True
1466
        save_evc_mock.return_value = True
1467
        sched_add_mock.return_value = True
1468
        evc_deploy_mock.return_value = True
1469
        link_from_dict_mock.return_value = 1
1470
        uni1 = create_autospec(UNI)
1471
        uni2 = create_autospec(UNI)
1472
        uni1.interface = create_autospec(Interface)
1473
        uni2.interface = create_autospec(Interface)
1474
        uni1.interface.switch = "00:00:00:00:00:00:00:01"
1475
        uni2.interface.switch = "00:00:00:00:00:00:00:02"
1476
        uni_from_dict_mock.side_effect = [uni1, uni2, uni1, uni2]
1477
1478
        api = self.get_app_test_client(self.napp)
1479
        payload1 = {
1480
            "name": "my evc1",
1481
            "uni_a": {
1482
                "interface_id": "00:00:00:00:00:00:00:01:1",
1483
                "tag": {"tag_type": 1, "value": 80},
1484
            },
1485
            "uni_z": {
1486
                "interface_id": "00:00:00:00:00:00:00:02:2",
1487
                "tag": {"tag_type": 1, "value": 1},
1488
            },
1489
            "dynamic_backup_path": True,
1490
        }
1491
1492
        payload2 = {
1493
            "primary_path": [
1494
                {
1495
                    "endpoint_a": {"id": "00:00:00:00:00:00:00:01:1"},
1496
                    "endpoint_b": {"id": "00:00:00:00:00:00:00:02:2"},
1497
                }
1498
            ]
1499
        }
1500
1501
        evc_as_dict_mock.return_value = payload1
1502
        response = api.post(
1503
            f"{self.server_name_url}/v2/evc/",
1504
            data=json.dumps(payload1),
1505
            content_type="application/json",
1506
        )
1507
        self.assertEqual(201, response.status_code)
1508
1509
        evc_as_dict_mock.return_value = payload2
1510
        current_data = json.loads(response.data)
1511
        circuit_id = current_data["circuit_id"]
1512
        response = api.patch(
1513
            f"{self.server_name_url}/v2/evc/{circuit_id}",
1514
            data=json.dumps(payload2),
1515
            content_type="application/json",
1516
        )
1517
        current_data = json.loads(response.data)
1518
        expected_data = "primary_path is not a valid path: error"
1519
        self.assertEqual(400, response.status_code)
1520
        self.assertEqual(current_data["description"], expected_data)
1521
1522 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...
1523
    @patch("napps.kytos.mef_eline.scheduler.Scheduler.add")
1524
    @patch("napps.kytos.mef_eline.main.Main._uni_from_dict")
1525
    @patch("napps.kytos.mef_eline.storehouse.StoreHouse.save_evc")
1526
    @patch("napps.kytos.mef_eline.models.evc.EVC._validate")
1527
    @patch("napps.kytos.mef_eline.main.EVC.as_dict")
1528
    def test_update_evc_no_json_mime(self, *args):
1529
        """Test update a circuit with wrong mimetype."""
1530
        # pylint: disable=too-many-locals
1531
        (
1532
            evc_as_dict_mock,
1533
            validate_mock,
1534
            save_evc_mock,
1535
            uni_from_dict_mock,
1536
            sched_add_mock,
1537
            evc_deploy_mock,
1538
        ) = args
1539
1540
        validate_mock.return_value = True
1541
        save_evc_mock.return_value = True
1542
        sched_add_mock.return_value = True
1543
        evc_deploy_mock.return_value = True
1544
        uni1 = create_autospec(UNI)
1545
        uni2 = create_autospec(UNI)
1546
        uni1.interface = create_autospec(Interface)
1547
        uni2.interface = create_autospec(Interface)
1548
        uni1.interface.switch = "00:00:00:00:00:00:00:01"
1549
        uni2.interface.switch = "00:00:00:00:00:00:00:02"
1550
        uni_from_dict_mock.side_effect = [uni1, uni2, uni1, uni2]
1551
1552
        api = self.get_app_test_client(self.napp)
1553
        payload1 = {
1554
            "name": "my evc1",
1555
            "uni_a": {
1556
                "interface_id": "00:00:00:00:00:00:00:01:1",
1557
                "tag": {"tag_type": 1, "value": 80},
1558
            },
1559
            "uni_z": {
1560
                "interface_id": "00:00:00:00:00:00:00:02:2",
1561
                "tag": {"tag_type": 1, "value": 1},
1562
            },
1563
            "dynamic_backup_path": True,
1564
        }
1565
1566
        payload2 = {"dynamic_backup_path": False}
1567
1568
        evc_as_dict_mock.return_value = payload1
1569
        response = api.post(
1570
            f"{self.server_name_url}/v2/evc/",
1571
            data=json.dumps(payload1),
1572
            content_type="application/json",
1573
        )
1574
        self.assertEqual(201, response.status_code)
1575
1576
        evc_as_dict_mock.return_value = payload2
1577
        current_data = json.loads(response.data)
1578
        circuit_id = current_data["circuit_id"]
1579
        response = api.patch(
1580
            f"{self.server_name_url}/v2/evc/{circuit_id}", data=payload2
1581
        )
1582
        current_data = json.loads(response.data)
1583
        expected_data = "The request body mimetype is not application/json."
1584
        self.assertEqual(current_data["description"], expected_data)
1585
        self.assertEqual(415, response.status_code)
1586
1587
    def test_delete_no_evc(self):
1588
        """Test delete when EVC does not exist."""
1589
        api = self.get_app_test_client(self.napp)
1590
        response = api.delete(f"{self.server_name_url}/v2/evc/123")
1591
        current_data = json.loads(response.data)
1592
        expected_data = "circuit_id 123 not found"
1593
        self.assertEqual(current_data["description"], expected_data)
1594
        self.assertEqual(404, response.status_code)
1595
1596
    def test_handle_link_up(self):
1597
        """Test handle_link_up method."""
1598
        evc_mock = create_autospec(EVC)
1599
        evc_mock.is_enabled = MagicMock(side_effect=[True, False, True])
1600
        evc_mock.lock = MagicMock()
1601
        type(evc_mock).archived = PropertyMock(
1602
            side_effect=[True, False, False]
1603
        )
1604
        evcs = [evc_mock, evc_mock, evc_mock]
1605
        event = KytosEvent(name="test", content={"link": "abc"})
1606
        self.napp.circuits = dict(zip(["1", "2", "3"], evcs))
1607
        self.napp.handle_link_up(event)
1608
        evc_mock.handle_link_up.assert_called_once_with("abc")
1609
1610
    def test_handle_link_down(self):
1611
        """Test handle_link_down method."""
1612
        evc_mock = create_autospec(EVC)
1613
        evc_mock.is_affected_by_link = MagicMock(
1614
            side_effect=[True, False, True]
1615
        )
1616
        evc_mock.lock = MagicMock()
1617
        evc_mock.handle_link_down = MagicMock(side_effect=[True, True])
1618
        evcs = [evc_mock, evc_mock, evc_mock]
1619
        event = KytosEvent(name="test", content={"link": "abc"})
1620
        self.napp.circuits = dict(zip(["1", "2", "3"], evcs))
1621
        self.napp.handle_link_down(event)
1622
        evc_mock.handle_link_down.assert_has_calls([call(), call()])
1623
1624
    def test_add_metadata(self):
1625
        """Test method to add metadata"""
1626
        evc_mock = create_autospec(EVC)
1627
        evc_mock.metadata = {}
1628
        evc_mock.id = 1234
1629
        self.napp.circuits = {"1234": evc_mock}
1630
1631
        api = self.get_app_test_client(self.napp)
1632
        payload = {"metadata1": 1, "metadata2": 2}
1633
        response = api.post(
1634
            f"{self.server_name_url}/v2/evc/1234/metadata",
1635
            data=json.dumps(payload),
1636
            content_type="application/json",
1637
        )
1638
1639
        self.assertEqual(response.status_code, 201)
1640
        evc_mock.extend_metadata.assert_called_with(payload)
1641