Skip to content

Commit a521675

Browse files
fixed tests
1 parent 49b096a commit a521675

11 files changed

+356
-335
lines changed

conftest.py

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pytest_plugins = [
2+
'jupyter_server.pytest_plugin'
3+
]

cylc/uiserver/app.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@
1919
from typing import List
2020

2121
from pkg_resources import parse_version
22-
from tornado import web, ioloop
22+
from tornado import ioloop
2323
from tornado.web import RedirectHandler
2424
from traitlets import (
25+
Dict,
2526
Float,
2627
TraitError,
2728
TraitType,
@@ -356,13 +357,13 @@ def launch_instance(cls, argv=None, **kwargs):
356357
argv = sys.argv[2:]
357358
super().launch_instance(argv=argv, **kwargs)
358359

359-
def stop_extension(self):
360+
async def stop_extension(self):
360361
for sub in self.data_store_mgr.w_subs.values():
361362
sub.stop()
362363
# Shutdown the thread pool executor
363364
for executor in self.data_store_mgr.executors.values():
364365
executor.shutdown(wait=False)
365366
# Destroy ZeroMQ context of all sockets
366367
self.workflows_mgr.context.destroy()
367-
ioloop.IOLoop.instance().stop()
368-
self.log.info('exit success')
368+
#ioloop.IOLoop.instance().stop()
369+
#self.log.info('exit success')

cylc/uiserver/tests/conftest.py

+25-25
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,14 @@
1717
import asyncio
1818
from getpass import getuser
1919
import inspect
20+
import logging
2021
from pathlib import Path
2122
from shutil import rmtree
2223
from socket import gethostname
2324
from tempfile import TemporaryDirectory
24-
from textwrap import dedent
2525

2626
import pytest
27+
from traitlets.config import Config
2728
import zmq
2829

2930
from cylc.flow.data_messages_pb2 import ( # type: ignore
@@ -34,7 +35,6 @@
3435
from cylc.flow.network import ZMQSocketBase
3536

3637
from cylc.uiserver.data_store_mgr import DataStoreMgr
37-
from cylc.uiserver.app import CylcUIServer
3838
from cylc.uiserver.workflows_mgr import WorkflowsManager
3939

4040

@@ -50,9 +50,11 @@ def will_return(self, returns):
5050
self.returns = returns
5151

5252
async def async_request(self, command, args=None, timeout=None):
53-
if inspect.isclass(self.returns) and \
54-
issubclass(self.returns, Exception):
55-
raise self.returns
53+
if (
54+
inspect.isclass(self.returns)
55+
and issubclass(self.returns, Exception)
56+
):
57+
raise self.returns('x')
5658
return self.returns
5759

5860
def stop(self, *args, **kwargs):
@@ -79,17 +81,15 @@ def event_loop():
7981

8082
@pytest.fixture
8183
def workflows_manager() -> WorkflowsManager:
82-
return WorkflowsManager(None)
84+
return WorkflowsManager(None, logging.getLogger('cylc'))
8385

8486

8587
@pytest.fixture
8688
def data_store_mgr(workflows_manager: WorkflowsManager) -> DataStoreMgr:
87-
return DataStoreMgr(workflows_mgr=workflows_manager)
88-
89-
90-
@pytest.fixture
91-
def uiserver() -> CylcUIServer:
92-
return CylcUIServer(0, '/mock', '')
89+
return DataStoreMgr(
90+
workflows_mgr=workflows_manager,
91+
log=logging.getLogger('cylc')
92+
)
9393

9494

9595
@pytest.fixture
@@ -152,33 +152,33 @@ def ui_build_dir(mod_tmp_path):
152152

153153
@pytest.fixture
154154
def mock_config(monkeypatch):
155-
"""Mock the UIServer/Hub configuration file.
155+
"""Mock the UIServer/Hub configuration.
156156
157157
This fixture auto-loads by setting a blank config.
158158
159159
Call the fixture with config code to override.
160160
161-
mock_config('''
162-
c.UIServer.my_config = 'my_value'
163-
''')
161+
mock_config(
162+
CylcUIServer={
163+
'trait': 42
164+
}
165+
)
164166
165167
Can be called multiple times.
166168
167-
Note the code you provide is exec'ed just like the real config file.
168-
169169
"""
170-
conf = ''
170+
conf = {}
171171

172-
def _write(string=''):
172+
def _write(**kwargs):
173173
nonlocal conf
174-
conf = dedent(string)
174+
conf = kwargs
175175

176-
def _read(obj, _):
176+
def _read(self):
177177
nonlocal conf
178-
exec(conf, {'c': obj.config})
178+
self.config = Config(conf)
179179

180180
monkeypatch.setattr(
181-
'cylc.uiserver.app.CylcUIServer.load_config_file',
181+
'cylc.uiserver.app.CylcUIServer.initialize_settings',
182182
_read
183183
)
184184

@@ -205,7 +205,7 @@ def _mock_authentication(user=None, server=None, none=False):
205205
if none:
206206
ret = None
207207
monkeypatch.setattr(
208-
'cylc.uiserver.handlers.BaseHandler.get_current_user',
208+
'cylc.uiserver.handlers.CylcAppHandler.get_current_user',
209209
lambda x: ret
210210
)
211211

cylc/uiserver/tests/test_auth.py

+198
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
#!/usr/bin/env python3
2+
# Copyright (C) NIWA & British Crown (Met Office) & Contributors.
3+
#
4+
# This program is free software: you can redistribute it and/or modify
5+
# it under the terms of the GNU General Public License as published by
6+
# the Free Software Foundation, either version 3 of the License, or
7+
# (at your option) any later version.
8+
#
9+
# This program is distributed in the hope that it will be useful,
10+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
# GNU General Public License for more details.
13+
#
14+
# You should have received a copy of the GNU General Public License
15+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
"""Test authorisation and authentication HTTP response codes."""
17+
18+
from functools import partial
19+
20+
import pytest
21+
22+
from tornado.httpclient import HTTPClientError
23+
24+
25+
@pytest.fixture
26+
def jp_server_config(jp_template_dir):
27+
"""Config to turn the CylcUIServer extension on."""
28+
config = {
29+
"ServerApp": {
30+
"jpserver_extensions": {
31+
'cylc.uiserver': True
32+
},
33+
}
34+
}
35+
return config
36+
37+
38+
@pytest.fixture
39+
def patch_conf_files(monkeypatch):
40+
"""Auto-patches the CylcUIServer to prevent it loading config files."""
41+
monkeypatch.setattr(
42+
'cylc.uiserver.app.CylcUIServer.config_file_paths', []
43+
)
44+
yield
45+
46+
47+
@pytest.mark.integration
48+
async def test_jp_handler(patch_conf_files, jp_fetch):
49+
resp = await jp_fetch(
50+
'/', method='GET'
51+
)
52+
assert resp.code == 200
53+
54+
55+
@pytest.mark.integration
56+
async def test_cylc_handler(patch_conf_files, jp_fetch):
57+
resp = await jp_fetch(
58+
'cylc', 'userprofile', method='GET'
59+
)
60+
assert resp.code == 200
61+
62+
63+
@pytest.mark.integration
64+
@pytest.mark.usefixtures("mock_authentication")
65+
@pytest.mark.parametrize(
66+
'endpoint,code,message,body',
67+
[
68+
pytest.param(
69+
('cylc', 'graphql'),
70+
400,
71+
None,
72+
b'Must provide query string',
73+
id='cylc/graphql',
74+
),
75+
pytest.param(
76+
('cylc', 'subscriptions'),
77+
400,
78+
None,
79+
b'WebSocket',
80+
id='cylc/subscriptions',
81+
),
82+
pytest.param(
83+
('cylc', 'userprofile'),
84+
200,
85+
None,
86+
b'"name":',
87+
id='cylc/userprofile',
88+
)
89+
]
90+
)
91+
async def test_authorised_and_authenticated(
92+
patch_conf_files,
93+
jp_fetch,
94+
endpoint,
95+
code,
96+
message,
97+
body
98+
):
99+
await _test(jp_fetch, endpoint, code, message, body)
100+
101+
102+
@pytest.mark.integration
103+
@pytest.mark.usefixtures("mock_authentication_none")
104+
@pytest.mark.parametrize(
105+
'endpoint,code,message,body',
106+
[
107+
# pytest.param(
108+
# ('cylc', 'graphql'),
109+
# 403,
110+
# 'Forbidden',
111+
# None,
112+
# id='cylc/graphql',
113+
# ),
114+
pytest.param(
115+
('cylc', 'subscriptions'),
116+
403,
117+
'Forbidden',
118+
None,
119+
id='cylc/subscriptions',
120+
),
121+
# pytest.param(
122+
# ('cylc', 'userprofile'),
123+
# 403,
124+
# 'Forbidden',
125+
# None,
126+
# id='cylc/userprofile',
127+
# )
128+
]
129+
)
130+
async def test_unauthenticated(
131+
patch_conf_files,
132+
jp_fetch,
133+
endpoint,
134+
code,
135+
message,
136+
body
137+
):
138+
await _test(jp_fetch, endpoint, code, message, body)
139+
140+
141+
@pytest.mark.integration
142+
@pytest.mark.usefixtures("mock_authentication_yossarian")
143+
@pytest.mark.parametrize(
144+
'endpoint,code,message,body',
145+
[
146+
# pytest.param(
147+
# ('cylc', 'graphql'),
148+
# 403,
149+
# 'authorisation insufficient',
150+
# None,
151+
# id='cylc/graphql',
152+
# ),
153+
# pytest.param(
154+
# ('cylc', 'subscriptions'),
155+
# 403,
156+
# 'authorisation insufficient',
157+
# None,
158+
# id='cylc/subscriptions',
159+
# ),
160+
# pytest.param(
161+
# ('cylc', 'userprofile'),
162+
# 403,
163+
# 'authorisation insufficient',
164+
# None,
165+
# id='cylc/userprofile',
166+
# )
167+
]
168+
)
169+
async def test_unauthorised(
170+
patch_conf_files,
171+
jp_fetch,
172+
endpoint,
173+
code,
174+
message,
175+
body
176+
):
177+
await _test(jp_fetch, endpoint, code, message, body)
178+
179+
180+
async def _test(jp_fetch, endpoint, code, message, body):
181+
"""Test 400 HTTP response (upgrade to websocket)."""
182+
fetch = partial(jp_fetch, *endpoint)
183+
if code != 200:
184+
with pytest.raises(HTTPClientError) as exc_ctx:
185+
await fetch()
186+
exc = exc_ctx.value
187+
assert exc.code == code
188+
if message:
189+
assert exc.message == message
190+
if body:
191+
assert body in exc.response.body
192+
else:
193+
response = await fetch()
194+
assert code == response.code
195+
if message:
196+
assert response.reason == message
197+
if body:
198+
assert body in response.body

cylc/uiserver/tests/test_config.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@
1919
import pytest
2020

2121
from cylc.uiserver import __file__ as UIS_PKG
22-
from cylc.uiserver.config import load
22+
from cylc.uiserver.jupyterhub_config import load
2323

2424

25-
SYS_CONF = Path(UIS_PKG).parent / 'jupyterhub_config.py'
26-
USER_CONF = Path('~/.cylc/hub/config.py').expanduser()
27-
SITE_CONF = Path('/etc/cylc/hub/config.py')
25+
SYS_CONF = Path(UIS_PKG).parent / 'jupyter_config.py'
26+
USER_CONF = Path('~/.cylc/hub/jupyter_config.py').expanduser()
27+
SITE_CONF = Path('/etc/cylc/hub/jupyter_config.py')
2828

2929

3030
@pytest.fixture
@@ -42,7 +42,7 @@ def capload(monkeypatch):
4242
empty config files.
4343
"""
4444
files = []
45-
monkeypatch.setattr('cylc.uiserver.config._load', files.append)
45+
monkeypatch.setattr('cylc.uiserver.jupyterhub_config._load', files.append)
4646
return files
4747

4848

0 commit comments

Comments
 (0)