Passed
Pull Request — master (#114)
by Vinicius
06:07
created

TestMain.test_notify_link_up_if_status()   A

Complexity

Conditions 1

Size

Total Lines 27
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 22
nop 4
dl 0
loc 27
rs 9.352
c 0
b 0
f 0
1
"""Module to test the main napp file."""
2
# pylint: disable=import-error,no-name-in-module,wrong-import-order
3
# pylint: disable=import-outside-toplevel
4
import pytest
5
import json
6
import time
7
from datetime import timedelta
8
from unittest import TestCase
9
from unittest.mock import MagicMock, create_autospec, patch
10
11
from kytos.core.common import EntityStatus
12
from kytos.core.helpers import now
13
from kytos.core.interface import Interface
14
from kytos.core.link import Link
15
from kytos.core.switch import Switch
16
from kytos.lib.helpers import (get_interface_mock, get_link_mock,
17
                               get_controller_mock, get_switch_mock,
18
                               get_test_client)
19
from napps.kytos.topology.exceptions import RestoreError
20
from tests.unit.helpers import get_napp_urls
21
22
23
@pytest.mark.parametrize("liveness_status, status",
24
                         [("up", EntityStatus.UP),
25
                          ("down", EntityStatus.DOWN)])
26
def test_handle_link_liveness_status(liveness_status, status) -> None:
27
    """Test handle link liveness."""
28
    from napps.kytos.topology.main import Main
29
    Main.get_topo_controller = MagicMock()
30
    napp = Main(get_controller_mock())
31
    napp.notify_topology_update = MagicMock()
32
    napp.notify_link_status_change = MagicMock()
33
34
    link = MagicMock(id="some_id", status=status)
35
    napp.handle_link_liveness_status(link, liveness_status)
36
37
    add_link_meta = napp.topo_controller.add_link_metadata
38
    add_link_meta.assert_called_with(link.id, {"liveness_status":
39
                                               liveness_status})
40
    link.extend_metadata.assert_called_with({"liveness_status":
41
                                             liveness_status})
42
    assert napp.notify_topology_update.call_count == 1
43
    assert napp.notify_link_status_change.call_count == 1
44
    reason = f"liveness_{liveness_status}"
45
    napp.notify_link_status_change.assert_called_with(link, reason=reason)
46
47
48
# pylint: disable=too-many-public-methods
49
class TestMain(TestCase):
50
    """Test the Main class."""
51
52
    # pylint: disable=too-many-public-methods, protected-access,C0302
53
54
    def setUp(self):
55
        """Execute steps before each tests.
56
57
        Set the server_name_url_url from kytos/topology
58
        """
59
        self.server_name_url = 'http://localhost:8181/api/kytos/topology'
60
61
        patch('kytos.core.helpers.run_on_thread', lambda x: x).start()
62
        # pylint: disable=import-outside-toplevel
63
        from napps.kytos.topology.main import Main
64
        Main.get_topo_controller = MagicMock()
65
        self.addCleanup(patch.stopall)
66
        self.napp = Main(get_controller_mock())
67
68
    def test_get_event_listeners(self):
69
        """Verify all event listeners registered."""
70
        expected_events = ['kytos/core.shutdown',
71
                           'kytos/core.shutdown.kytos/topology',
72
                           'kytos/maintenance.start_link',
73
                           'kytos/maintenance.end_link',
74
                           'kytos/maintenance.start_switch',
75
                           'kytos/maintenance.end_switch',
76
                           'kytos/.*.link_available_tags',
77
                           '.*.topo_controller.upsert_switch',
78
                           '.*.of_lldp.network_status.updated',
79
                           '.*.interface.is.nni',
80
                           '.*.connection.lost',
81
                           '.*.switch.interfaces.created',
82
                           '.*.topology.switch.interface.created',
83
                           '.*.switch.interface.deleted',
84
                           '.*.switch.interface.link_down',
85
                           '.*.switch.interface.link_up',
86
                           '.*.switch.(new|reconnected)',
87
                           'kytos/.*.liveness.(up|down)',
88
                           'kytos/.*.liveness.disabled',
89
                           'kytos/topology.get',
90
                           '.*.switch.port.created']
91
        actual_events = self.napp.listeners()
92
        self.assertCountEqual(expected_events, actual_events)
93
94 View Code Duplication
    def test_verify_api_urls(self):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
95
        """Verify all APIs registered."""
96
        expected_urls = [
97
         ({}, {'GET', 'OPTIONS', 'HEAD'}, '/api/kytos/topology/v3/interfaces'),
98
         ({}, {'GET', 'OPTIONS', 'HEAD'}, '/api/kytos/topology/v3/switches'),
99
         ({}, {'GET', 'OPTIONS', 'HEAD'}, '/api/kytos/topology/v3/links'),
100
         ({}, {'GET', 'OPTIONS', 'HEAD'}, '/api/kytos/topology/v3/'),
101
         ({'dpid': '[dpid]'}, {'POST', 'OPTIONS'},
102
          '/api/kytos/topology/v3/interfaces/switch/<dpid>/disable'),
103
         ({'dpid': '[dpid]'}, {'POST', 'OPTIONS'},
104
          '/api/kytos/topology/v3/interfaces/switch/<dpid>/enable'),
105
         ({'key': '[key]', 'interface_id': '[interface_id]'},
106
          {'OPTIONS', 'DELETE'},
107
          '/api/kytos/topology/v3/interfaces/<interface_id>/metadata/<key>'),
108
         ({'interface_id': '[interface_id]'}, {'POST', 'OPTIONS'},
109
          '/api/kytos/topology/v3/interfaces/<interface_id>/metadata'),
110
         ({'interface_id': '[interface_id]'}, {'GET', 'OPTIONS', 'HEAD'},
111
          '/api/kytos/topology/v3/interfaces/<interface_id>/metadata'),
112
         ({'interface_disable_id': '[interface_disable_id]'},
113
          {'POST', 'OPTIONS'},
114
          '/api/kytos/topology/v3/interfaces/<interface_disable_id>/disable'),
115
         ({'interface_enable_id': '[interface_enable_id]'},
116
          {'POST', 'OPTIONS'},
117
          '/api/kytos/topology/v3/interfaces/<interface_enable_id>/enable'),
118
         ({'dpid': '[dpid]', 'key': '[key]'}, {'OPTIONS', 'DELETE'},
119
          '/api/kytos/topology/v3/switches/<dpid>/metadata/<key>'),
120
         ({'dpid': '[dpid]'}, {'POST', 'OPTIONS'},
121
          '/api/kytos/topology/v3/switches/<dpid>/metadata'),
122
         ({'dpid': '[dpid]'}, {'GET', 'OPTIONS', 'HEAD'},
123
          '/api/kytos/topology/v3/switches/<dpid>/metadata'),
124
         ({'dpid': '[dpid]'}, {'POST', 'OPTIONS'},
125
          '/api/kytos/topology/v3/switches/<dpid>/disable'),
126
         ({'dpid': '[dpid]'}, {'POST', 'OPTIONS'},
127
          '/api/kytos/topology/v3/switches/<dpid>/enable'),
128
         ({'link_id': '[link_id]', 'key': '[key]'}, {'OPTIONS', 'DELETE'},
129
          '/api/kytos/topology/v3/links/<link_id>/metadata/<key>'),
130
         ({'link_id': '[link_id]'}, {'POST', 'OPTIONS'},
131
          '/api/kytos/topology/v3/links/<link_id>/metadata'),
132
         ({'link_id': '[link_id]'}, {'GET', 'OPTIONS', 'HEAD'},
133
          '/api/kytos/topology/v3/links/<link_id>/metadata'),
134
         ({'link_id': '[link_id]'}, {'POST', 'OPTIONS'},
135
          '/api/kytos/topology/v3/links/<link_id>/disable'),
136
         ({'link_id': '[link_id]'}, {'POST', 'OPTIONS'},
137
          '/api/kytos/topology/v3/links/<link_id>/enable')]
138
139
        urls = get_napp_urls(self.napp)
140
        self.assertEqual(expected_urls, urls)
141
142
    def test_get_link_or_create(self):
143
        """Test _get_link_or_create."""
144
        dpid_a = "00:00:00:00:00:00:00:01"
145
        dpid_b = "00:00:00:00:00:00:00:02"
146
        mock_switch_a = get_switch_mock(dpid_a, 0x04)
147
        mock_switch_b = get_switch_mock(dpid_b, 0x04)
148
        mock_interface_a = get_interface_mock('s1-eth1', 1, mock_switch_a)
149
        mock_interface_b = get_interface_mock('s2-eth1', 1, mock_switch_b)
150
        mock_interface_a.id = dpid_a
151
        mock_interface_b.id = dpid_b
152
153
        link, created = self.napp._get_link_or_create(mock_interface_a,
154
                                                      mock_interface_b)
155
        self.assertTrue(created)
156
        self.assertEqual(link.endpoint_a.id, dpid_a)
157
        self.assertEqual(link.endpoint_b.id, dpid_b)
158
159
        link, created = self.napp._get_link_or_create(mock_interface_a,
160
                                                      mock_interface_b)
161
        self.assertFalse(created)
162
163
    def test_get_link_from_interface(self):
164
        """Test _get_link_from_interface."""
165
        mock_switch_a = get_switch_mock("00:00:00:00:00:00:00:01", 0x04)
166
        mock_switch_b = get_switch_mock("00:00:00:00:00:00:00:02", 0x04)
167
        mock_interface_a = get_interface_mock('s1-eth1', 1, mock_switch_a)
168
        mock_interface_b = get_interface_mock('s2-eth1', 1, mock_switch_b)
169
        mock_interface_c = get_interface_mock('s2-eth1', 2, mock_switch_b)
170
        mock_link = get_link_mock(mock_interface_a, mock_interface_b)
171
        self.napp.links = {'0e2b5d7bc858b9f38db11b69': mock_link}
172
        response = self.napp._get_link_from_interface(mock_interface_a)
173
        self.assertEqual(response, mock_link)
174
175
        response = self.napp._get_link_from_interface(mock_interface_c)
176
        self.assertEqual(response, None)
177
178
    def test_get_topology(self):
179
        """Test get_topology."""
180
        dpid_a = "00:00:00:00:00:00:00:01"
181
        dpid_b = "00:00:00:00:00:00:00:02"
182
        expected = {
183
                      "topology": {
184
                        "switches": {
185
                          "00:00:00:00:00:00:00:01": {
186
                            "metadata": {
187
                              "lat": "0.0",
188
                              "lng": "-30.0"
189
                            }
190
                          },
191
                          "00:00:00:00:00:00:00:02": {
192
                            "metadata": {
193
                              "lat": "0.0",
194
                              "lng": "-30.0"
195
                            }
196
                          }
197
                        },
198
                        "links": {
199
                          "cf0f4071be4": {
200
                            "id": "cf0f4071be4"
201
                          }
202
                        }
203
                      }
204
                    }
205
206
        mock_switch_a = get_switch_mock(dpid_a, 0x04)
207
        mock_switch_b = get_switch_mock(dpid_b, 0x04)
208
        mock_interface_a = get_interface_mock('s1-eth1', 1, mock_switch_a)
209
        mock_interface_b = get_interface_mock('s2-eth1', 1, mock_switch_b)
210
211
        mock_link = get_link_mock(mock_interface_a, mock_interface_b)
212
        mock_link.id = 'cf0f4071be4'
213
        mock_switch_a.id = dpid_a
214
        mock_switch_a.as_dict.return_value = {'metadata': {'lat': '0.0',
215
                                              'lng': '-30.0'}}
216
        mock_switch_b.id = dpid_b
217
        mock_switch_b.as_dict.return_value = {'metadata': {'lat': '0.0',
218
                                              'lng': '-30.0'}}
219
220
        self.napp.controller.switches = {dpid_a: mock_switch_a,
221
                                         dpid_b: mock_switch_b}
222
223
        self.napp.links = {"cf0f4071be4": mock_link}
224
        mock_link.as_dict.return_value = {"id": "cf0f4071be4"}
225
        api = get_test_client(self.napp.controller, self.napp)
226
227
        url = f'{self.server_name_url}/v3/'
228
        response = api.get(url)
229
        self.assertEqual(response.status_code, 200)
230
        self.assertEqual(json.loads(response.data), expected)
231
232
    def test_load_topology(self):
233
        """Test load_topology."""
234
        link_id = \
235
            'cf0f4071be426b3f745027f5d22bc61f8312ae86293c9b28e7e66015607a9260'
236
        dpid_a = '00:00:00:00:00:00:00:01'
237
        dpid_b = '00:00:00:00:00:00:00:02'
238
        topology = {
239
            "topology": {
240
                "links": {
241
                    link_id: {
242
                        "enabled": True,
243
                        "id": link_id,
244
                        "endpoint_a": {"id": f"{dpid_a}:2"},
245
                        "endpoint_b": {"id": f"{dpid_b}:2"},
246
                    }
247
                },
248
                "switches": {
249
                    dpid_a: {
250
                        "dpid": dpid_a,
251
                        "enabled": True,
252
                        "metadata": {},
253
                        "id": dpid_a,
254
                        "interfaces": {
255
                            f"{dpid_a}:2": {
256
                                "enabled": True,
257
                                "metadata": {},
258
                                "lldp": True,
259
                                "port_number": 2,
260
                                "name": "s1-eth2",
261
                            }
262
                        },
263
                    },
264
                    dpid_b: {
265
                        "dpid": dpid_b,
266
                        "enabled": True,
267
                        "metadata": {},
268
                        "id": dpid_b,
269
                        "interfaces": {
270
                            f"{dpid_b}:2": {
271
                                "enabled": True,
272
                                "metadata": {},
273
                                "lldp": True,
274
                                "port_number": 2,
275
                                "name": "s2-eth2",
276
                            }
277
                        },
278
                    },
279
                },
280
            }
281
        }
282
        switches_expected = [dpid_a, dpid_b]
283
        interfaces_expected = [f'{dpid_a}:2', f'{dpid_b}:2']
284
        links_expected = [link_id]
285
        self.napp.topo_controller.get_topology.return_value = topology
286
        self.napp.load_topology()
287
        self.assertListEqual(switches_expected,
288
                             list(self.napp.controller.switches.keys()))
289
        interfaces = []
290
        for switch in self.napp.controller.switches.values():
291
            for iface in switch.interfaces.values():
292
                interfaces.append(iface.id)
293
        self.assertListEqual(interfaces_expected, interfaces)
294
        self.assertListEqual(links_expected, list(self.napp.links.keys()))
295
296
    @patch('napps.kytos.topology.main.Main._load_switch')
297
    @patch('napps.kytos.topology.main.Main._load_link')
298
    def test_load_topology_does_nothing(self, *args):
299
        """Test _load_network_status doing nothing."""
300
        (mock_load_link, mock_load_switch) = args
301
        self.napp.topo_controller.get_topology.return_value = {
302
            "topology": {"switches": {}, "links": {}}
303
        }
304
        self.napp.topo_controller.load_topology()
305
        assert mock_load_link.call_count == 0
306
        assert mock_load_switch.call_count == 0
307
308
    @patch('napps.kytos.topology.main.Main._load_switch')
309
    @patch('napps.kytos.topology.main.log')
310
    def test_load_topology_fail_switch(self, *args):
311
        """Test load_topology failure in switch."""
312
        (mock_log, mock_load_switch) = args
313
        topology = {
314
            'topology': {
315
                'links': {},
316
                'switches': {
317
                    '1': {}
318
                }
319
            }
320
        }
321
        mock_log.error.return_value = True
322
        self.napp.topo_controller.get_topology.return_value = topology
323
        mock_load_switch.side_effect = Exception('xpto')
324
        self.napp.load_topology()
325
        error = 'Error loading switch: xpto'
326
        mock_log.error.assert_called_with(error)
327
328
    @patch('napps.kytos.topology.main.Main._load_link')
329
    @patch('napps.kytos.topology.main.log')
330
    def test_load_topology_fail_link(self, *args):
331
        """Test load_topology failure in link."""
332
        (mock_log, mock_load_link) = args
333
        topology = {
334
            'topology': {
335
                'switches': {},
336
                'links': {
337
                    '1': {}
338
                }
339
            }
340
        }
341
        mock_log.error.return_value = True
342
        self.napp.topo_controller.get_topology.return_value = topology
343
        mock_load_link.side_effect = Exception('xpto')
344
        self.napp.load_topology()
345
        error = 'Error loading link 1: xpto'
346
        mock_log.error.assert_called_with(error)
347
348
    @patch('napps.kytos.topology.main.Main.load_interfaces_available_tags')
349
    @patch('napps.kytos.topology.main.KytosEvent')
350
    @patch('kytos.core.buffers.KytosEventBuffer.put')
351
    def test_load_switch(self, *args):
352
        """Test _load_switch."""
353
        (mock_buffers_put, mock_event, mock_load_tags) = args
354
        dpid_a = "00:00:00:00:00:00:00:01"
355
        dpid_x = "00:00:00:00:00:00:00:XX"
356
        iface_a = f'{dpid_a}:1'
357
        switch_attrs = {
358
            'dpid': dpid_a,
359
            'enabled': True,
360
            'id': dpid_a,
361
            'metadata': {},
362
            'interfaces': {
363
                iface_a: {
364
                    'enabled': True,
365
                    'lldp': True,
366
                    'id': iface_a,
367
                    'switch': dpid_a,
368
                    'metadata': {},
369
                    'name': 's2-eth1',
370
                    'port_number': 1
371
                }
372
            }
373
        }
374
        self.napp._load_switch(dpid_a, switch_attrs)
375
376
        self.assertEqual(len(self.napp.controller.switches), 1)
377
        self.assertIn(dpid_a, self.napp.controller.switches)
378
        self.assertNotIn(dpid_x, self.napp.controller.switches)
379
        switch = self.napp.controller.switches[dpid_a]
380
        interface_details = self.napp.topo_controller.get_interfaces_details
381
        interface_details.assert_called_once_with([iface_a])
382
        mock_load_tags.assert_called()
383
384
        self.assertEqual(switch.id, dpid_a)
385
        self.assertEqual(switch.dpid, dpid_a)
386
        self.assertTrue(switch.is_enabled())
387
        self.assertFalse(switch.is_active())
388
389
        self.assertEqual(len(switch.interfaces), 1)
390
        self.assertIn(1, switch.interfaces)
391
        self.assertNotIn(2, switch.interfaces)
392
        mock_event.assert_called()
393
        mock_buffers_put.assert_called()
394
395
        interface = switch.interfaces[1]
396
        self.assertEqual(interface.id, iface_a)
397
        self.assertEqual(interface.switch.id, dpid_a)
398
        self.assertEqual(interface.port_number, 1)
399
        self.assertTrue(interface.is_enabled())
400
        self.assertTrue(interface.lldp)
401
        self.assertTrue(interface.uni)
402
        self.assertFalse(interface.nni)
403
404
    def test_load_switch_attrs(self):
405
        """Test _load_switch."""
406
        dpid_b = "00:00:00:00:00:00:00:02"
407
        iface_b = f'{dpid_b}:1'
408
        switch_attrs = {
409
            "active": True,
410
            "connection": "127.0.0.1:43230",
411
            "data_path": "XX Human readable desc of dp",
412
            "dpid": "00:00:00:00:00:00:00:02",
413
            "enabled": False,
414
            "hardware": "Open vSwitch",
415
            "id": "00:00:00:00:00:00:00:02",
416
            "interfaces": {
417
                "00:00:00:00:00:00:00:02:1": {
418
                    "active": True,
419
                    "enabled": False,
420
                    "id": "00:00:00:00:00:00:00:02:1",
421
                    "link": "",
422
                    "lldp": False,
423
                    "mac": "de:58:c3:30:b7:b7",
424
                    "metadata": {},
425
                    "name": "s2-eth1",
426
                    "nni": False,
427
                    "port_number": 1,
428
                    "speed": 1250000000,
429
                    "switch": "00:00:00:00:00:00:00:02",
430
                    "type": "interface",
431
                    "uni": True
432
                },
433
            },
434
            "manufacturer": "Nicira, Inc.",
435
            "metadata": {},
436
            "name": "00:00:00:00:00:00:00:04",
437
            "ofp_version": "0x04",
438
            "serial": "XX serial number",
439
            "software": "2.10.7",
440
            "type": "switch"
441
        }
442
443
        self.assertEqual(len(self.napp.controller.switches), 0)
444
        self.napp._load_switch(dpid_b, switch_attrs)
445
        self.assertEqual(len(self.napp.controller.switches), 1)
446
        self.assertIn(dpid_b, self.napp.controller.switches)
447
448
        switch = self.napp.controller.switches[dpid_b]
449
        self.assertEqual(switch.id, dpid_b)
450
        self.assertEqual(switch.dpid, dpid_b)
451
        self.assertFalse(switch.is_enabled())
452
        self.assertFalse(switch.is_active())
453
        self.assertEqual(switch.description['manufacturer'], 'Nicira, Inc.')
454
        self.assertEqual(switch.description['hardware'], 'Open vSwitch')
455
        self.assertEqual(switch.description['software'], '2.10.7')
456
        self.assertEqual(switch.description['serial'], 'XX serial number')
457
        self.assertEqual(switch.description['data_path'],
458
                         'XX Human readable desc of dp')
459
460
        self.assertEqual(len(switch.interfaces), 1)
461
        self.assertIn(1, switch.interfaces)
462
        self.assertNotIn(2, switch.interfaces)
463
464
        interface = switch.interfaces[1]
465
        self.assertEqual(interface.id, iface_b)
466
        self.assertEqual(interface.switch.id, dpid_b)
467
        self.assertEqual(interface.port_number, 1)
468
        self.assertFalse(interface.is_enabled())
469
        self.assertFalse(interface.lldp)
470
        self.assertTrue(interface.uni)
471
        self.assertFalse(interface.nni)
472
473
    def test_interfaces_available_tags(self):
474
        """Test load_interfaces_available_tags."""
475
        dpid_a = "00:00:00:00:00:00:00:01"
476
        mock_switch_a = get_switch_mock(dpid_a, 0x04)
477
        mock_interface_a = get_interface_mock('s1-eth1', 1, mock_switch_a)
478
        mock_interface_a.id = dpid_a + ':1'
479
        mock_switch_a.interfaces = {1: mock_interface_a}
480
        tags = [1, 2, 3]
481
        interface_details = [{"id": mock_interface_a.id,
482
                             "available_vlans": tags}]
483
        self.napp.load_interfaces_available_tags(mock_switch_a,
484
                                                 interface_details)
485
        mock_interface_a.set_available_tags.assert_called_once_with(tags)
486
487
    def test_handle_on_link_available_tags(self):
488
        """test_handle_on_link_available_tags."""
489
        dpid_a = "00:00:00:00:00:00:00:01"
490
        dpid_b = "00:00:00:00:00:00:00:02"
491
        tag = MagicMock()
492
        tag.value = 1
493
        tags = [tag]
494
        link_mock = MagicMock()
495
        mock_switch_a = get_switch_mock(dpid_a, 0x04)
496
        mock_interface_a = get_interface_mock('s1-eth1', 1, mock_switch_a)
497
        mock_interface_a.available_tags = tags
498
        mock_switch_b = get_switch_mock(dpid_b, 0x04)
499
        mock_interface_b = get_interface_mock('s2-eth1', 1, mock_switch_b)
500
        mock_interface_b.available_tags = tags
501
        link_id = '4d42dc08522'
502
        link_mock.id = link_id
503
        link_mock.endpoint_a = mock_interface_a
504
        link_mock.endpoint_b = mock_interface_b
505
506
        self.napp.links[link_id] = link_mock
507
        self.napp.handle_on_link_available_tags(link_mock)
508
        bulk_upsert = self.napp.topo_controller.bulk_upsert_interface_details
509
        bulk_upsert.assert_called_once_with(
510
            [
511
                (
512
                    "00:00:00:00:00:00:00:01:1",
513
                    {"_id": "00:00:00:00:00:00:00:01:1",
514
                     "available_vlans": [1]},
515
                ),
516
                (
517
                    "00:00:00:00:00:00:00:02:1",
518
                    {"_id": "00:00:00:00:00:00:00:02:1",
519
                     "available_vlans": [1]},
520
                ),
521
            ]
522
        )
523
524
    def test_load_link(self):
525
        """Test _load_link."""
526
        dpid_a = "00:00:00:00:00:00:00:01"
527
        dpid_b = "00:00:00:00:00:00:00:02"
528
        link_id = '4d42dc08522'
529
        mock_switch_a = get_switch_mock(dpid_a, 0x04)
530
        mock_switch_b = get_switch_mock(dpid_b, 0x04)
531
        mock_interface_a = get_interface_mock('s1-eth1', 1, mock_switch_a)
532
        mock_interface_a.id = dpid_a + ':1'
533
        mock_interface_a.available_tags = [1, 2, 3]
534
        mock_interface_b = get_interface_mock('s2-eth1', 1, mock_switch_b)
535
        mock_interface_b.id = dpid_b + ':1'
536
        mock_interface_b.available_tags = [1, 2, 3]
537
        mock_switch_a.interfaces = {1: mock_interface_a}
538
        mock_switch_b.interfaces = {1: mock_interface_b}
539
        self.napp.controller.switches[dpid_a] = mock_switch_a
540
        self.napp.controller.switches[dpid_b] = mock_switch_b
541
        link_attrs = {
542
            'enabled': True,
543
            'id': link_id,
544
            'metadata': {},
545
            'endpoint_a': {
546
                'id': mock_interface_a.id
547
            },
548
            'endpoint_b': {
549
                'id': mock_interface_b.id
550
            }
551
        }
552
553
        self.napp._load_link(link_attrs)
554
555
        self.assertEqual(len(self.napp.links), 1)
556
        link = list(self.napp.links.values())[0]
557
558
        self.assertEqual(link.endpoint_a.id, mock_interface_a.id)
559
        self.assertEqual(link.endpoint_b.id, mock_interface_b.id)
560
        self.assertTrue(mock_interface_a.nni)
561
        self.assertTrue(mock_interface_b.nni)
562
        self.assertEqual(mock_interface_a.update_link.call_count, 1)
563
        self.assertEqual(mock_interface_b.update_link.call_count, 1)
564
565
        # test enable/disable
566
        link_id = '4d42dc08522'
567
        mock_interface_a = get_interface_mock('s1-eth1', 1, mock_switch_a)
568
        mock_interface_b = get_interface_mock('s2-eth1', 1, mock_switch_b)
569
        mock_link = get_link_mock(mock_interface_a, mock_interface_b)
570
        mock_link.id = link_id
571
        with patch('napps.kytos.topology.main.Main._get_link_or_create',
572
                   return_value=(mock_link, True)):
573
            # enable link
574
            link_attrs['enabled'] = True
575
            self.napp.links = {link_id: mock_link}
576
            self.napp._load_link(link_attrs)
577
            self.assertEqual(mock_link.enable.call_count, 1)
578
            # disable link
579
            link_attrs['enabled'] = False
580
            self.napp.links = {link_id: mock_link}
581
            self.napp._load_link(link_attrs)
582
            self.assertEqual(mock_link.disable.call_count, 1)
583
584
    @patch('napps.kytos.topology.main.Main._get_link_or_create')
585
    def test_fail_load_link(self, get_link_or_create_mock):
586
        """Test fail load_link."""
587
        dpid_a = '00:00:00:00:00:00:00:01'
588
        dpid_b = '00:00:00:00:00:00:00:02'
589
        link_id = '4d42dc08522'
590
        mock_switch_a = get_switch_mock(dpid_a)
591
        mock_switch_b = get_switch_mock(dpid_b)
592
        mock_interface_a_1 = get_interface_mock('s1-eth1', 1, mock_switch_a)
593
        mock_interface_b_1 = get_interface_mock('s2-eth1', 1, mock_switch_b)
594
        mock_link = get_link_mock(mock_interface_a_1, mock_interface_b_1)
595
        mock_link.id = link_id
596
        self.napp.links = {link_id: mock_link}
597
        get_link_or_create_mock.return_value = mock_link
598
599
        link_attrs_fail = {
600
            'enabled': True,
601
            'id': link_id,
602
            'metadata': {},
603
            'endpoint_a': {
604
                'id': f"{dpid_a}:999",
605
            },
606
            'endpoint_b': {
607
                'id': f"{dpid_b}:999",
608
            }
609
        }
610
        with self.assertRaises(RestoreError):
611
            self.napp._load_link(link_attrs_fail)
612
613
        link_attrs_fail = {
614
            'enabled': True,
615
            'id': link_id,
616
            'endpoint_a': {
617
                'id': f"{dpid_a}:1",
618
            },
619
            'endpoint_b': {
620
                'id': f"{dpid_b}:1",
621
            }
622
        }
623
        with self.assertRaises(RestoreError):
624
            self.napp._load_link(link_attrs_fail)
625
626 View Code Duplication
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
627
    def test_enable_switch(self, mock_notify_topo):
628
        """Test enable_switch."""
629
        dpid = "00:00:00:00:00:00:00:01"
630
        mock_switch = get_switch_mock(dpid)
631
        self.napp.controller.switches = {dpid: mock_switch}
632
        api = get_test_client(self.napp.controller, self.napp)
633
634
        url = f'{self.server_name_url}/v3/switches/{dpid}/enable'
635
        response = api.post(url)
636
        self.assertEqual(response.status_code, 201, response.data)
637
        self.assertEqual(mock_switch.enable.call_count, 1)
638
        self.napp.topo_controller.enable_switch.assert_called_once_with(dpid)
639
        mock_notify_topo.assert_called()
640
641
        # fail case
642
        mock_switch.enable.call_count = 0
643
        dpid = "00:00:00:00:00:00:00:02"
644
        url = f'{self.server_name_url}/v3/switches/{dpid}/enable'
645
        response = api.post(url)
646
        self.assertEqual(response.status_code, 404, response.data)
647
        self.assertEqual(mock_switch.enable.call_count, 0)
648
649 View Code Duplication
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
650
    def test_disable_switch(self, mock_notify_topo):
651
        """Test disable_switch."""
652
        dpid = "00:00:00:00:00:00:00:01"
653
        mock_switch = get_switch_mock(dpid)
654
        self.napp.controller.switches = {dpid: mock_switch}
655
        api = get_test_client(self.napp.controller, self.napp)
656
657
        url = f'{self.server_name_url}/v3/switches/{dpid}/disable'
658
        response = api.post(url)
659
        self.assertEqual(response.status_code, 201, response.data)
660
        self.assertEqual(mock_switch.disable.call_count, 1)
661
        self.napp.topo_controller.disable_switch.assert_called_once_with(dpid)
662
        mock_notify_topo.assert_called()
663
664
        # fail case
665
        mock_switch.disable.call_count = 0
666
        dpid = "00:00:00:00:00:00:00:02"
667
        url = f'{self.server_name_url}/v3/switches/{dpid}/disable'
668
        response = api.post(url)
669
        self.assertEqual(response.status_code, 404, response.data)
670
        self.assertEqual(mock_switch.disable.call_count, 0)
671
672
    def test_get_switch_metadata(self):
673
        """Test get_switch_metadata."""
674
        dpid = "00:00:00:00:00:00:00:01"
675
        mock_switch = get_switch_mock(dpid)
676
        mock_switch.metadata = "A"
677
        self.napp.controller.switches = {dpid: mock_switch}
678
        api = get_test_client(self.napp.controller, self.napp)
679
680
        url = f'{self.server_name_url}/v3/switches/{dpid}/metadata'
681
        response = api.get(url)
682
        self.assertEqual(response.status_code, 200, response.data)
683
684
        # fail case
685
        dpid = "00:00:00:00:00:00:00:02"
686
        url = f'{self.server_name_url}/v3/switches/{dpid}/metadata'
687
        response = api.get(url)
688
        self.assertEqual(response.status_code, 404, response.data)
689
690
    @patch('napps.kytos.topology.main.Main.notify_metadata_changes')
691
    def test_add_switch_metadata(self, mock_metadata_changes):
692
        """Test add_switch_metadata."""
693
        dpid = "00:00:00:00:00:00:00:01"
694
        mock_switch = get_switch_mock(dpid)
695
        self.napp.controller.switches = {dpid: mock_switch}
696
        api = get_test_client(self.napp.controller, self.napp)
697
        payload = {"data": "A"}
698
699
        url = f'{self.server_name_url}/v3/switches/{dpid}/metadata'
700
        response = api.post(url, data=json.dumps(payload),
701
                            content_type='application/json')
702
        self.assertEqual(response.status_code, 201, response.data)
703
        mock_metadata_changes.assert_called()
704
        self.napp.topo_controller.add_switch_metadata.assert_called_once_with(
705
            dpid, payload
706
        )
707
708
        # fail case
709
        dpid = "00:00:00:00:00:00:00:02"
710
        url = f'{self.server_name_url}/v3/switches/{dpid}/metadata'
711
        response = api.post(url, data=json.dumps(payload),
712
                            content_type='application/json')
713
        self.assertEqual(response.status_code, 404, response.data)
714
715
    def test_add_switch_metadata_wrong_format(self):
716
        """Test add_switch_metadata_wrong_format."""
717
        dpid = "00:00:00:00:00:00:00:01"
718
        api = get_test_client(self.napp.controller, self.napp)
719
        payload = 'A'
720
721
        url = f'{self.server_name_url}/v3/switches/{dpid}/metadata'
722
        response = api.post(url, data=payload, content_type='application/json')
723
        self.assertEqual(response.status_code, 400, response.data)
724
725
        payload = None
726
        response = api.post(url, data=json.dumps(payload),
727
                            content_type='application/json')
728
        self.assertEqual(response.status_code, 415, response.data)
729
730
    @patch('napps.kytos.topology.main.Main.notify_metadata_changes')
731
    def test_delete_switch_metadata(self, mock_metadata_changes):
732
        """Test delete_switch_metadata."""
733
        dpid = "00:00:00:00:00:00:00:01"
734
        mock_switch = get_switch_mock(dpid)
735
        mock_switch.metadata = {"A": "A"}
736
        self.napp.controller.switches = {dpid: mock_switch}
737
        api = get_test_client(self.napp.controller, self.napp)
738
739
        key = "A"
740
        url = f'{self.server_name_url}/v3/switches/{dpid}/metadata/{key}'
741
        response = api.delete(url)
742
        self.assertEqual(mock_metadata_changes.call_count, 1)
743
        self.assertEqual(response.status_code, 200, response.data)
744
        del_key_mock = self.napp.topo_controller.delete_switch_metadata_key
745
        del_key_mock.assert_called_with(
746
            dpid, key
747
        )
748
749
        # fail case
750
        key = "A"
751
        dpid = "00:00:00:00:00:00:00:02"
752
        url = f'{self.server_name_url}/v3/switches/{dpid}/metadata/{key}'
753
        response = api.delete(url)
754
        self.assertEqual(mock_metadata_changes.call_count, 1)  # remains 1 call
755
        self.assertEqual(response.status_code, 404, response.data)
756
757 View Code Duplication
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
758
    def test_enable_interfaces(self, mock_notify_topo):
759
        """Test enable_interfaces."""
760
        dpid = '00:00:00:00:00:00:00:01'
761
        mock_switch = get_switch_mock(dpid)
762
        mock_interface_1 = get_interface_mock('s1-eth1', 1, mock_switch)
763
        mock_interface_2 = get_interface_mock('s1-eth2', 2, mock_switch)
764
        mock_switch.interfaces = {1: mock_interface_1, 2: mock_interface_2}
765
        self.napp.controller.switches = {dpid: mock_switch}
766
        api = get_test_client(self.napp.controller, self.napp)
767
768
        interface_id = '00:00:00:00:00:00:00:01:1'
769
        url = f'{self.server_name_url}/v3/interfaces/{interface_id}/enable'
770
        response = api.post(url)
771
        self.assertEqual(response.status_code, 200, response.data)
772
        self.assertEqual(mock_interface_1.enable.call_count, 1)
773
        self.assertEqual(mock_interface_2.enable.call_count, 0)
774
        self.napp.topo_controller.enable_interface.assert_called_with(
775
            interface_id
776
        )
777
        mock_notify_topo.assert_called()
778
779
        mock_interface_1.enable.call_count = 0
780
        mock_interface_2.enable.call_count = 0
781
        url = f'{self.server_name_url}/v3/interfaces/switch/{dpid}/enable'
782
        response = api.post(url)
783
        self.assertEqual(response.status_code, 200, response.data)
784
        self.napp.topo_controller.upsert_switch.assert_called_with(
785
            mock_switch.id, mock_switch.as_dict()
786
        )
787
        self.assertEqual(mock_interface_1.enable.call_count, 1)
788
        self.assertEqual(mock_interface_2.enable.call_count, 1)
789
790
        # test interface not found
791
        interface_id = '00:00:00:00:00:00:00:01:3'
792
        mock_interface_1.enable.call_count = 0
793
        mock_interface_2.enable.call_count = 0
794
        url = f'{self.server_name_url}/v3/interfaces/{interface_id}/enable'
795
        response = api.post(url)
796
        self.assertEqual(response.status_code, 404, response.data)
797
        self.assertEqual(mock_interface_1.enable.call_count, 0)
798
        self.assertEqual(mock_interface_2.enable.call_count, 0)
799
800
        # test switch not found
801
        dpid = '00:00:00:00:00:00:00:02'
802
        url = f'{self.server_name_url}/v3/interfaces/switch/{dpid}/enable'
803
        response = api.post(url)
804
        self.assertEqual(response.status_code, 404, response.data)
805
        self.assertEqual(mock_interface_1.enable.call_count, 0)
806
        self.assertEqual(mock_interface_2.enable.call_count, 0)
807
808 View Code Duplication
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
809
    def test_disable_interfaces(self, mock_notify_topo):
810
        """Test disable_interfaces."""
811
        interface_id = '00:00:00:00:00:00:00:01:1'
812
        dpid = '00:00:00:00:00:00:00:01'
813
        mock_switch = get_switch_mock(dpid)
814
        mock_interface_1 = get_interface_mock('s1-eth1', 1, mock_switch)
815
        mock_interface_2 = get_interface_mock('s1-eth2', 2, mock_switch)
816
        mock_switch.interfaces = {1: mock_interface_1, 2: mock_interface_2}
817
        self.napp.controller.switches = {dpid: mock_switch}
818
        api = get_test_client(self.napp.controller, self.napp)
819
820
        url = f'{self.server_name_url}/v3/interfaces/{interface_id}/disable'
821
        response = api.post(url)
822
        self.assertEqual(response.status_code, 200, response.data)
823
        self.napp.topo_controller.disable_interface.assert_called_with(
824
            interface_id
825
        )
826
        self.assertEqual(mock_interface_1.disable.call_count, 1)
827
        self.assertEqual(mock_interface_2.disable.call_count, 0)
828
        mock_notify_topo.assert_called()
829
830
        mock_interface_1.disable.call_count = 0
831
        mock_interface_2.disable.call_count = 0
832
        url = f'{self.server_name_url}/v3/interfaces/switch/{dpid}/disable'
833
        response = api.post(url)
834
        self.assertEqual(response.status_code, 200, response.data)
835
        self.napp.topo_controller.upsert_switch.assert_called_with(
836
            mock_switch.id, mock_switch.as_dict()
837
        )
838
        self.assertEqual(mock_interface_1.disable.call_count, 1)
839
        self.assertEqual(mock_interface_2.disable.call_count, 1)
840
841
        # test interface not found
842
        interface_id = '00:00:00:00:00:00:00:01:3'
843
        mock_interface_1.disable.call_count = 0
844
        mock_interface_2.disable.call_count = 0
845
        url = f'{self.server_name_url}/v3/interfaces/{interface_id}/disable'
846
        response = api.post(url)
847
        self.assertEqual(response.status_code, 404, response.data)
848
        self.assertEqual(mock_interface_1.disable.call_count, 0)
849
        self.assertEqual(mock_interface_2.disable.call_count, 0)
850
851
        # test switch not found
852
        dpid = '00:00:00:00:00:00:00:02'
853
        url = f'{self.server_name_url}/v3/interfaces/switch/{dpid}/disable'
854
        response = api.post(url)
855
        self.assertEqual(response.status_code, 404, response.data)
856
        self.assertEqual(mock_interface_1.disable.call_count, 0)
857
        self.assertEqual(mock_interface_2.disable.call_count, 0)
858
859
    def test_get_interface_metadata(self):
860
        """Test get_interface_metada."""
861
        interface_id = '00:00:00:00:00:00:00:01:1'
862
        dpid = '00:00:00:00:00:00:00:01'
863
        mock_switch = get_switch_mock(dpid)
864
        mock_interface = get_interface_mock('s1-eth1', 1, mock_switch)
865
        mock_interface.metadata = {"metada": "A"}
866
        mock_switch.interfaces = {1: mock_interface}
867
        self.napp.controller.switches = {dpid: mock_switch}
868
        api = get_test_client(self.napp.controller, self.napp)
869
870
        url = f'{self.server_name_url}/v3/interfaces/{interface_id}/metadata'
871
        response = api.get(url)
872
        self.assertEqual(response.status_code, 200, response.data)
873
874
        # fail case switch not found
875
        interface_id = '00:00:00:00:00:00:00:02:1'
876
        url = f'{self.server_name_url}/v3/interfaces/{interface_id}/metadata'
877
        response = api.get(url)
878
        self.assertEqual(response.status_code, 404, response.data)
879
880
        # fail case interface not found
881
        interface_id = '00:00:00:00:00:00:00:01:2'
882
        url = f'{self.server_name_url}/v3/interfaces/{interface_id}/metadata'
883
        response = api.get(url)
884
        self.assertEqual(response.status_code, 404, response.data)
885
886
    @patch('napps.kytos.topology.main.Main.notify_metadata_changes')
887
    def test_add_interface_metadata(self, mock_metadata_changes):
888
        """Test add_interface_metadata."""
889
        interface_id = '00:00:00:00:00:00:00:01:1'
890
        dpid = '00:00:00:00:00:00:00:01'
891
        mock_switch = get_switch_mock(dpid)
892
        mock_interface = get_interface_mock('s1-eth1', 1, mock_switch)
893
        mock_interface.metadata = {"metada": "A"}
894
        mock_switch.interfaces = {1: mock_interface}
895
        self.napp.controller.switches = {dpid: mock_switch}
896
        api = get_test_client(self.napp.controller, self.napp)
897
898
        url = f'{self.server_name_url}/v3/interfaces/{interface_id}/metadata'
899
        payload = {"metada": "A"}
900
        response = api.post(url, data=json.dumps(payload),
901
                            content_type='application/json')
902
        self.assertEqual(response.status_code, 201, response.data)
903
        mock_metadata_changes.assert_called()
904
905
        # fail case switch not found
906
        interface_id = '00:00:00:00:00:00:00:02:1'
907
        url = f'{self.server_name_url}/v3/interfaces/{interface_id}/metadata'
908
        response = api.post(url, data=json.dumps(payload),
909
                            content_type='application/json')
910
        self.assertEqual(response.status_code, 404, response.data)
911
912
        # fail case interface not found
913
        interface_id = '00:00:00:00:00:00:00:01:2'
914
        url = f'{self.server_name_url}/v3/interfaces/{interface_id}/metadata'
915
        response = api.post(url, data=json.dumps(payload),
916
                            content_type='application/json')
917
        self.assertEqual(response.status_code, 404, response.data)
918
919
    def test_add_interface_metadata_wrong_format(self):
920
        """Test add_interface_metadata_wrong_format."""
921
        dpid = "00:00:00:00:00:00:00:01:1"
922
        api = get_test_client(self.napp.controller, self.napp)
923
        payload = 'A'
924
925
        url = f'{self.server_name_url}/v3/interfaces/{dpid}/metadata'
926
        response = api.post(url, data=payload, content_type='application/json')
927
        self.assertEqual(response.status_code, 400, response.data)
928
929
        payload = None
930
        response = api.post(url, data=json.dumps(payload),
931
                            content_type='application/json')
932
        self.assertEqual(response.status_code, 415, response.data)
933
934
    def test_delete_interface_metadata(self):
935
        """Test delete_interface_metadata."""
936
        interface_id = '00:00:00:00:00:00:00:01:1'
937
        dpid = '00:00:00:00:00:00:00:01'
938
        iface_url = '/v3/interfaces/'
939
        mock_switch = get_switch_mock(dpid)
940
        mock_interface = get_interface_mock('s1-eth1', 1, mock_switch)
941
        mock_interface.remove_metadata.side_effect = [True, False]
942
        mock_interface.metadata = {"A": "A"}
943
        mock_switch.interfaces = {1: mock_interface}
944
        self.napp.controller.switches = {'00:00:00:00:00:00:00:01':
945
                                         mock_switch}
946
        api = get_test_client(self.napp.controller, self.napp)
947
948
        key = 'A'
949
        url = f'{self.server_name_url}{iface_url}{interface_id}/metadata/{key}'
950
        response = api.delete(url)
951
        self.assertEqual(response.status_code, 200, response.data)
952
        del_key_mock = self.napp.topo_controller.delete_interface_metadata_key
953
        del_key_mock.assert_called_once_with(interface_id, key)
954
955
        # fail case switch not found
956
        key = 'A'
957
        interface_id = '00:00:00:00:00:00:00:02:1'
958
        url = f'{self.server_name_url}{iface_url}{interface_id}/metadata/{key}'
959
        response = api.delete(url)
960
        self.assertEqual(response.status_code, 404, response.data)
961
962
        # fail case interface not found
963
        key = 'A'
964
        interface_id = '00:00:00:00:00:00:00:01:2'
965
        url = f'{self.server_name_url}{iface_url}{interface_id}/metadata/{key}'
966
        response = api.delete(url)
967
        self.assertEqual(response.status_code, 404, response.data)
968
969
        # fail case metadata not found
970
        key = 'B'
971
        interface_id = '00:00:00:00:00:00:00:01:1'
972
        url = f'{self.server_name_url}{iface_url}{interface_id}/metadata/{key}'
973
        response = api.delete(url)
974
        self.assertEqual(response.status_code, 404, response.data)
975
976
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
977
    def test_enable_link(self, mock_notify_topo):
978
        """Test enable_link."""
979
        mock_link = MagicMock(Link)
980
        self.napp.links = {'1': mock_link}
981
        api = get_test_client(self.napp.controller, self.napp)
982
983
        link_id = "1"
984
        url = f'{self.server_name_url}/v3/links/{link_id}/enable'
985
        response = api.post(url)
986
        self.assertEqual(response.status_code, 201, response.data)
987
        self.assertEqual(mock_link.enable.call_count, 1)
988
        self.napp.topo_controller.enable_link.assert_called_with(link_id)
989
        mock_notify_topo.assert_called()
990
991
        # fail case
992
        link_id = 2
993
        url = f'{self.server_name_url}/v3/links/{link_id}/enable'
994
        response = api.post(url)
995
        self.assertEqual(response.status_code, 404, response.data)
996
997
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
998
    def test_disable_link(self, mock_notify_topo):
999
        """Test disable_link."""
1000
        mock_link = MagicMock(Link)
1001
        self.napp.links = {'1': mock_link}
1002
        api = get_test_client(self.napp.controller, self.napp)
1003
1004
        link_id = "1"
1005
        url = f'{self.server_name_url}/v3/links/{link_id}/disable'
1006
        response = api.post(url)
1007
        self.assertEqual(response.status_code, 201, response.data)
1008
        self.assertEqual(mock_link.disable.call_count, 1)
1009
        self.assertEqual(mock_notify_topo.call_count, 1)
1010
        self.napp.topo_controller.disable_link.assert_called_with(link_id)
1011
1012
        # fail case
1013
        link_id = 2
1014
        url = f'{self.server_name_url}/v3/links/{link_id}/disable'
1015
        response = api.post(url)
1016
        self.assertEqual(response.status_code, 404, response.data)
1017
1018
    def test_handle_lldp_status_updated(self):
1019
        """Test handle_lldp_status_updated."""
1020
        event = MagicMock()
1021
        self.napp.controller.buffers.app.put = MagicMock()
1022
1023
        dpid_a = "00:00:00:00:00:00:00:01"
1024
        dpid_b = "00:00:00:00:00:00:00:02"
1025
        dpids = [dpid_a, dpid_b]
1026
        interface_ids = [f"{dpid}:1" for dpid in dpids]
1027
1028
        mock_switch_a = get_switch_mock(dpid_a, 0x04)
1029
        mock_switch_b = get_switch_mock(dpid_b, 0x04)
1030
        self.napp.controller.switches = {dpid_a: mock_switch_a,
1031
                                         dpid_b: mock_switch_b}
1032
1033
        event.content = {"interface_ids": interface_ids, "state": "disabled"}
1034
        self.napp.handle_lldp_status_updated(event)
1035
1036
        mock_put = self.napp.controller.buffers.app.put
1037
        assert mock_put.call_count == len(interface_ids)
1038
1039
    def test_handle_topo_controller_upsert_switch(self):
1040
        """Test handle_topo_controller_upsert_switch."""
1041
        event = MagicMock()
1042
        self.napp.handle_topo_controller_upsert_switch(event)
1043
        mock = self.napp.topo_controller.upsert_switch
1044
        mock.assert_called_with(event.id, event.as_dict())
1045
1046
    def test_get_link_metadata(self):
1047
        """Test get_link_metadata."""
1048
        mock_link = MagicMock(Link)
1049
        mock_link.metadata = "A"
1050
        self.napp.links = {'1': mock_link}
1051
        msg_success = {"metadata": "A"}
1052
        api = get_test_client(self.napp.controller, self.napp)
1053
1054
        link_id = 1
1055
        url = f'{self.server_name_url}/v3/links/{link_id}/metadata'
1056
        response = api.get(url)
1057
        self.assertEqual(response.status_code, 200, response.data)
1058
        self.assertEqual(msg_success, json.loads(response.data))
1059
1060
        # fail case
1061
        link_id = 2
1062
        url = f'{self.server_name_url}/v3/links/{link_id}/metadata'
1063
        response = api.get(url)
1064
        self.assertEqual(response.status_code, 404, response.data)
1065
1066
    @patch('napps.kytos.topology.main.Main.notify_metadata_changes')
1067
    def test_add_link_metadata(self, mock_metadata_changes):
1068
        """Test add_link_metadata."""
1069
        mock_link = MagicMock(Link)
1070
        mock_link.metadata = "A"
1071
        self.napp.links = {'1': mock_link}
1072
        payload = {"metadata": "A"}
1073
        api = get_test_client(self.napp.controller, self.napp)
1074
1075
        link_id = 1
1076
        url = f'{self.server_name_url}/v3/links/{link_id}/metadata'
1077
        response = api.post(url, data=json.dumps(payload),
1078
                            content_type='application/json')
1079
        self.assertEqual(response.status_code, 201, response.data)
1080
        mock_metadata_changes.assert_called()
1081
1082
        # fail case
1083
        link_id = 2
1084
        url = f'{self.server_name_url}/v3/links/{link_id}/metadata'
1085
        response = api.post(url, data=json.dumps(payload),
1086
                            content_type='application/json')
1087
        self.assertEqual(response.status_code, 404, response.data)
1088
1089
    def test_add_link_metadata_wrong_format(self):
1090
        """Test add_link_metadata_wrong_format."""
1091
        link_id = 'cf0f4071be426b3f745027f5d22'
1092
        api = get_test_client(self.napp.controller, self.napp)
1093
        payload = "A"
1094
1095
        url = f'{self.server_name_url}/v3/links/{link_id}/metadata'
1096
        response = api.post(url, data=payload,
1097
                            content_type='application/json')
1098
        self.assertEqual(response.status_code, 400, response.data)
1099
1100
        payload = None
1101
        response = api.post(url, data=json.dumps(payload),
1102
                            content_type='application/json')
1103
        self.assertEqual(response.status_code, 415, response.data)
1104
1105
    @patch('napps.kytos.topology.main.Main.notify_metadata_changes')
1106
    def test_delete_link_metadata(self, mock_metadata_changes):
1107
        """Test delete_link_metadata."""
1108
        mock_link = MagicMock(Link)
1109
        mock_link.metadata = {"A": "A"}
1110
        mock_link.remove_metadata.side_effect = [True, False]
1111
        self.napp.links = {'1': mock_link}
1112
        api = get_test_client(self.napp.controller, self.napp)
1113
1114
        link_id = 1
1115
        key = 'A'
1116
        url = f'{self.server_name_url}/v3/links/{link_id}/metadata/{key}'
1117
        response = api.delete(url)
1118
        self.assertEqual(response.status_code, 200, response.data)
1119
        del_mock = self.napp.topo_controller.delete_link_metadata_key
1120
        del_mock.assert_called_once_with(mock_link.id, key)
1121
        mock_metadata_changes.assert_called()
1122
1123
        # fail case link not found
1124
        link_id = 2
1125
        key = 'A'
1126
        url = f'{self.server_name_url}/v3/links/{link_id}/metadata/{key}'
1127
        response = api.delete(url)
1128
        self.assertEqual(response.status_code, 404, response.data)
1129
1130
        # fail case metadata not found
1131
        link_id = 1
1132
        key = 'B'
1133
        url = f'{self.server_name_url}/v3/links/{link_id}/metadata/{key}'
1134
        response = api.delete(url)
1135
        self.assertEqual(response.status_code, 404, response.data)
1136
1137
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1138
    def test_handle_new_switch(self, mock_notify_topology_update):
1139
        """Test handle_new_switch."""
1140
        mock_event = MagicMock()
1141
        mock_switch = create_autospec(Switch)
1142
        mock_event.content['switch'] = mock_switch
1143
        self.napp.handle_new_switch(mock_event)
1144
        mock = self.napp.topo_controller.upsert_switch
1145
        mock.assert_called_once_with(mock_event.content['switch'].id,
1146
                                     mock_event.content['switch'].as_dict())
1147
        mock_notify_topology_update.assert_called()
1148
1149
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1150
    def test_handle_connection_lost(self, mock_notify_topology_update):
1151
        """Test handle connection_lost."""
1152
        mock_event = MagicMock()
1153
        mock_switch = create_autospec(Switch)
1154
        mock_switch.return_value = True
1155
        mock_event.content['source'] = mock_switch
1156
        self.napp.handle_connection_lost(mock_event)
1157
        mock_notify_topology_update.assert_called()
1158
1159
    @patch('napps.kytos.topology.main.Main.handle_interface_link_up')
1160
    def test_handle_interface_created(self, mock_link_up):
1161
        """Test handle_interface_created."""
1162
        mock_event = MagicMock()
1163
        mock_interface = create_autospec(Interface)
1164
        mock_interface.id = "1"
1165
        mock_event.content = {'interface': mock_interface}
1166
        self.napp.handle_interface_created(mock_event)
1167
        mock_link_up.assert_called()
1168
1169
    def test_handle_interfaces_created(self):
1170
        """Test handle_interfaces_created."""
1171
        buffers_app_mock = MagicMock()
1172
        self.napp.controller.buffers.app = buffers_app_mock
1173
        mock_switch = create_autospec(Switch)
1174
        mock_event = MagicMock()
1175
        mock_interface = create_autospec(Interface)
1176
        mock_interface.id = "1"
1177
        mock_interface.switch = mock_switch
1178
        mock_interface_two = create_autospec(Interface)
1179
        mock_interface_two.id = "2"
1180
        mock_event.content = {'interfaces': [mock_interface,
1181
                              mock_interface_two]}
1182
        self.napp.handle_interfaces_created(mock_event)
1183
        upsert_mock = self.napp.topo_controller.upsert_switch
1184
        upsert_mock.assert_called_with(mock_switch.id, mock_switch.as_dict())
1185
        assert self.napp.controller.buffers.app.put.call_count == 2
1186
1187
    @patch('napps.kytos.topology.main.Main.handle_interface_link_down')
1188
    def test_handle_interface_down(self, mock_handle_interface_link_down):
1189
        """Test handle interface down."""
1190
        mock_event = MagicMock()
1191
        mock_interface = create_autospec(Interface)
1192
        mock_event.content['interface'] = mock_interface
1193
        self.napp.handle_interface_down(mock_event)
1194
        mock_handle_interface_link_down.assert_called()
1195
1196
    @patch('napps.kytos.topology.main.Main.handle_interface_down')
1197
    def test_interface_deleted(self, mock_handle_interface_link_down):
1198
        """Test interface deleted."""
1199
        mock_event = MagicMock()
1200
        self.napp.handle_interface_deleted(mock_event)
1201
        mock_handle_interface_link_down.assert_called()
1202
1203
    @patch('napps.kytos.topology.main.Main._get_link_from_interface')
1204
    @patch('napps.kytos.topology.main.Main.notify_link_up_if_status')
1205
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1206
    def test_interface_link_up(self, *args):
1207
        """Test interface link_up."""
1208
        (mock_notify_topology_update,
1209
         mock_notify_link_up_if_status,
1210
         mock_link_from_interface) = args
1211
1212
        tnow = time.time()
1213
        mock_interface_a = create_autospec(Interface)
1214
        mock_interface_a.is_active.return_value = False
1215
        mock_interface_b = create_autospec(Interface)
1216
        mock_interface_b.is_active.return_value = True
1217
        mock_link = create_autospec(Link)
1218
        mock_link.get_metadata.return_value = tnow
1219
        mock_link.is_active.side_effect = [False, True]
1220
        mock_link.endpoint_a = mock_interface_a
1221
        mock_link.endpoint_b = mock_interface_b
1222
        mock_link_from_interface.return_value = mock_link
1223
        mock_link.status = EntityStatus.UP
1224
        self.napp.handle_interface_link_up(mock_interface_a)
1225
        mock_notify_topology_update.assert_called()
1226
        mock_link.extend_metadata.assert_called()
1227
        mock_link.activate.assert_called()
1228
        self.napp.topo_controller.activate_link.assert_called()
1229
        mock_notify_link_up_if_status.assert_called()
1230
1231
    @patch('napps.kytos.topology.main.Main._get_link_from_interface')
1232
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1233
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1234
    def test_interface_link_down(self, *args):
1235
        """Test interface link down."""
1236
        (mock_status_change, mock_topology_update,
1237
         mock_link_from_interface) = args
1238
1239
        mock_event = MagicMock()
1240
        mock_interface = create_autospec(Interface)
1241
        mock_link = create_autospec(Link)
1242
        mock_link.is_active.return_value = True
1243
        mock_link_from_interface.return_value = mock_link
1244
        mock_event.content['interface'] = mock_interface
1245
        self.napp.handle_interface_link_down(mock_event)
1246
        mock_topology_update.assert_called()
1247
        mock_status_change.assert_called()
1248
1249
    @patch('napps.kytos.topology.main.Main._get_link_from_interface')
1250
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1251
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1252
    def test_handle_link_down(self, *args):
1253
        """Test interface link down."""
1254
        (mock_status_change, mock_topology_update,
1255
         mock_link_from_interface) = args
1256
1257
        mock_interface = create_autospec(Interface)
1258
        mock_link = create_autospec(Link)
1259
        mock_link.is_active.return_value = True
1260
        mock_link_from_interface.return_value = mock_link
1261
        self.napp.handle_link_down(mock_interface)
1262
        assert self.napp.topo_controller.deactivate_link.call_count == 1
1263
        mock_interface.deactivate.assert_called()
1264
        mock_link.deactivate.assert_called()
1265
        assert mock_topology_update.call_count == 1
1266
        mock_status_change.assert_called()
1267
        assert self.napp.topo_controller.deactivate_interface.call_count == 1
1268
1269
    @patch('napps.kytos.topology.main.Main._get_link_from_interface')
1270
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1271
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1272
    def test_handle_link_down_not_active(self, *args):
1273
        """Test interface link down with link not active."""
1274
        (mock_status_change, mock_topology_update,
1275
         mock_link_from_interface) = args
1276
1277
        mock_interface = create_autospec(Interface)
1278
        mock_link = create_autospec(Link)
1279
        mock_link.is_active.return_value = False
1280
        mock_link_from_interface.return_value = mock_link
1281
        mock_link.get_metadata.return_value = False
1282
        self.napp.handle_link_down(mock_interface)
1283
        assert self.napp.topo_controller.deactivate_link.call_count == 0
1284
        mock_interface.deactivate.assert_called()
1285
        mock_topology_update.assert_called()
1286
        mock_status_change.assert_not_called()
1287
        assert self.napp.topo_controller.deactivate_interface.call_count == 1
1288
1289
    @patch('napps.kytos.topology.main.Main._get_link_from_interface')
1290
    @patch('napps.kytos.topology.main.Main.notify_link_up_if_status')
1291
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1292
    def test_handle_link_up(self, *args):
1293
        """Test handle link up."""
1294
        (mock_notify_topology_update,
1295
         mock_notify_link_up_if_status,
1296
         mock_link_from_interface) = args
1297
1298
        mock_interface = create_autospec(Interface)
1299
        mock_link = MagicMock(status=EntityStatus.UP)
1300
        mock_link.is_active.return_value = True
1301
        mock_link_from_interface.return_value = mock_link
1302
        self.napp.handle_link_up(mock_interface)
1303
        assert self.napp.topo_controller.activate_link.call_count == 1
1304
        mock_interface.activate.assert_called()
1305
        assert mock_notify_link_up_if_status.call_count == 1
1306
        mock_notify_topology_update.assert_called()
1307
1308
    @patch('time.sleep')
1309
    @patch('napps.kytos.topology.main.Main._get_link_from_interface')
1310
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1311
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1312
    def test_handle_link_up_intf_down(self, *args):
1313
        """Test handle link up but one intf down."""
1314
        (mock_status_change, mock_topology_update,
1315
         mock_link_from_interface, _) = args
1316
1317
        mock_interface = create_autospec(Interface)
1318
        mock_link = MagicMock()
1319
        mock_link.endpoint_a.is_active.return_value = False
1320
        mock_link.is_active.return_value = False
1321
        mock_link_from_interface.return_value = mock_link
1322
        self.napp.handle_link_up(mock_interface)
1323
        mock_interface.activate.assert_called()
1324
        assert self.napp.topo_controller.activate_link.call_count == 0
1325
        assert mock_topology_update.call_count == 1
1326
        mock_status_change.assert_not_called()
1327
1328
    @patch('napps.kytos.topology.main.Main._get_link_or_create')
1329
    @patch('napps.kytos.topology.main.Main.notify_link_up_if_status')
1330
    def test_add_links(self, *args):
1331
        """Test add_links."""
1332
        (mock_notify_link_up_if_status,
1333
         mock_get_link_or_create) = args
1334
1335
        mock_link = MagicMock()
1336
        mock_get_link_or_create.return_value = (mock_link, True)
1337
        mock_event = MagicMock()
1338
        mock_intf_a = MagicMock()
1339
        mock_intf_b = MagicMock()
1340
        mock_event.content = {"interface_a": mock_intf_a,
1341
                              "interface_b": mock_intf_b}
1342
        self.napp.add_links(mock_event)
1343
        mock_link.extend_metadata.assert_called()
1344
        mock_get_link_or_create.assert_called()
1345
        mock_notify_link_up_if_status.assert_called()
1346
        mock_intf_a.update_link.assert_called()
1347
        mock_intf_b.update_link.assert_called()
1348
        mock_link.endpoint_a = mock_intf_a
1349
        mock_link.endpoint_b = mock_intf_b
1350
1351
    @patch('napps.kytos.topology.main.KytosEvent')
1352
    @patch('kytos.core.buffers.KytosEventBuffer.put')
1353
    def test_notify_switch_enabled(self, *args):
1354
        """Test notify switch enabled."""
1355
        dpid = "00:00:00:00:00:00:00:01"
1356
        (mock_buffers_put, mock_event) = args
1357
        self.napp.notify_switch_enabled(dpid)
1358
        mock_event.assert_called()
1359
        mock_buffers_put.assert_called()
1360
1361
    @patch('napps.kytos.topology.main.KytosEvent')
1362
    @patch('kytos.core.buffers.KytosEventBuffer.put')
1363
    def test_notify_switch_disabled(self, *args):
1364
        """Test notify switch disabled."""
1365
        dpid = "00:00:00:00:00:00:00:01"
1366
        (mock_buffers_put, mock_event) = args
1367
        self.napp.notify_switch_disabled(dpid)
1368
        mock_event.assert_called()
1369
        mock_buffers_put.assert_called()
1370
1371
    @patch('napps.kytos.topology.main.KytosEvent')
1372
    @patch('kytos.core.buffers.KytosEventBuffer.put')
1373
    def test_notify_topology_update(self, *args):
1374
        """Test notify_topology_update."""
1375
        (mock_buffers_put, mock_event) = args
1376
        self.napp.notify_topology_update()
1377
        mock_event.assert_called()
1378
        mock_buffers_put.assert_called()
1379
1380
    @patch('kytos.core.buffers.KytosEventBuffer.put')
1381
    def test_notify_current_topology(self, mock_buffers_put):
1382
        """Test notify_current_topology."""
1383
        self.napp.notify_current_topology()
1384
        mock_buffers_put.assert_called()
1385
        args = mock_buffers_put.call_args
1386
        expected_event = args[0][0]
1387
        assert expected_event.name == "kytos/topology.current"
1388
        assert "topology" in expected_event.content
1389
1390
    @patch('napps.kytos.topology.main.KytosEvent')
1391
    @patch('kytos.core.buffers.KytosEventBuffer.put')
1392
    def test_notify_link_status_change(self, *args):
1393
        """Test notify link status change."""
1394
        (mock_buffers_put, mock_event) = args
1395
        mock_link = create_autospec(Link)
1396
        self.napp.notify_link_status_change(mock_link)
1397
        mock_event.assert_called()
1398
        mock_buffers_put.assert_called()
1399
1400
    @patch('napps.kytos.topology.main.KytosEvent')
1401
    @patch('kytos.core.buffers.KytosEventBuffer.put')
1402
    def test_notify_metadata_changes(self, *args):
1403
        """Test notify metadata changes."""
1404
        (mock_buffers_put, mock_event) = args
1405
        count = 0
1406
        for spec in [Switch, Interface, Link]:
1407
            mock_obj = create_autospec(spec)
1408
            mock_obj.metadata = {"some_key": "some_value"}
1409
            self.napp.notify_metadata_changes(mock_obj, 'added')
1410
            self.assertEqual(mock_event.call_count, count+1)
1411
            self.assertEqual(mock_buffers_put.call_count, count+1)
1412
            count += 1
1413
        with self.assertRaises(ValueError):
1414
            self.napp.notify_metadata_changes(MagicMock(), 'added')
1415
1416
    @patch('napps.kytos.topology.main.KytosEvent')
1417
    @patch('kytos.core.buffers.KytosEventBuffer.put')
1418
    def test_notify_port_created(self, *args):
1419
        """Test notify port created."""
1420
        (mock_buffers_put, mock_kytos_event) = args
1421
        mock_event = MagicMock()
1422
        self.napp.notify_port_created(mock_event)
1423
        mock_kytos_event.assert_called()
1424
        mock_buffers_put.assert_called()
1425
1426
    def test_get_links_from_interfaces(self) -> None:
1427
        """Test get_links_from_interfaces."""
1428
        interfaces = [MagicMock(id=f"intf{n}") for n in range(4)]
1429
        links = {
1430
            "link1": MagicMock(id="link1",
1431
                               endpoint_a=interfaces[0],
1432
                               endpoint_b=interfaces[1]),
1433
            "link2": MagicMock(id="link2",
1434
                               endpoint_a=interfaces[2],
1435
                               endpoint_b=interfaces[3]),
1436
        }
1437
        self.napp.links = links
1438
        response = self.napp.get_links_from_interfaces(interfaces)
1439
        assert links == response
1440
        response = self.napp.get_links_from_interfaces(interfaces[:2])
1441
        assert response == {"link1": links["link1"]}
1442
1443
    def test_handle_link_liveness_disabled(self) -> None:
1444
        """Test handle_link_liveness_disabled."""
1445
        interfaces = [MagicMock(id=f"intf{n}") for n in range(4)]
1446
        links = {
1447
            "link1": MagicMock(id="link1",
1448
                               endpoint_a=interfaces[0],
1449
                               endpoint_b=interfaces[1]),
1450
            "link2": MagicMock(id="link2",
1451
                               endpoint_a=interfaces[2],
1452
                               endpoint_b=interfaces[3]),
1453
        }
1454
        self.napp.links = links
1455
        self.napp.notify_topology_update = MagicMock()
1456
        self.napp.notify_link_status_change = MagicMock()
1457
1458
        self.napp.handle_link_liveness_disabled(interfaces)
1459
1460
        bulk_delete = self.napp.topo_controller.bulk_delete_link_metadata_key
1461
        assert bulk_delete.call_count == 1
1462
        assert self.napp.notify_topology_update.call_count == 1
1463
        assert self.napp.notify_link_status_change.call_count == len(links)
1464
1465
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1466
    def test_handle_link_maintenance_start(self, status_change_mock):
1467
        """Test handle_link_maintenance_start."""
1468
        link1 = MagicMock()
1469
        link1.id = 2
1470
        link2 = MagicMock()
1471
        link2.id = 3
1472
        link3 = MagicMock()
1473
        link3.id = 4
1474
        content = {'links': [link1, link2]}
1475
        event = MagicMock()
1476
        event.content = content
1477
        self.napp.links = {2: link1, 4: link3}
1478
        self.napp.handle_link_maintenance_start(event)
1479
        status_change_mock.assert_called_once_with(link1, reason='maintenance')
1480
1481
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1482
    def test_handle_link_maintenance_end(self, status_change_mock):
1483
        """Test handle_link_maintenance_end."""
1484
        link1 = MagicMock()
1485
        link1.id = 2
1486
        link2 = MagicMock()
1487
        link2.id = 3
1488
        link3 = MagicMock()
1489
        link3.id = 4
1490
        content = {'links': [link1, link2]}
1491
        event = MagicMock()
1492
        event.content = content
1493
        self.napp.links = {2: link1, 4: link3}
1494
        self.napp.handle_link_maintenance_end(event)
1495
        status_change_mock.assert_called_once_with(link1, reason='maintenance')
1496
1497 View Code Duplication
    @patch('napps.kytos.topology.main.Main.handle_link_down')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1498
    def test_handle_switch_maintenance_start(self, handle_link_down_mock):
1499
        """Test handle_switch_maintenance_start."""
1500
        switch1 = MagicMock()
1501
        interface1 = MagicMock()
1502
        interface1.is_active.return_value = True
1503
        interface2 = MagicMock()
1504
        interface2.is_active.return_value = False
1505
        interface3 = MagicMock()
1506
        interface3.is_active.return_value = True
1507
        switch1.interfaces = {1: interface1, 2: interface2, 3: interface3}
1508
        switch2 = MagicMock()
1509
        interface4 = MagicMock()
1510
        interface4.is_active.return_value = False
1511
        interface5 = MagicMock()
1512
        interface5.is_active.return_value = True
1513
        switch2.interfaces = {1: interface4, 2: interface5}
1514
        content = {'switches': [switch1, switch2]}
1515
        event = MagicMock()
1516
        event.content = content
1517
        self.napp.handle_switch_maintenance_start(event)
1518
        self.assertEqual(handle_link_down_mock.call_count, 3)
1519
1520 View Code Duplication
    @patch('napps.kytos.topology.main.Main.handle_link_up')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1521
    def test_handle_switch_maintenance_end(self, handle_link_up_mock):
1522
        """Test handle_switch_maintenance_end."""
1523
        switch1 = MagicMock()
1524
        interface1 = MagicMock()
1525
        interface1.is_active.return_value = True
1526
        interface2 = MagicMock()
1527
        interface2.is_active.return_value = False
1528
        interface3 = MagicMock()
1529
        interface3.is_active.return_value = True
1530
        switch1.interfaces = {1: interface1, 2: interface2, 3: interface3}
1531
        switch2 = MagicMock()
1532
        interface4 = MagicMock()
1533
        interface4.is_active.return_value = False
1534
        interface5 = MagicMock()
1535
        interface5.is_active.return_value = True
1536
        switch2.interfaces = {1: interface4, 2: interface5}
1537
        content = {'switches': [switch1, switch2]}
1538
        event = MagicMock()
1539
        event.content = content
1540
        self.napp.handle_switch_maintenance_end(event)
1541
        self.assertEqual(handle_link_up_mock.call_count, 5)
1542
1543
    def test_link_status_hook_link_up_timer(self) -> None:
1544
        """Test status hook link up timer."""
1545
        last_change = time.time() - self.napp.link_up_timer + 5
1546
        link = MagicMock(metadata={"last_status_change": last_change})
1547
        link.is_active.return_value = True
1548
        link.is_enabled.return_value = True
1549
        res = self.napp.link_status_hook_link_up_timer(link)
1550
        assert res == EntityStatus.DOWN
1551
1552
        last_change = time.time() - self.napp.link_up_timer
1553
        link.metadata["last_status_change"] = last_change
1554
        res = self.napp.link_status_hook_link_up_timer(link)
1555
        assert res is None
1556
1557
    @patch('napps.kytos.topology.main.Main.notify_link_status_change')
1558
    @patch('napps.kytos.topology.main.Main.notify_topology_update')
1559
    @patch('time.sleep')
1560
    def test_notify_link_up_if_status(
1561
        self,
1562
        mock_sleep,
1563
        mock_notify_topo,
1564
        mock_notify_link,
1565
    ) -> None:
1566
        """Test notify link up if status."""
1567
1568
        link = MagicMock(status=EntityStatus.UP)
1569
        link.get_metadata.return_value = now()
1570
        assert not self.napp.notify_link_up_if_status(link)
1571
        link.update_metadata.assert_not_called()
1572
        mock_notify_topo.assert_not_called()
1573
        mock_notify_link.assert_not_called()
1574
1575
        link = MagicMock(status=EntityStatus.UP)
1576
        link.get_metadata.return_value = now() - timedelta(seconds=60)
1577
        assert not self.napp.notify_link_up_if_status(link)
1578
        link.update_metadata.assert_called()
1579
        self.napp.topo_controller.add_link_metadata.assert_called()
1580
        mock_notify_topo.assert_called()
1581
        mock_notify_link.assert_called()
1582
1583
        assert mock_sleep.call_count == 2
1584