Passed
Push — master ( 74a04b...775f07 )
by Vinicius
04:04 queued 32s
created

TestController.setup_method()   A

Complexity

Conditions 1

Size

Total Lines 13
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 11
nop 1
dl 0
loc 13
ccs 11
cts 11
cp 1
crap 1
rs 9.85
c 0
b 0
f 0
1
"""Test kytos.core.controller module."""
2 1
import json
3 1
import logging
4 1
import sys
5 1
import tempfile
6 1
import warnings
7 1
from collections import Counter
8 1
from copy import copy
9 1
from unittest.mock import AsyncMock, MagicMock, Mock, call, patch
10
11 1
import pytest
12 1
from janus import Queue
13 1
from pyof.foundation.exceptions import PackException
14
15 1
from kytos.core import Controller
16 1
from kytos.core.auth import Auth
17 1
from kytos.core.buffers import KytosBuffers, KytosEventBuffer
18 1
from kytos.core.config import KytosConfig
19 1
from kytos.core.events import KytosEvent
20 1
from kytos.core.exceptions import KytosNAppSetupException
21 1
from kytos.core.logs import LogManager
22 1
from kytos.core.rest_api import Request
23
24
25
# pylint: disable=protected-access, too-many-public-methods
26 1
class TestController:
27
    """Controller tests."""
28
29 1
    def setup_method(self):
30
        """Instantiate a controller."""
31
32 1
        self.options = KytosConfig().options['daemon']
33 1
        self.napps_manager = Mock()
34 1
        Auth.get_user_controller = MagicMock()
35 1
        self.controller = Controller(self.options)
36 1
        self.controller._buffers = MagicMock()
37 1
        self.controller.napps_manager = self.napps_manager
38 1
        self.controller.log = Mock()
39 1
        self.controller.log.getEffectiveLevel.return_value = 20
40 1
        self.app = self.controller.api_server.app
41 1
        self.base_url = "http://127.0.0.1/api/kytos/core/"
42
43 1
    @staticmethod
44 1
    @patch('kytos.core.controller.LogManager')
45 1
    @patch('kytos.core.logs.Path')
46 1
    @pytest.mark.skip(reason="TODO issue 371 in a future PR")
47 1
    def test_websocket_log_usage(path, log_manager):
48
        """Assert that the web socket log is used."""
49
        # Save original state
50
        handlers_bak = copy(logging.root.handlers)
51
52
        # Minimum to instantiate Controller
53
        options = Mock(napps='', logger_decorators=[])
54
        path.return_value.exists.return_value = False
55
        controller = Controller(options)
56
57
        # The test
58
        controller.enable_logs()
59
        log_manager.enable_websocket.assert_called_once()
60
61
        # Restore original state
62
        logging.root.handlers = handlers_bak
63
64 1
    @patch('kytos.core.api_server.APIServer.remove_napp_endpoints')
65 1
    def test_unload_napp_listener(self, _):
66
        """Call NApp shutdown listener on unload."""
67 1
        username, napp_name = 'test', 'napp'
68 1
        listener = self._add_napp(username, napp_name)
69
70 1
        listener.assert_not_called()
71 1
        self.controller.unload_napp(username, napp_name)
72 1
        listener.assert_called()
73
74 1
    @patch('kytos.core.api_server.APIServer.remove_napp_endpoints')
75 1
    def test_unload_napp_other_listener(self, _):
76
        """Should not call other NApps' shutdown listener on unload."""
77 1
        username, napp_name = 'test', 'napp1'
78 1
        self._add_napp(username, napp_name)
79 1
        other_listener = self._add_napp('test', 'napp2')
80
81 1
        self.controller.unload_napp(username, napp_name)
82 1
        other_listener.assert_not_called()
83
84 1
    def _add_napp(self, username, napp_name):
85
        """Add a mocked NApp to the controller."""
86 1
        napp_id = f'{username}/{napp_name}'
87 1
        event_name = f'kytos/core.shutdown.{napp_id}'
88 1
        listener = Mock()
89 1
        self.controller.events_listeners[event_name] = [listener]
90 1
        napp = Mock(_listeners={})
91 1
        self.controller.napps[(username, napp_name)] = napp
92 1
        return listener
93
94 1
    def test_deprecation_warning(self):
95
        """Deprecated method should suggest @rest decorator."""
96 1
        with warnings.catch_warnings(record=True) as wrngs:
97 1
            warnings.simplefilter("always")  # trigger all warnings
98 1
            self.controller.register_rest_endpoint('x', lambda x: x, ['GET'])
99 1
            assert 1 == len(wrngs)
100 1
            warning = wrngs[0]
101 1
            assert warning.category == DeprecationWarning
102 1
            assert '@rest' in str(warning.message)
103
104 1
    def test_loggers(self):
