Test Failed
Pull Request — master (#375)
by Vinicius
05:51
created

tests.unit.test_core.test_api_server   C

Complexity

Total Complexity 53

Size/Duplication

Total Lines 518
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 369
dl 0
loc 518
rs 6.96
c 0
b 0
f 0
ccs 313
cts 313
cp 1
wmc 53

1 Function

Rating   Name   Duplication   Size   Complexity  
A test_custom_encoder() 0 5 1

40 Methods

Rating   Name   Duplication   Size   Complexity  
A TestAPIServer.setup_method() 0 13 1
A TestAPIServer.test_enable_napp__success() 0 7 1
A TestAPIServer.test_update_web_ui_error() 0 33 2
A TestAPIServer.test_get_napp_metadata() 0 8 1
A TestAPIServer.test_fetch_latest_ui_tag() 0 11 1
A TestAPIServer.test_fetch_latest_ui_tag_fallback() 0 9 1
A TestAPIServer.test_install_napp__http_error() 0 10 1
A TestAPIServer.test_uninstall_napp__success_not_installed() 0 8 1
A TestAPIServer.test_status_api() 0 5 1
A TestAPIServer.test_disable_napp__error_not_enabling() 0 7 1
A TestAPIServer.test_web_ui() 0 14 4
A TestAPIServer.test_get_ui_components() 0 12 1
A TestAPIServer.test_enable_napp__error_not_installed() 0 6 1
A TestAPIDecorator.test_sync_route() 0 22 2
A TestAPIServer.test_install_napp__error_not_installing() 0 7 1
A TestAPIServer.test_install_napp__success() 0 7 1
A TestAPIServer.test_install_napp__success_is_installed() 0 6 1
A TestAPIServer.test_static_web_ui() 0 12 3
A TestAPIServer.test_list_enabled_napps() 0 9 1
A RESTNApp.__init__() 0 4 1
A TestAPIServer.test_get_napp_metadata__not_installed() 0 7 1
A TestAPIServer.test_get_napp_metadata__invalid_key() 0 6 1
A TestAPIServer.test_list_installed_napps() 0 9 1
A TestAPIServer.test_unzip_backup_web_ui_error() 0 11 2
A TestAPIDecorator.test_async_route() 0 16 1
A TestAPIServer.test_deprecation_warning() 0 10 3
A TestAPIServer.test_disable_napp__success() 0 7 1
A TestAPIServer.test_disable_napp__error_not_installed() 0 6 1
A TestAPIServer.test_enable_napp__error_not_enabling() 0 7 1
A TestAPIServer.test_unzip_backup_web_ui() 0 21 2
A TestAPIServer.test_uninstall_napp__error_not_uninstalling() 0 9 1
A TestAPIServer.test_run() 0 4 1
A TestAPIServer.test_uninstall_napp__success() 0 7 1
A TestAPIServer.test_update_web_ui() 0 24 2
A TestAPIServer.test_stop_api_server() 0 4 1
A TestAPIServer.test_run_error() 0 7 1
A TestAPIDecorator.test_route_ordering() 0 29 1
A TestAPIDecorator.test_route_from_classmethod() 0 15 1
A TestAPIDecorator.test_remove_napp_endpoints() 0 18 1
A TestAPIDecorator.test_route_from_staticmethod() 0 15 1

How to fix   Complexity   

Complexity

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

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

1
"""APIServer tests."""
2 1
import asyncio
3 1
import json
4 1
import warnings
5 1
from datetime import datetime
6
# Disable not-grouped imports that conflicts with isort
7 1
from unittest.mock import MagicMock
8
from urllib.error import HTTPError, URLError
9 1
10
import pytest
11 1
from httpx import AsyncClient, RequestError
12 1
13
from kytos.core.api_server import APIServer
14 1
from kytos.core.napps import rest
15 1
from kytos.core.rest_api import JSONResponse, Request
16
17
18 1
def test_custom_encoder() -> None:
19
    """Test json custom encoder."""
20 1
    some_datetime = datetime(year=2022, month=1, day=30)
21 1
    resp = JSONResponse(some_datetime)
22 1
    assert json.loads(resp.body.decode()) == "2022-01-30T00:00:00"
23
24
25
# pylint: disable=protected-access,attribute-defined-outside-init
26 1
class TestAPIServer:
27
    """Test the class APIServer."""
28
29 1
    def setup_method(self):
30
        """Instantiate a APIServer."""
31 1
        self.api_server = APIServer()
32 1
        self.app = self.api_server.app
33 1
        self.napps_manager = MagicMock()
34 1
        self.api_server.server = MagicMock()
35 1
        self.api_server.napps_manager = self.napps_manager
36 1
        self.api_server.napps_dir = 'napps_dir'
37
        self.api_server.flask_dir = 'flask_dir'
38 1
        self.api_server.start_api()
39
        self.api_server.start_web_ui()
40 1
        base_url = "http://127.0.0.1/api/kytos/core/"
41 1
        self.client = AsyncClient(app=self.app, base_url=base_url)
42 1
43
    def test_deprecation_warning(self):
44 1
        """Deprecated method should suggest @rest decorator."""
45 1
        with warnings.catch_warnings(record=True) as wrngs:
46 1
            warnings.simplefilter("always")  # trigger all warnings
47 1
            self.api_server.register_rest_endpoint(
48
                'rule', lambda x: x, ['POST'])
49 1
            assert 1 == len(wrngs)
50
            warning = wrngs[0]
51 1
            assert warning.category == DeprecationWarning
52
            assert '@rest' in str(warning.message)
53 1
54
    def test_run(self):
55
        """Test run method."""
56
        self.api_server.run()
57 1
        self.api_server.server.run.assert_called_with()
58 1
59
    def test_run_error(self, monkeypatch):
60 1
        """Test run method to error case."""
61 1
        mock_exit = MagicMock()
62
        monkeypatch.setattr("sys.exit", mock_exit)
63 1
        self.api_server.server.run.side_effect = OSError
64
        self.api_server.run()
65 1
        mock_exit.assert_called()
66
67 1
    async def test_status_api(self):
68 1
        """Test status_api method."""
69 1
        response = await self.client.get("status/")
70 1
        assert response.status_code == 200
71
        assert response.json() == {"response": "running"}
72 1
73
    def test_stop_api_server(self):
74 1
        """Test stop_api_server method."""
75 1
        self.api_server.stop_api_server()
76 1
        assert self.api_server.server.should_exit
77 1
78
    @pytest.mark.parametrize("file_exist", [True, False])
79 1
    async def test_static_web_ui(self, monkeypatch, file_exist):
80
        """Test static_web_ui method to success case."""
81 1
        monkeypatch.setattr("os.path.exists", lambda x: file_exist)
82 1
        monkeypatch.setattr("kytos.core.api_server.FileResponse",
83
                            lambda x: JSONResponse({}))
84 1
85 1
        endpoint = f"/ui/kytos/napp/{self.api_server.napps_dir}"
86
        client = AsyncClient(app=self.app, base_url="http://127.0.0.1")
87 1
        response = await client.get(endpoint)
88
        expected_status_code = {True: 200, False: 404}
89 1
        assert expected_status_code[file_exist] == response.status_code
90 1
91
    async def test_get_ui_components(self, monkeypatch):
92 1
        """Test get_ui_components method."""
93 1
        mock_glob = MagicMock()
94 1
        mock_glob.return_value = ['napps_dir/*/*/ui/*/*.kytos']
95
        monkeypatch.setattr("kytos.core.api_server.glob", mock_glob)
96 1
        expected_json = [{'name': '*-*-*-*', 'url': 'ui/*/*/*/*.kytos'}]
97 1
98
        endpoint = "/ui/k-toolbar/"
99 1
        client = AsyncClient(app=self.app, base_url="http://127.0.0.1")
100
        resp = await client.get(endpoint)
101 1
        assert resp.status_code == 200
102 1
        assert resp.json() == expected_json
103
104 1
    @pytest.mark.parametrize("file_exist", [True, False])
105
    async def test_web_ui(self, monkeypatch, file_exist):
106 1
        """Test web_ui method."""
107 1
        monkeypatch.setattr("os.path.exists", lambda x: file_exist)
108
        monkeypatch.setattr("kytos.core.api_server.FileResponse",
109 1
                            lambda x: JSONResponse({}))
110 1
111
        client = AsyncClient(app=self.app, base_url="http://127.0.0.1")
112 1
        coros = [client.get("/index.html"), client.get("/")]
113 1
        coros = asyncio.gather(*coros)
114 1
        responses = await coros
115
        expected_status_code = {True: 200, False: 404}
116 1
        for response in responses:
117 1
            assert expected_status_code[file_exist] == response.status_code
118 1
119
    def test_unzip_backup_web_ui(self, monkeypatch) -> None:
120 1
        """Test _unzip_backup_web_ui."""
121 1
        mock_log, mock_shutil = MagicMock(), MagicMock()
122 1
        mock_zipfile, zipfile = MagicMock(), MagicMock()
123
        zip_data, mock_mkdir = MagicMock(), MagicMock()
124 1
        zipfile.__enter__.return_value = zip_data
125 1
        zip_data.testzip.return_value = None
126
        mock_zipfile.return_value = zipfile
127 1
128
        monkeypatch.setattr("zipfile.ZipFile", mock_zipfile)
129 1
        monkeypatch.setattr("os.path.exists", lambda x: True)
130 1
        monkeypatch.setattr("os.mkdir", mock_mkdir)
131
        monkeypatch.setattr("shutil.move", mock_shutil)
132 1
        monkeypatch.setattr("kytos.core.api_server.LOG", mock_log)
133 1
        package, uri = "/tmp/file", "http://localhost/some_file.zip"
134
        self.api_server._unzip_backup_web_ui(package, uri)
135 1
        assert mock_mkdir.call_count == 1
136
        assert mock_shutil.call_count == 1
137 1
        assert zip_data.extractall.call_count == 1
138 1
        assert zip_data.close.call_count == 1
139 1
        assert mock_log.info.call_count == 1
140 1
141 1
    def test_unzip_backup_web_ui_error(self, monkeypatch) -> None:
142 1
        """Test _unzip_backup_web_ui error."""
143 1
        mock_log, mock_zipfile, zipdata = MagicMock(), MagicMock(), MagicMock()
144
        mock_zipfile.__enter__.return_value = zipdata
145 1
        monkeypatch.setattr("zipfile.ZipFile", mock_zipfile)
146
        monkeypatch.setattr("kytos.core.api_server.LOG", mock_log)
147 1
        package, uri = "/tmp/file", "http://localhost/some_file.zip"
148 1
        with pytest.raises(ValueError) as exc:
149 1
            self.api_server._unzip_backup_web_ui(package, uri)
150 1
        assert "is corrupted" in str(exc)
151 1
        assert mock_log.error.call_count == 1
152
153 1
    def test_fetch_latest_ui_tag(self, monkeypatch) -> None:
154 1
        """Test fetch lastest ui."""
155 1
        mock_get, mock_res, ver = MagicMock(), MagicMock(), "X.Y.Z"
156
        mock_get.return_value = mock_res
157 1
        mock_res.json.return_value = {"tag_name": ver}
158
        monkeypatch.setattr("kytos.core.api_server.httpx.get", mock_get)
159 1
        version = self.api_server._fetch_latest_ui_tag()
160
        assert version == ver
161 1
        url = 'https://api.github.com/repos/kytos-ng/' \
162
              'ui/releases/latest'
163 1
        mock_get.assert_called_with(url, timeout=10)
164
165 1
    def test_fetch_latest_ui_tag_fallback(self, monkeypatch) -> None:
166
        """Test fetch lastest ui fallback tag."""
167 1
        mock_get, mock_log = MagicMock(), MagicMock()
168
        mock_get.side_effect = RequestError("some error")
169 1
        monkeypatch.setattr("kytos.core.api_server.httpx.get", mock_get)
170 1
        monkeypatch.setattr("kytos.core.api_server.LOG", mock_log)
171 1
        version = self.api_server._fetch_latest_ui_tag()
172
        assert version == "2022.3.0"
173 1
        assert mock_log.warning.call_count == 1
174 1
175 1
    async def test_update_web_ui(self, monkeypatch):
176 1
        """Test update_web_ui method."""
177
        mock_log, mock_urlretrieve = MagicMock(), MagicMock()
178 1
        mock_urlretrieve.return_value = ["/tmp/file"]
179
        monkeypatch.setattr("os.path.exists", lambda x: False)
180 1
        monkeypatch.setattr("kytos.core.api_server.LOG", mock_log)
181 1
        monkeypatch.setattr("kytos.core.api_server.urlretrieve",
182 1
                            mock_urlretrieve)
183
184 1
        version = "2022.3.0"
185
        mock_fetch = MagicMock(return_value=version)
186 1
        self.api_server._fetch_latest_ui_tag = mock_fetch
187
        self.api_server._unzip_backup_web_ui = MagicMock()
188 1
        response = await self.client.post("web/update")
189 1
        assert response.status_code == 200
190
        assert response.json() == "Web UI was updated"
191
192 1
        assert self.api_server._fetch_latest_ui_tag.call_count == 1
193
        assert self.api_server._unzip_backup_web_ui.call_count == 1
194 1
        assert mock_log.info.call_count == 2
195
196 1
        url = 'https://github.com/kytos-ng/ui/releases/' + \
197
              f'download/{version}/latest.zip'
198 1
        mock_urlretrieve.assert_called_with(url)
199 1
200
    @pytest.mark.parametrize(
201 1
        "err_name,exp_msg",
202 1
        [
203 1
            ("HTTPError", "Uri not found"),
204 1
            ("URLError", "Error accessing"),
205 1
        ],
206
    )
207
    async def test_update_web_ui_error(self, monkeypatch, err_name, exp_msg):
208 1
        """Test update_web_ui method error.
209 1
210 1
        HTTPError had issues when being initialized in the decorator args,
211 1
        so it it's being instantiated dynamically in the test body, see:
212
        https://bugs.python.org/issue45955
213 1
        """
214
        mock_log, mock_urlretrieve = MagicMock(), MagicMock()
215 1
        http_error = HTTPError("errorx", 500, "errorx", {}, None)
216 1
        errs = {"HTTPError": http_error, "URLError": URLError("some reason")}
217
        mock_urlretrieve.side_effect = errs[err_name]
218 1
        monkeypatch.setattr("os.path.exists", lambda x: False)
219
        monkeypatch.setattr("kytos.core.api_server.LOG", mock_log)
220 1
        monkeypatch.setattr("kytos.core.api_server.urlretrieve",
221
                            mock_urlretrieve)
222 1
223
        version = "2022.3.0"
224 1
        self.api_server._unzip_backup_web_ui = MagicMock()
225
        response = await self.client.post(f"web/update/{version}")
226 1
        assert response.status_code == 500
227
        assert exp_msg in response.json()
228
        assert mock_log.error.call_count == 1
229 1
230 1
        url = 'https://github.com/kytos-ng/ui/releases/' + \
231
              f'download/{version}/latest.zip'
232 1
        mock_urlretrieve.assert_called_with(url)
233
234 1
    async def test_enable_napp__error_not_installed(self):
235
        """Test _enable_napp method error case when napp is not installed."""
236 1
        self.napps_manager.is_installed.return_value = False
237
        resp = await self.client.get("/napps/kytos/napp/enable")
238 1
        assert resp.status_code == 400
239 1
        assert resp.json() == {"response": "not installed"}
240
241 1
    async def test_enable_napp__error_not_enabling(self):
242
        """Test _enable_napp method error case when napp is not enabling."""
243 1
        self.napps_manager.is_installed.return_value = True
244 1
        self.napps_manager.is_enabled.side_effect = [False, False]
245
        resp = await self.client.get("napps/kytos/napp/enable")
246 1
        assert resp.status_code == 500
247
        assert resp.json() == {"response": "error"}
248 1
249 1
    async def test_enable_napp__success(self):
250
        """Test _enable_napp method success case."""
251 1
        self.napps_manager.is_installed.return_value = True
252
        self.napps_manager.is_enabled.side_effect = [False, True]
253 1
        resp = await self.client.get("napps/kytos/napp/enable")
254 1
        assert resp.status_code == 200
255
        assert resp.json() == {"response": "enabled"}
256 1
257
    async def test_disable_napp__error_not_installed(self):
258 1
        """Test _disable_napp method error case when napp is not installed."""
259 1
        self.napps_manager.is_installed.return_value = False
260
        resp = await self.client.get("napps/kytos/napp/disable")
261 1
        assert resp.status_code == 400
262
        assert resp.json() == {"response": "not installed"}
263 1
264
    async def test_disable_napp__error_not_enabling(self):
265 1
        """Test _disable_napp method error case when napp is not enabling."""
266
        self.napps_manager.is_installed.return_value = True
267 1
        self.napps_manager.is_enabled.side_effect = [True, True]
268 1
        resp = await self.client.get("napps/kytos/napp/disable")
269
        assert resp.status_code == 500
270 1
        assert resp.json() == {"response": "error"}
271
272 1
    async def test_disable_napp__success(self):
273 1
        """Test _disable_napp method success case."""
274
        self.napps_manager.is_installed.return_value = True
275 1
        self.napps_manager.is_enabled.side_effect = [True, False]
276
        resp = await self.client.get("napps/kytos/napp/disable")
277 1
        assert resp.status_code == 200
278 1
        assert resp.json() == {"response": "disabled"}
279
280 1
    async def test_install_napp__error_not_installing(self):
281
        """Test _install_napp method error case when napp is not installing."""
282 1
        self.napps_manager.is_installed.return_value = False
283 1
        self.napps_manager.install.return_value = False
284
        resp = await self.client.get("napps/kytos/napp/install")
285 1
        assert resp.status_code == 500
286
        assert resp.json() == {"response": "error"}
287 1
288 1
    async def test_install_napp__http_error(self):
289
        """Test _install_napp method to http error case."""
290 1
        self.napps_manager.is_installed.return_value = False
291
        self.napps_manager.install.side_effect = HTTPError('url', 123, 'msg',
292 1
                                                           'hdrs', MagicMock())
293 1
294
        resp = await self.client.get("napps/kytos/napp/install")
295 1
        assert resp.status_code == 123
296
        assert resp.json() == {"response": "error"}
297 1
        self.napps_manager.install.side_effect = None
298 1
299
    async def test_install_napp__success_is_installed(self):
300 1
        """Test _install_napp method success case when napp is installed."""
301
        self.napps_manager.is_installed.return_value = True
302 1
        resp = await self.client.get("napps/kytos/napp/install")
303 1
        assert resp.status_code == 200
304
        assert resp.json() == {"response": "installed"}
305
306 1
    async def test_install_napp__success(self):
307
        """Test _install_napp method success case."""
308 1
        self.napps_manager.is_installed.return_value = False
309 1
        self.napps_manager.install.return_value = True
310
        resp = await self.client.get("napps/kytos/napp/install")
311 1
        assert resp.status_code == 200
312
        assert resp.json() == {"response": "installed"}
313 1
314
    async def test_uninstall_napp__error_not_uninstalling(self):
315 1
        """Test _uninstall_napp method error case when napp is not
316
           uninstalling.
317 1
        """
318 1
        self.napps_manager.is_installed.return_value = True
319
        self.napps_manager.uninstall.return_value = False
320 1
        resp = await self.client.get("napps/kytos/napp/uninstall")
321
        assert resp.status_code == 500
322 1
        assert resp.json() == {"response": "error"}
323 1
324
    async def test_uninstall_napp__success_not_installed(self):
325 1
        """Test _uninstall_napp method success case when napp is not
326
           installed.
327 1
        """
328 1
        self.napps_manager.is_installed.return_value = False
329
        resp = await self.client.get("napps/kytos/napp/uninstall")
330 1
        assert resp.status_code == 200
331
        assert resp.json() == {"response": "uninstalled"}
332
333
    async def test_uninstall_napp__success(self):
334 1
        """Test _uninstall_napp method success case."""
335 1
        self.napps_manager.is_installed.return_value = True
336
        self.napps_manager.uninstall.return_value = True
337 1
        resp = await self.client.get("napps/kytos/napp/uninstall")
338
        assert resp.status_code == 200
339 1
        assert resp.json() == {"response": "uninstalled"}
340 1
341
    async def test_list_enabled_napps(self):
342 1
        """Test _list_enabled_napps method."""
343
        napp = MagicMock()
344
        napp.username = "kytos"
345
        napp.name = "name"
346 1
        self.napps_manager.get_enabled_napps.return_value = [napp]
347
        resp = await self.client.get("napps_enabled")
348 1
        assert resp.status_code == 200
349
        assert resp.json() == {"napps": [["kytos", "name"]]}
350 1
351 1
    async def test_list_installed_napps(self):
352
        """Test _list_installed_napps method."""
353 1
        napp = MagicMock()
354
        napp.username = "kytos"
355 1
        napp.name = "name"
356 1
        self.napps_manager.get_installed_napps.return_value = [napp]
357
        resp = await self.client.get("napps_installed")
358 1
        assert resp.status_code == 200
359
        assert resp.json() == {"napps": [["kytos", "name"]]}
360 1
361 1
    async def test_get_napp_metadata__not_installed(self):
362
        """Test _get_napp_metadata method to error case when napp is not
363 1
           installed."""
364
        self.napps_manager.is_installed.return_value = False
365 1
        resp = await self.client.get("napps/kytos/napp/metadata/version")
366 1
        assert resp.status_code == 400
367 1
        assert resp.json() == "NApp is not installed."
368 1
369
    async def test_get_napp_metadata__invalid_key(self):
370 1
        """Test _get_napp_metadata method to error case when key is invalid."""
371
        self.napps_manager.is_installed.return_value = True
372 1
        resp = await self.client.get("napps/kytos/napp/metadata/any")
373 1
        assert resp.status_code == 400
374
        assert resp.json() == "Invalid key."
375 1
376
    async def test_get_napp_metadata(self):
377 1
        """Test _get_napp_metadata method."""
378 1
        value = "1.0"
379 1
        self.napps_manager.is_installed.return_value = True
380 1
        self.napps_manager.get_napp_metadata.return_value = value
381
        resp = await self.client.get("napps/kytos/napp/metadata/version")
382 1
        assert resp.status_code == 200
383
        assert resp.json() == {"version": value}
384 1
385 1
386
class RESTNApp:  # pylint: disable=too-few-public-methods
387 1
    """Bare minimum for the decorator to work. Not a functional NApp."""
388
389
    def __init__(self):
390 1
        self.username = 'test'
391 1
        self.name = 'MyNApp'
392
        self.napp_id = 'test/MyNApp'
393
394 1
395 1
class TestAPIDecorator:
396
    """Test suite for @rest decorator."""
397 1
398
    async def test_sync_route(self, controller, api_client):
399 1
        """Test rest decorator sync route."""
400 1
        class MyNApp(RESTNApp):
401
            """API decorator example usage."""
402
403 1
            @rest("some_route/", methods=["POST"])
404 1
            def some_endpoint(self, _request: Request) -> JSONResponse:
405
                """Some endpoint."""
406 1
                return JSONResponse({"some_response": "some_value"})
407
408 1
        napp = MyNApp()
409
        controller.api_server.register_napp_endpoints(napp)
410
411 1
        routes = ["test/MyNApp/some_route/", "test/MyNApp/some_route"]
412 1
        coros = [api_client.post(route) for route in routes]
413 1
        responses = await asyncio.gather(*coros)
414
        for resp in responses:
415
            assert resp.status_code == 200
416 1
            assert resp.json() == {"some_response": "some_value"}
417 1
418 1
        resp = await api_client.get("test/MyNApp/some_route/")
419
        assert resp.status_code == 405
420
421 1
    async def test_async_route(self, controller, api_client):
422
        """Test rest decorator async route."""
423
        class MyNApp(RESTNApp):
424 1
            """API decorator example usage."""
425 1
426 1
            @rest("some_route/", methods=["POST"])
427 1
            async def some_endpoint(self, _request: Request) -> JSONResponse:
428
                """Some endpoint."""
429
                await asyncio.sleep(0)
430 1
                return JSONResponse({})
431
432
        napp = MyNApp()
433 1
        controller.api_server.register_napp_endpoints(napp)
434 1
        resp = await api_client.post("test/MyNApp/some_route/")
435 1
        assert resp.status_code == 200
436
        assert resp.json() == {}
437 1
438
    async def test_route_ordering(self, controller, api_client):
439 1
        """Test rest decorator route ordering."""
440
        class MyNApp(RESTNApp):
441 1
            """API decorator example usage."""
442
443
            @rest("some_route/hello", methods=["POST"])
444 1
            async def hello(self, _request: Request) -> JSONResponse:
445 1
                """Hello endpoint."""
446
                await asyncio.sleep(0)
447
                return JSONResponse({"hello": "world"})
448 1
449 1
            @rest("some_route/{some_arg}", methods=["POST"])
450
            async def ahello_arg(self, request: Request) -> JSONResponse:
451 1
                """Hello endpoint."""
452 1
                arg = request.path_params["some_arg"]
453 1
                await asyncio.sleep(0)
454
                return JSONResponse({"hello": arg})
455
456 1
        napp = MyNApp()
457 1
        controller.api_server.register_napp_endpoints(napp)
458
        coros = [
459 1
            api_client.post("test/MyNApp/some_route/abc"),
460
            api_client.post("test/MyNApp/some_route/hello")
461
        ]
462 1
        responses = await asyncio.gather(*coros)
463 1
        assert responses[0].status_code == 200
464 1
        assert responses[1].status_code == 200
465 1
        assert responses[0].json() == {"hello": "abc"}
466
        assert responses[1].json() == {"hello": "world"}
467 1
468 1
    async def test_remove_napp_endpoints(self, controller, api_client):
469
        """Test remove napp endpoints."""
470 1
        class MyNApp(RESTNApp):  # pylint: disable=too-few-public-methods
471 1
            """API decorator example usage."""
472 1
473 1
            @rest("some_route/", methods=["POST"])
474 1
            def some_endpoint(self, _request: Request) -> JSONResponse:
475
                """Some endpoint."""
476 1
                return JSONResponse({})
477 1
478
        napp = MyNApp()
479 1
        controller.api_server.register_napp_endpoints(napp)
480
        resp = await api_client.post("test/MyNApp/some_route/")
481
        assert resp.status_code == 200
482 1
483 1
        controller.api_server.remove_napp_endpoints(napp)
484
        resp = await api_client.post("test/MyNApp/some_route/")
485 1
        assert resp.status_code == 404
486
487 1
    async def test_route_from_classmethod(self, controller, api_client):
488
        """Test route from classmethod."""
489 1
        class MyNApp(RESTNApp):  # pylint: disable=too-few-public-methods
490 1
            """API decorator example usage."""
491 1
492
            @rest("some_route/", methods=["POST"])
493 1
            @classmethod
494
            def some_endpoint(cls, _request: Request) -> JSONResponse:
495
                """Some endpoint."""
496 1
                return JSONResponse({})
497 1
498
        napp = MyNApp()
499
        controller.api_server.register_napp_endpoints(napp)
500 1
        resp = await api_client.post("test/MyNApp/some_route/")
501 1
        assert resp.status_code == 200
502 1
503
    async def test_route_from_staticmethod(self, controller, api_client):
504 1
        """Test route from staticmethod."""
505 1
        class MyNApp(RESTNApp):  # pylint: disable=too-few-public-methods
506 1
            """API decorator example usage."""
507
508 1
            @rest("some_route/", methods=["POST"])
509
            @staticmethod
510
            def some_endpoint(_request: Request) -> JSONResponse:
511 1
                """Some endpoint."""
512 1
                return JSONResponse({})
513 1
514
        napp = MyNApp()
515
        controller.api_server.register_napp_endpoints(napp)
516 1
        resp = await api_client.post("test/MyNApp/some_route/")
517
        assert resp.status_code == 200
518