105
        """Test that all controller loggers are under kytos
106
        hierarchy logger.
107
        """
108 1
        loggers = self.controller.loggers()
109 1
        for logger in loggers:
110 1
            assert logger.name.startswith("kytos")
111
112 1
    def test_debug_on(self):
113
        """Test the enable debug feature."""
114
        # Enable debug for kytos.core
115 1
        self.controller.toggle_debug("kytos.core")
116 1
        self._test_debug_result()
117
118 1
    def test_debug_on_defaults(self):
119
        """Test the enable debug feature. Test the default parameter"""
120
        # Enable debug for kytos.core
121 1
        self.controller.toggle_debug("kytos.core")
122 1
        self._test_debug_result()
123
124 1
    def _test_debug_result(self):
125
        """Verify if the loggers have level debug."""
126 1
        loggers = self.controller.loggers()
127 1
        for logger in loggers:
128
            # Check if all kytos.core loggers are in DEBUG mode.
129
            # All the rest must remain the same.
130 1
            if logger.name.startswith("kytos.core"):
131 1
                assert logger.getEffectiveLevel(), logging.DEBUG
132
            else:
133 1
                assert logger.getEffectiveLevel(), logging.CRITICAL
134
135 1
    def test_debug_off(self):
136
        """Test the disable debug feature"""
137
        # Fist we enable the debug
138 1
        self.controller.toggle_debug("kytos.core")
139
        # ... then we disable the debug for the test
140 1
        self.controller.toggle_debug("kytos.core")
141 1
        loggers = self.controller.loggers()
142 1
        for logger in loggers:
143 1
            assert logger.getEffectiveLevel(), logging.CRITICAL
144
145 1
    @patch.object(LogManager, 'load_config_file')
146 1
    def test_debug_no_name(self, mock_load_config_file):
147
        """Test the enable debug logger with default levels."""
148
        # Mock the LogManager that loads the default Loggers
149 1
        self.controller.toggle_debug()
150 1
        self._test_debug_result()
151
152 1
        mock_load_config_file.assert_called_once()
153
154 1
    @patch.object(LogManager, 'load_config_file')
155 1
    def test_debug_empty_name(self, mock_load_config_file):
156
        """Test the enable debug logger with default levels."""
157
        # Mock the LogManager that loads the default Loggers
158 1
        self.controller.toggle_debug('')
159 1
        self._test_debug_result()
160
161 1
        mock_load_config_file.assert_called_once()
162
163 1
    def test_debug_wrong_name(self):
164
        """Test the enable debug logger with wrong name."""
165 1
        pytest.raises(ValueError,
166
                      self.controller.toggle_debug, name="foobar")
167
168 1
    @patch('kytos.core.controller.init_apm')
169 1
    @patch('kytos.core.controller.db_conn_wait')
170 1
    @patch('kytos.core.controller.Controller.start_controller')
171 1
    @patch('kytos.core.controller.Controller.create_pidfile')
172 1
    @patch('kytos.core.controller.Controller.enable_logs')
173 1
    def test_start(self, *args):
174
        """Test start method."""
175 1
        (mock_enable_logs, mock_create_pidfile,
176
         mock_start_controller, mock_db_conn_wait,
177
         mock_init_apm) = args
178 1
        self.controller.start()
179
180 1
        mock_enable_logs.assert_called()
181 1
        mock_create_pidfile.assert_called()
182 1
        mock_start_controller.assert_called()
183 1
        mock_db_conn_wait.assert_not_called()
184 1
        mock_init_apm.assert_not_called()
185
186 1
    @patch('kytos.core.controller.sys')
187 1
    @patch('kytos.core.controller.init_apm')
188 1
    @patch('kytos.core.controller.db_conn_wait')
189 1
    @patch('kytos.core.controller.Controller.start_controller')
190 1
    @patch('kytos.core.controller.Controller.create_pidfile')
191 1
    @patch('kytos.core.controller.Controller.enable_logs')
192 1
    def test_start_error_broad_exception(self, *args):
193
        """Test start error handling broad exception."""
194 1
        (mock_enable_logs, mock_create_pidfile,
195
         mock_start_controller, mock_db_conn_wait,
196
         mock_init_apm, mock_sys) = args
197 1
        mock_start_controller.side_effect = Exception
198 1
        self.controller.start()
199
200 1
        mock_enable_logs.assert_called()
201 1
        mock_create_pidfile.assert_called()
202 1
        mock_start_controller.assert_called()
203 1
        mock_db_conn_wait.assert_not_called()
204 1
        mock_init_apm.assert_not_called()
205 1
        mock_sys.exit.assert_called()
206
207 1
    @patch('kytos.core.controller.init_apm')
208 1
    @patch('kytos.core.controller.db_conn_wait')
209 1
    @patch('kytos.core.controller.Controller.start_controller')
210 1
    @patch('kytos.core.controller.Controller.create_pidfile')
211 1
    @patch('kytos.core.controller.Controller.enable_logs')
212 1
    def test_start_with_mongodb_and_apm(self, *args):
213
        """Test start method with database and APM options set."""
214 1
        (mock_enable_logs, mock_create_pidfile,
215
         mock_start_controller, mock_db_conn_wait,
216
         mock_init_apm) = args
217 1
        self.controller.options.database = "mongodb"
218 1
        self.controller.options.apm = "es"
219 1
        self.controller.start()
220
221 1
        mock_enable_logs.assert_called()
222 1
        mock_create_pidfile.assert_called()
223 1
        mock_start_controller.assert_called()
224 1
        mock_db_conn_wait.assert_called()
225 1
        mock_init_apm.assert_called()
226
227 1
    @patch('kytos.core.controller.sys.exit')
228 1
    @patch('kytos.core.controller.Controller.create_pidfile')
229 1
    @patch('kytos.core.controller.Controller.enable_logs')
230 1
    def test_start_with_invalid_database_backend(self, *args):
231
        """Test start method with unsupported database backend."""
232 1
        (mock_enable_logs, _, mock_sys_exit) = args
233 1
        self.controller.options.database = "invalid"
234 1
        self.controller.start()
235 1
        mock_enable_logs.assert_called()
236 1
        mock_sys_exit.assert_called()
237
238 1
    @patch('os.getpid')
239 1
    @patch('kytos.core.controller.atexit')
240 1
    def test_create_pidfile(self, *args):
241
        """Test activate method."""
242 1
        (_, mock_getpid) = args
243 1
        mock_getpid.return_value = 2
244 1
        with tempfile.NamedTemporaryFile() as tmp_file:
245 1
            tmp_file.write(b'4194305')  # pid_max +1
246 1
            tmp_file.seek(0)
247 1
            self.controller.options.pidfile = tmp_file.name
248
249 1
            self.controller.create_pidfile()
250
251 1
            pid = tmp_file.read()
252 1
            assert pid == b'2'
253
254 1
    @patch('kytos.core.controller.Controller.__init__')
255 1
    @patch('kytos.core.controller.Controller.start')
256 1
    @patch('kytos.core.controller.Controller.stop')
257 1
    def test_restart(self, *args):
258
        """Test restart method."""
259 1
        (mock_stop, mock_start, mock_init) = args
260 1
        self.controller.started_at = 1
261
262 1
        graceful = True
263 1
        self.controller.restart(graceful)
264
265 1
        mock_stop.assert_called_with(graceful)
266 1
        mock_init.assert_called_with(self.controller.options)
267 1
        mock_start.assert_called_with(restart=True)
268
269 1
    @patch('kytos.core.controller.Controller.stop_controller')
270 1
    def test_stop(self, mock_stop_controller):
271
        """Test stop method."""
272 1
        self.controller.started_at = 1
273
274 1
        graceful = True
275 1
        self.controller.stop(graceful)
276
277 1
        mock_stop_controller.assert_called_with(graceful)
278
279 1
    def test_status(self):
280
        """Test status method."""
281 1
        status_1 = self.controller.status()
282 1
        self.controller.started_at = 1
283 1
        status_2 = self.controller.status()
284
285 1
        assert status_1 == 'Stopped'
286 1
        assert status_2 == 'Running since 1'
287
288 1
    @patch('kytos.core.controller.now')
289 1
    def test_uptime(self, mock_now):
290
        """Test uptime method."""
291 1
        mock_now.return_value = 11
292
293 1
        uptime_1 = self.controller.uptime()
294 1
        self.controller.started_at = 1
295 1
        uptime_2 = self.controller.uptime()
296
297 1
        assert uptime_1 == 0
298 1
        assert uptime_2 == 10
299
300 1
    def test_metadata_endpoint(self):
301
        """Test metadata_endpoint method."""
302 1
        req = Request(scope={"type": "http"})
303 1
        resp = self.controller.metadata_endpoint(req)
304 1
        json_metadata = json.loads(resp.body.decode())
305
306 1
        expected_keys = ['__version__', '__author__', '__license__', '__url__',
307
                         '__description__']
308 1
        assert list(json_metadata.keys()) == expected_keys
309
310 1
    def test_notify_listeners(self):
311
        """Test notify_listeners method."""
312 1
        method = MagicMock()
313 1
        self.controller.events_listeners = {'kytos/any': [method]}
314
315 1
        event = MagicMock()
316 1
        event.name = 'kytos/any'
317 1
        self.controller.notify_listeners(event)
318
319 1
        method.assert_called_with(event)
320
321 1
    def test_get_interface_by_id__not_interface(self):
322
        """Test get_interface_by_id method when interface does not exist."""
323 1
        resp_interface = self.controller.get_interface_by_id(None)
324
325 1
        assert resp_interface is None
326
327 1
    def test_get_interface_by_id__not_switch(self):
328
        """Test get_interface_by_id method when switch does not exist."""
329 1
        interface = MagicMock()
330 1
        switch = MagicMock()
331 1
        switch.interfaces = {123: interface}
332 1
        self.controller.switches = {'00:00:00:00:00:00:00:02': switch}
333
334 1
        interface_id = '00:00:00:00:00:00:00:01:123'
335 1
        resp_interface = self.controller.get_interface_by_id(interface_id)
336
337 1
        assert resp_interface is None
338
339 1
    def test_get_interface_by_id(self):
340
        """Test get_interface_by_id method."""
341 1
        interface = MagicMock()
342 1
        switch = MagicMock()
343 1
        switch.interfaces = {123: interface}
344 1
        self.controller.switches = {'00:00:00:00:00:00:00:01': switch}
345
346 1
        interface_id = '00:00:00:00:00:00:00:01:123'
347 1
        resp_interface = self.controller.get_interface_by_id(interface_id)
348
349 1
        assert resp_interface == interface
350
351 1
    def test_get_switch_by_dpid(self):
352
        """Test get_switch_by_dpid method."""
353 1
        dpid = '00:00:00:00:00:00:00:01'
354 1
        switch = MagicMock(dpid=dpid)
355 1
        self.controller.switches = {dpid: switch}
356
357 1
        resp_switch = self.controller.get_switch_by_dpid(dpid)
358
359 1
        assert resp_switch == switch
360
361 1
    def test_get_switch_or_create__exists(self):
362
        """Test status_api method when switch exists."""
363 1
        dpid = '00:00:00:00:00:00:00:01'
364 1
        switch = MagicMock(dpid=dpid)
365 1
        self.controller.switches = {dpid: switch}
366 1
        self.controller.buffers.conn = MagicMock()
367
368 1
        connection = MagicMock()
369 1
        resp_switch = self.controller.get_switch_or_create(dpid, connection)
370
371 1
        assert resp_switch == switch
372 1
        self.controller.buffers.conn.put.assert_called()
373 1
        ev_name = "kytos/core.switch.reconnected"
374 1
        assert self.controller.buffers.conn.put.call_args[0][0].name == ev_name
375
376 1
    def test_get_switch_or_create__not_exists(self):
377
        """Test status_api method when switch does not exist."""
378 1
        self.controller.switches = {}
379 1
        self.controller.buffers.conn = MagicMock()
380
381 1
        dpid = '00:00:00:00:00:00:00:01'
382 1
        connection = MagicMock()
383 1
        switch = self.controller.get_switch_or_create(dpid, connection)
384
385 1
        expected_switches = {'00:00:00:00:00:00:00:01': switch}
386 1
        assert self.controller.switches == expected_switches
387 1
        self.controller.buffers.conn.put.assert_called()
388 1
        ev_name = "kytos/core.switch.new"
389 1
        assert self.controller.buffers.conn.put.call_args[0][0].name == ev_name
390
391 1
    def test_create_or_update_connection(self):
392
        """Test create_or_update_connection method."""
393 1
        self.controller.connections = {}
394
395 1
        connection = MagicMock()
396 1
        connection.id = '123'
397 1
        self.controller.create_or_update_connection(connection)
398
399 1
        assert self.controller.connections == {'123': connection}
400
401 1
    def test_get_connection_by_id(self):
402
        """Test get_connection_by_id method."""
403 1
        connection = MagicMock()
404 1
        connection.id = '123'
405 1
        self.controller.connections = {connection.id: connection}
406
407 1
        resp_connection = self.controller.get_connection_by_id('123')
408
409 1
        assert resp_connection == connection
410
411 1
    def test_remove_connection(self):
412
        """Test remove_connection method."""
413 1
        connection = MagicMock()
414 1
        connection.id = '123'
415 1
        self.controller.connections = {connection.id: connection}
416
417 1
        self.controller.remove_connection(connection)
418
419 1
        assert not self.controller.connections
420
421 1
    def test_remove_switch(self):
422
        """Test remove_switch method."""
423 1
        switch = MagicMock()
424 1
        switch.dpid = '00:00:00:00:00:00:00:01'
425 1
        self.controller.switches = {switch.dpid: switch}
426
427 1
        self.controller.remove_switch(switch)
428
429 1
        assert not self.controller.switches
430
431 1
    def test_remove_switch__error(self):
432
        """Test remove_switch method to error case."""
433 1
        switch_1 = MagicMock()
434 1
        switch_2 = MagicMock()
435 1
        switch_1.dpid = '00:00:00:00:00:00:00:01'
436 1
        switch_2.dpid = '00:00:00:00:00:00:00:02'
437 1
        self.controller.switches = {switch_1.dpid: switch_1}
438
439 1
        self.controller.remove_switch(switch_2)
440
441 1
        assert self.controller.switches == {switch_1.dpid: switch_1}
442
443 1
    def test_new_connection(self):
444
        """Test new_connection method."""
445 1
        self.controller.connections = {}
446
447 1
        connection = MagicMock()
448 1
        connection.id = '123'
449 1
        event = MagicMock()
450 1
        event.source = connection
451 1
        self.controller.new_connection(event)
452
453 1
        assert self.controller.connections == {'123': connection}
454
455 1
    def test_add_new_switch(self):
456
        """Test add_new_switch method."""
457 1
        self.controller.switches = {}
458
459 1
        switch = MagicMock()
460 1
        switch.dpid = '00:00:00:00:00:00:00:01'
461 1
        self.controller.add_new_switch(switch)
462
463 1
        expected_switches = {'00:00:00:00:00:00:00:01': switch}
464 1
        assert self.controller.switches == expected_switches
465
466 1
    @patch('kytos.core.controller.module_from_spec')
467 1
    @patch('kytos.core.controller.spec_from_file_location')
468 1
    def test_import_napp(self, *args):
469
        """Test _import_napp method."""
470 1
        (mock_spec_from_file, mock_module_from_spec) = args
471 1
        napp_spec = MagicMock()
472 1
        napp_spec.name = 'spec_name'
473 1
        mock_spec_from_file.return_value = napp_spec
474 1
        napp_module = MagicMock()
475 1
        mock_module_from_spec.return_value = napp_module
476
477 1
        self.controller.options.napps = 'napps'
478 1
        self.controller._import_napp('kytos', 'napp')
479
480 1
        assert sys.modules[napp_spec.name] == napp_module
481 1
        mock_spec_from_file.assert_called_with('napps.kytos.napp.main',
482
                                               'napps/kytos/napp/main.py')
483 1
        napp_spec.loader.exec_module.assert_called_with(napp_module)
484
485 1
    def test_load_napp__loaded(self):
486
        """Test load_napp method when napp is already loaded."""
487 1
        napp = MagicMock()
488 1
        self.controller.napps = {('kytos', 'napp'): napp}
489
490 1
        self.controller.load_napp('kytos', 'napp')
491
492 1
        assert self.controller.napps == {('kytos', 'napp'): napp}
493
494 1
    @patch('kytos.core.controller.Controller._import_napp')
495 1
    def test_load_napp__module_not_found(self, mock_import_napp):
496
        """Test load_napp method when module is not found."""
497 1
        mock_import_napp.side_effect = ModuleNotFoundError
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ModuleNotFoundError does not seem to be defined.
Loading history...
498 1
        self.controller.napps = {}
499
500 1
        self.controller.load_napp('kytos', 'napp')
501
502 1
        assert not self.controller.napps
503
504 1
    @patch('kytos.core.controller.Controller._import_napp')
505 1
    def test_load_napp__file_not_found(self, mock_import_napp):
506
        """Test load_napp method when file is not found."""
507 1
        mock_import_napp.side_effect = FileNotFoundError
508 1
        self.controller.napps = {}
509
510 1
        self.controller.load_napp('kytos', 'napp')
511
512 1
        assert not self.controller.napps
513
514 1
    @patch('kytos.core.api_server.APIServer.register_napp_endpoints')
515 1
    @patch('kytos.core.controller.Controller._import_napp')
516 1
    def test_load_napp__error(self, *args):
517
        """Test load_napp method when an error is raised on napp module
518
           attribution."""
519 1
        (mock_import_napp, _) = args
520 1
        self.controller.napps = {}
521
522 1
        module = MagicMock()
523 1
        module.Main.side_effect = Exception
524 1
        mock_import_napp.return_value = module
525
526 1
        with pytest.raises(KytosNAppSetupException):
527 1
            self.controller.load_napp('kytos', 'napp')
528
529 1
        assert not self.controller.napps
530
531 1
    @patch('kytos.core.api_server.APIServer.register_napp_endpoints')
532 1
    @patch('kytos.core.controller.Controller._import_napp')
533 1
    def test_load_napp(self, *args):
534
        """Test load_napp method."""
535 1
        (mock_import_napp, mock_register) = args
536 1
        self.controller.napps = {}
537
538 1
        napp = MagicMock()
539 1
        module = MagicMock()
540 1
        module.Main.return_value = napp
541 1
        mock_import_napp.return_value = module
542
543 1
        self.controller.load_napp('kytos', 'napp')
544
545 1
        assert self.controller.napps == {('kytos', 'napp'): napp}
546 1
        napp.start.assert_called()
547 1
        mock_register.assert_called_with(napp)
548
549 1
    def test_pre_install_napps(self):
550
        """Test pre_install_napps method."""
551 1
        napp_1 = MagicMock()
552 1
        napp_2 = MagicMock()
553 1
        installed_napps = [napp_1]
554 1
        napps = [str(napp_1), str(napp_2)]
555 1
        self.napps_manager.get_installed_napps.return_value = installed_napps
556
557 1
        self.controller.pre_install_napps(napps)
558
559 1
        self.napps_manager.install.assert_called_with(str(napp_2), enable=True)
560
561 1
    @patch('kytos.core.controller.Controller.load_napp')
562 1
    def test_load_napps(self, mock_load):
563
        """Test load_napps method."""
564 1
        napp = MagicMock()
565 1
        napp.username = 'kytos'
566 1
        napp.name = 'name'
567 1
        enabled_napps = [napp]
568 1
        self.napps_manager.get_enabled_napps.return_value = enabled_napps
569
570 1
        self.controller.load_napps()
571
572 1
        mock_load.assert_called_with('kytos', 'name')
573
574 1
    @patch('kytos.core.controller.Controller.unload_napp')
575 1
    def test_unload_napps(self, mock_unload):
576
        """Test un_load_napps method."""
577 1
        napp_tuples = [("kytos", "of_core"), ("kytos", "mef_eline")]
578 1
        enabled_napps = []
579 1
        expected_calls = []
580 1
        for username, napp_name in napp_tuples:
581 1
            mock = MagicMock()
582 1
            mock.username = username
583 1
            mock.name = napp_name
584 1
            enabled_napps.append(mock)
585 1
            expected_calls.append(call(mock.username, mock.name))
586 1
        self.napps_manager.get_enabled_napps.return_value = enabled_napps
587
588 1
        self.controller.unload_napps()
589 1
        assert mock_unload.call_count == len(enabled_napps)
590 1
        assert mock_unload.mock_calls == list(reversed(expected_calls))
591
592 1
    @patch('kytos.core.controller.import_module')
593 1
    def test_reload_napp_module__module_not_found(self, mock_import_module):
594
        """Test reload_napp_module method when module is not found."""
595 1
        mock_import_module.side_effect = ModuleNotFoundError
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ModuleNotFoundError does not seem to be defined.
Loading history...
596
597 1
        with pytest.raises(ModuleNotFoundError):
598 1
            self.controller.reload_napp_module('kytos', 'napp', 'napp_file')
599
600 1
    @patch('kytos.core.controller.reload_module')
601 1
    @patch('kytos.core.controller.import_module')
602 1
    def test_reload_napp_module__import_error(self, *args):
603
        """Test reload_napp_module method when an import error occurs."""
604 1
        (mock_import_module, mock_reload_module) = args
605 1
        napp_module = MagicMock()
606 1
        mock_import_module.return_value = napp_module
607 1
        mock_reload_module.side_effect = ImportError
608
609 1
        with pytest.raises(ImportError):
610 1
            self.controller.reload_napp_module('kytos', 'napp', 'napp_file')
611
612 1
    @patch('kytos.core.controller.reload_module')
613 1
    @patch('kytos.core.controller.import_module')
614 1
    def test_reload_napp_module(self, *args):
615
        """Test reload_napp_module method."""
616 1
        (mock_import_module, mock_reload_module) = args
617 1
        napp_module = MagicMock()
618 1
        mock_import_module.return_value = napp_module
619
620 1
        self.controller.reload_napp_module('kytos', 'napp', 'napp_file')
621
622 1
        mock_import_module.assert_called_with('napps.kytos.napp.napp_file')
623 1
        mock_reload_module.assert_called_with(napp_module)
624
625 1
    @patch('kytos.core.controller.Controller.load_napp')
626 1
    @patch('kytos.core.controller.Controller.unload_napp')
627 1
    @patch('kytos.core.controller.Controller.reload_napp_module')
628 1
    def test_reload_napp(self, *args):
629
        """Test reload_napp method."""
630 1
        (mock_reload_napp_module, mock_unload, mock_load) = args
631
632 1
        code = self.controller.reload_napp('kytos', 'napp')
633
634 1
        mock_unload.assert_called_with('kytos', 'napp')
635 1
        calls = [call('kytos', 'napp', 'settings'),
636
                 call('kytos', 'napp', 'main')]
637 1
        mock_reload_napp_module.assert_has_calls(calls)
638 1
        mock_load.assert_called_with('kytos', 'napp')
639 1
        assert code == 200
640
641 1
    @patch('kytos.core.controller.Controller.unload_napp')
642 1
    @patch('kytos.core.controller.Controller.reload_napp_module')
643 1
    def test_reload_napp__error(self, *args):
644
        """Test reload_napp method to error case."""
645 1
        (mock_reload_napp_module, _) = args
646 1
        mock_reload_napp_module.side_effect = ModuleNotFoundError
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ModuleNotFoundError does not seem to be defined.
Loading history...
647
648 1
        code = self.controller.reload_napp('kytos', 'napp')
649
650 1
        assert code == 400
651
652 1
    @patch('kytos.core.controller.Controller.reload_napp', return_value=200)
653 1
    def test_rest_reload_napp(self, mock_reload_napp):
654
        """Test rest_reload_napp method."""
655 1
        req = Request(
656
            scope={
657
                "type": "http",
658
                "path_params": {"username": "kytos", "napp_name": "napp"},
659
            }
660
        )
661 1
        resp = self.controller.rest_reload_napp(req)
662
663 1
        mock_reload_napp.assert_called_with('kytos', 'napp')
664 1
        assert json.loads(resp.body.decode()) == 'reloaded'
665 1
        assert resp.status_code == 200
666
667 1
    @patch('kytos.core.controller.Controller.reload_napp')
668 1
    def test_rest_reload_all_napps(self, mock_reload_napp):
669
        """Test rest_reload_all_napps method."""
670 1
        req = Request(
671
            scope={
672
                "type": "http",
673
                "path_params": {"username": "kytos", "napp_name": "napp"},
674
            }
675
        )
676 1
        self.controller.napps = [('kytos', 'napp')]
677 1
        resp = self.controller.rest_reload_all_napps(req)
678
679 1
        mock_reload_napp.assert_called_with('kytos', 'napp')
680 1
        assert json.loads(resp.body.decode()) == 'reloaded'
681 1
        assert resp.status_code == 200
682
683 1
    def test_init_attrs(self):
684
        """Test init attrs."""
685 1
        self.controller.start_auth()
686 1
        assert self.controller.auth
687 1
        assert self.controller.dead_letter
688
689 1
    def test_try_to_fmt_traceback_msg(self) -> None:
690
        """Test test_try_to_fmt_traceback_msg."""
691 1
        counter = Counter(range(5))
692 1
        msg = "some traceback msg"
693 1
        fmt_msg = self.controller._try_to_fmt_traceback_msg(msg, counter)
694 1
        assert msg in fmt_msg
695 1
        assert "counters" in fmt_msg
696
697 1
    def test_config_default_maxsize_multiplier(self) -> None:
698
        """Test KytosConfig default maxsize multiplier."""
699 1
        event_buffer_conf = self.controller.options.event_buffer_conf
700 1
        assert event_buffer_conf
701 1
        queues = event_buffer_conf.values()
702 1
        assert queues
703 1
        for queue in queues:
704 1
            assert queue["queue"]["maxsize_multiplier"] == 2
705
706
707 1
class TestControllerAsync:
708
709
    """TestControllerAsync."""
710
711 1
    async def test_start_controller(self, controller, monkeypatch):
712
        """Test start controller."""
713 1
        controller._buffers = KytosBuffers()
714 1
        controller.loop = MagicMock()
715 1
        server = MagicMock()
716 1
        monkeypatch.setattr("kytos.core.controller.KytosServer", server)
717 1
        napp = MagicMock()
718 1
        controller._pool = MagicMock()
719 1
        controller.pre_install_napps = MagicMock()
720 1
        controller.api_server = MagicMock()
721 1
        controller.load_napps = MagicMock()
722 1
        controller.options.napps_pre_installed = [napp]
723 1
        controller.start_controller()
724 1
        assert controller.buffers
725
726 1
        controller.server.serve_forever.assert_called()
727 1
        all_buffers = controller.buffers.get_all_buffers()
728
        # It's expected that all buffers have a task + the api server task
729 1
        assert controller.loop.create_task.call_count == len(all_buffers) + 1
730 1
        assert len(controller._tasks) == len(all_buffers) + 1
731 1
        controller.pre_install_napps.assert_called_with([napp])
732 1
        controller.load_napps.assert_called()
733 1
        controller.api_server.start_web_ui.assert_called()
734
735 1
    async def test_stop_controller(self, controller):
736
        """Test stop_controller method."""
737 1
        controller.loop = MagicMock()
738 1
        api_server = MagicMock()
739 1
        napp_dir_listener = MagicMock()
740 1
        controller.server = MagicMock()
741 1
        controller.unload_napps = MagicMock()
742 1
        controller._buffers = MagicMock()
743 1
        controller.api_server = api_server
744 1
        controller.napp_dir_listener = napp_dir_listener
745
746 1
        controller.stop_controller()
747
748 1
        controller.buffers.send_stop_signal.assert_called()
749 1
        api_server.stop.assert_called()
750 1
        napp_dir_listener.stop.assert_called()
751 1
        controller.unload_napps.assert_called()
752 1
        controller.server.shutdown.assert_called()
753 1
        controller.loop.stop.assert_called()
754
755 1
    async def test_raw_event_handler(self, controller):
756
        """Test raw_event_handler async method by handling a shutdown event."""
757 1
        controller._buffers = KytosBuffers()
758 1
        event = KytosEvent("kytos/core.shutdown")
759 1
        controller.notify_listeners = MagicMock()
760 1
        await controller.buffers.raw._queue.async_q.put(event)
761 1
        await controller.event_handler("raw")
762 1
        controller.notify_listeners.assert_called_with(event)
763
764 1
    async def test_msg_in_event_handler(self, controller):
765
        """Test msg_in_event_handler async method by handling a shutdown
766
           event."""
767 1
        controller._buffers = KytosBuffers()
768 1
        event = KytosEvent("kytos/core.shutdown")
769 1
        controller.notify_listeners = MagicMock()
770 1
        await controller.buffers.msg_in._queue.async_q.put(event)
771 1
        await controller.event_handler("msg_in")
772 1
        controller.notify_listeners.assert_called_with(event)
773
774 1
    async def test_msg_out_event_handler(self, controller):
775
        """Test msg_out_event_handler async method by handling a common and a
776
           shutdown event."""
777 1
        controller._buffers = KytosBuffers()
778 1
        controller.notify_listeners = MagicMock()
779 1
        dst = MagicMock()
780 1
        dst.state = 0
781 1
        packet = MagicMock()
782 1
        msg = MagicMock()
783 1
        msg.pack.return_value = packet
784
785 1
        event_1 = KytosEvent('kytos/core.any',
786
                             content={'message': msg, 'destination': dst})
787 1
        event_2 = KytosEvent('kytos/core.shutdown')
788
789 1
        await controller.buffers.msg_out._queue.async_q.put(event_1)
790 1
        await controller.buffers.msg_out._queue.async_q.put(event_2)
791 1
        await controller.msg_out_event_handler()
792 1
        dst.send.assert_called_with(packet)
793 1
        controller.notify_listeners.assert_called_with(event_1)
794
795 1
    async def test_msg_out_event_handler_pack_exc(self, controller):
796
        """Test msg_out_event_handler async pack exception."""
797 1
        controller._buffers = KytosBuffers()
798 1
        dst, msg = MagicMock(), MagicMock()
799 1
        dst.state = 0
800 1
        msg.pack.side_effect = PackException("some error")
801 1
        event_1 = KytosEvent('kytos/core.any',
802
                             content={'message': msg, 'destination': dst})
803 1
        event_2 = KytosEvent('kytos/core.shutdown')
804
805 1
        await controller.buffers.msg_out._queue.async_q.put(event_1)
806 1
        await controller.buffers.msg_out._queue.async_q.put(event_2)
807 1
        await controller.msg_out_event_handler()
808 1
        assert controller.log.error.call_count == 1
809
810 1
    async def test_app_event_handler(self, controller):
811
        """Test app_event_handler async method by handling a shutdown event."""
812 1
        controller._buffers = KytosBuffers()
813 1
        event = KytosEvent("kytos/core.shutdown")
814 1
        controller.notify_listeners = MagicMock()
815 1
        await controller.buffers.app._queue.async_q.put(event)
816 1
        await controller.event_handler("app")
817 1
        controller.notify_listeners.assert_called_with(event)
818
819 1
    async def test_configuration_endpoint(self, controller, api_client):
820
        """Should return the attribute options as json."""
821 1
        expected = vars(controller.options)
822 1
        expected.pop("jwt_secret", None)
823 1
        resp = await api_client.get("kytos/core/config")
824 1
        assert resp.status_code == 200
825 1
        assert expected == resp.json()
826
827 1
    async def test_publish_connection_error(self, controller):
828
        """Test publish_connection_error."""
829 1
        controller.buffers.conn.aput = AsyncMock()
830 1
        await controller.publish_connection_error(MagicMock())
831 1
        controller.buffers.conn.aput.assert_called()
832
833 1
    async def test_full_queue_counter(self, controller) -> None:
834
        """Test full queue counter."""
835 1
        maxsize = 2
836 1
        queue = Queue(maxsize=maxsize)
837 1
        buffer = KytosEventBuffer("app", queue)
838 1
        for i in range(maxsize):
839 1
            await buffer.aput(KytosEvent(str(i)))
840 1
        assert buffer.full()
841 1
        controller._buffers.get_all_buffers.return_value = [buffer]
842 1
        counter = controller._full_queue_counter()
843 1
        assert counter
844 1
        assert len(counter["app"]) == maxsize
845 1
        queue.close()
846
        await queue.wait_closed()
847