update tests for pytest-asyncio

- remove gen_test marker
- use async def
- find/replace yield->await approximately one million times
This commit is contained in:
Min RK
2018-12-12 13:05:45 +01:00
parent d5f87fe09f
commit 37cdba370f
16 changed files with 666 additions and 828 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -63,8 +63,7 @@ def test_generate_config():
assert 'Authenticator.whitelist' in cfg_text
@pytest.mark.gen_test
def test_init_tokens(request):
async def test_init_tokens(request):
with TemporaryDirectory() as td:
db_file = os.path.join(td, 'jupyterhub.sqlite')
tokens = {
@@ -77,7 +76,7 @@ def test_init_tokens(request):
if ssl_enabled:
kwargs['internal_certs_location'] = td
app = MockHub(**kwargs)
yield app.initialize([])
await app.initialize([])
db = app.db
for token, username in tokens.items():
api_token = orm.APIToken.find(db, token)
@@ -87,7 +86,7 @@ def test_init_tokens(request):
# simulate second startup, reloading same tokens:
app = MockHub(**kwargs)
yield app.initialize([])
await app.initialize([])
db = app.db
for token, username in tokens.items():
api_token = orm.APIToken.find(db, token)
@@ -99,7 +98,7 @@ def test_init_tokens(request):
tokens['short'] = 'gman'
app = MockHub(**kwargs)
with pytest.raises(ValueError):
yield app.initialize([])
await app.initialize([])
assert orm.User.find(app.db, 'gman') is None
@@ -169,8 +168,7 @@ def test_cookie_secret_env(tmpdir, request):
assert not os.path.exists(hub.cookie_secret_file)
@pytest.mark.gen_test
def test_load_groups(tmpdir, request):
async def test_load_groups(tmpdir, request):
to_load = {
'blue': ['cyclops', 'rogue', 'wolverine'],
'gold': ['storm', 'jean-grey', 'colossus'],
@@ -181,8 +179,8 @@ def test_load_groups(tmpdir, request):
kwargs['internal_certs_location'] = str(tmpdir)
hub = MockHub(**kwargs)
hub.init_db()
yield hub.init_users()
yield hub.init_groups()
await hub.init_users()
await hub.init_groups()
db = hub.db
blue = orm.Group.find(db, name='blue')
assert blue is not None
@@ -192,16 +190,15 @@ def test_load_groups(tmpdir, request):
assert sorted([ u.name for u in gold.users ]) == sorted(to_load['gold'])
@pytest.mark.gen_test
def test_resume_spawners(tmpdir, request):
async def test_resume_spawners(tmpdir, request):
if not os.getenv('JUPYTERHUB_TEST_DB_URL'):
p = patch.dict(os.environ, {
'JUPYTERHUB_TEST_DB_URL': 'sqlite:///%s' % tmpdir.join('jupyterhub.sqlite'),
})
p.start()
request.addfinalizer(p.stop)
@gen.coroutine
def new_hub():
async def new_hub():
kwargs = {}
ssl_enabled = getattr(request.module, "ssl_enabled", False)
if ssl_enabled:
@@ -209,26 +206,27 @@ def test_resume_spawners(tmpdir, request):
app = MockHub(test_clean_db=False, **kwargs)
app.config.ConfigurableHTTPProxy.should_start = False
app.config.ConfigurableHTTPProxy.auth_token = 'unused'
yield app.initialize([])
await app.initialize([])
return app
app = yield new_hub()
app = await new_hub()
db = app.db
# spawn a user's server
name = 'kurt'
user = add_user(db, app, name=name)
yield user.spawn()
await user.spawn()
proc = user.spawner.proc
assert proc is not None
# stop the Hub without cleaning up servers
app.cleanup_servers = False
yield app.stop()
app.stop()
# proc is still running
assert proc.poll() is None
# resume Hub, should still be running
app = yield new_hub()
app = await new_hub()
db = app.db
user = app.users[name]
assert user.running
@@ -236,7 +234,7 @@ def test_resume_spawners(tmpdir, request):
# stop the Hub without cleaning up servers
app.cleanup_servers = False
yield app.stop()
app.stop()
# stop the server while the Hub is down. BAMF!
proc.terminate()
@@ -244,7 +242,7 @@ def test_resume_spawners(tmpdir, request):
assert proc.poll() is not None
# resume Hub, should be stopped
app = yield new_hub()
app = await new_hub()
db = app.db
user = app.users[name]
assert not user.running

View File

@@ -14,47 +14,44 @@ from jupyterhub import auth, crypto, orm
from .mocking import MockPAMAuthenticator, MockStructGroup, MockStructPasswd
from .test_api import add_user
@pytest.mark.gen_test
def test_pam_auth():
async def test_pam_auth():
authenticator = MockPAMAuthenticator()
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'match',
'password': 'match',
})
assert authorized['name'] == 'match'
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'match',
'password': 'nomatch',
})
assert authorized is None
# Account check is on by default for increased security
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'notallowedmatch',
'password': 'notallowedmatch',
})
assert authorized is None
@pytest.mark.gen_test
def test_pam_auth_account_check_disabled():
async def test_pam_auth_account_check_disabled():
authenticator = MockPAMAuthenticator(check_account=False)
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'allowedmatch',
'password': 'allowedmatch',
})
assert authorized['name'] == 'allowedmatch'
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'notallowedmatch',
'password': 'notallowedmatch',
})
assert authorized['name'] == 'notallowedmatch'
@pytest.mark.gen_test
def test_pam_auth_admin_groups():
async def test_pam_auth_admin_groups():
jh_users = MockStructGroup('jh_users', ['group_admin', 'also_group_admin', 'override_admin', 'non_admin'], 1234)
jh_admins = MockStructGroup('jh_admins', ['group_admin'], 5678)
wheel = MockStructGroup('wheel', ['also_group_admin'], 9999)
@@ -90,7 +87,7 @@ def test_pam_auth_admin_groups():
_getgrnam=getgrnam,
_getpwnam=getpwnam,
_getgrouplist=getgrouplist):
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'group_admin',
'password': 'group_admin'
})
@@ -102,7 +99,7 @@ def test_pam_auth_admin_groups():
_getgrnam=getgrnam,
_getpwnam=getpwnam,
_getgrouplist=getgrouplist):
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'also_group_admin',
'password': 'also_group_admin'
})
@@ -114,7 +111,7 @@ def test_pam_auth_admin_groups():
_getgrnam=getgrnam,
_getpwnam=getpwnam,
_getgrouplist=getgrouplist):
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'override_admin',
'password': 'override_admin'
})
@@ -126,7 +123,7 @@ def test_pam_auth_admin_groups():
_getgrnam=getgrnam,
_getpwnam=getpwnam,
_getgrouplist=getgrouplist):
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'non_admin',
'password': 'non_admin'
})
@@ -134,55 +131,52 @@ def test_pam_auth_admin_groups():
assert authorized['admin'] is False
@pytest.mark.gen_test
def test_pam_auth_whitelist():
async def test_pam_auth_whitelist():
authenticator = MockPAMAuthenticator(whitelist={'wash', 'kaylee'})
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'kaylee',
'password': 'kaylee',
})
assert authorized['name'] == 'kaylee'
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'wash',
'password': 'nomatch',
})
assert authorized is None
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'mal',
'password': 'mal',
})
assert authorized is None
@pytest.mark.gen_test
def test_pam_auth_group_whitelist():
async def test_pam_auth_group_whitelist():
def getgrnam(name):
return MockStructGroup('grp', ['kaylee'])
authenticator = MockPAMAuthenticator(group_whitelist={'group'})
with mock.patch.object(authenticator, '_getgrnam', getgrnam):
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'kaylee',
'password': 'kaylee',
})
assert authorized['name'] == 'kaylee'
with mock.patch.object(authenticator, '_getgrnam', getgrnam):
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'mal',
'password': 'mal',
})
assert authorized is None
@pytest.mark.gen_test
def test_pam_auth_blacklist():
async def test_pam_auth_blacklist():
# Null case compared to next case
authenticator = MockPAMAuthenticator()
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'wash',
'password': 'wash',
})
@@ -190,7 +184,7 @@ def test_pam_auth_blacklist():
# Blacklist basics
authenticator = MockPAMAuthenticator(blacklist={'wash'})
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'wash',
'password': 'wash',
})
@@ -198,7 +192,7 @@ def test_pam_auth_blacklist():
# User in both white and blacklists: default deny. Make error someday?
authenticator = MockPAMAuthenticator(blacklist={'wash'}, whitelist={'wash', 'kaylee'})
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'wash',
'password': 'wash',
})
@@ -206,7 +200,7 @@ def test_pam_auth_blacklist():
# User not in blacklist can log in
authenticator = MockPAMAuthenticator(blacklist={'wash'}, whitelist={'wash', 'kaylee'})
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'kaylee',
'password': 'kaylee',
})
@@ -214,7 +208,7 @@ def test_pam_auth_blacklist():
# User in whitelist, blacklist irrelevent
authenticator = MockPAMAuthenticator(blacklist={'mal'}, whitelist={'wash', 'kaylee'})
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'wash',
'password': 'wash',
})
@@ -222,7 +216,7 @@ def test_pam_auth_blacklist():
# User in neither list
authenticator = MockPAMAuthenticator(blacklist={'mal'}, whitelist={'wash', 'kaylee'})
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'simon',
'password': 'simon',
})
@@ -230,34 +224,31 @@ def test_pam_auth_blacklist():
# blacklist == {}
authenticator = MockPAMAuthenticator(blacklist=set(), whitelist={'wash', 'kaylee'})
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'kaylee',
'password': 'kaylee',
})
assert authorized['name'] == 'kaylee'
@pytest.mark.gen_test
def test_pam_auth_no_such_group():
async def test_pam_auth_no_such_group():
authenticator = MockPAMAuthenticator(group_whitelist={'nosuchcrazygroup'})
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'kaylee',
'password': 'kaylee',
})
assert authorized is None
@pytest.mark.gen_test
def test_wont_add_system_user():
async def test_wont_add_system_user():
user = orm.User(name='lioness4321')
authenticator = auth.PAMAuthenticator(whitelist={'mal'})
authenticator.create_system_users = False
with pytest.raises(KeyError):
yield authenticator.add_user(user)
await authenticator.add_user(user)
@pytest.mark.gen_test
def test_cant_add_system_user():
async def test_cant_add_system_user():
user = orm.User(name='lioness4321')
authenticator = auth.PAMAuthenticator(whitelist={'mal'})
authenticator.add_user_cmd = ['jupyterhub-fake-command']
@@ -279,12 +270,11 @@ def test_cant_add_system_user():
with mock.patch.object(auth, 'Popen', DummyPopen):
with pytest.raises(RuntimeError) as exc:
yield authenticator.add_user(user)
await authenticator.add_user(user)
assert str(exc.value) == 'Failed to create system user lioness4321: dummy error'
@pytest.mark.gen_test
def test_add_system_user():
async def test_add_system_user():
user = orm.User(name='lioness4321')
authenticator = auth.PAMAuthenticator(whitelist={'mal'})
authenticator.create_system_users = True
@@ -300,17 +290,16 @@ def test_add_system_user():
return
with mock.patch.object(auth, 'Popen', DummyPopen):
yield authenticator.add_user(user)
await authenticator.add_user(user)
assert record['cmd'] == ['echo', '/home/lioness4321', 'lioness4321']
@pytest.mark.gen_test
def test_delete_user():
async def test_delete_user():
user = orm.User(name='zoe')
a = MockPAMAuthenticator(whitelist={'mal'})
assert 'zoe' not in a.whitelist
yield a.add_user(user)
await a.add_user(user)
assert 'zoe' in a.whitelist
a.delete_user(user)
assert 'zoe' not in a.whitelist
@@ -330,47 +319,43 @@ def test_handlers(app):
assert handlers[0][0] == '/login'
@pytest.mark.gen_test
def test_auth_state(app, auth_state_enabled):
async def test_auth_state(app, auth_state_enabled):
"""auth_state enabled and available"""
name = 'kiwi'
user = add_user(app.db, app, name=name)
assert user.encrypted_auth_state is None
cookies = yield app.login_user(name)
auth_state = yield user.get_auth_state()
cookies = await app.login_user(name)
auth_state = await user.get_auth_state()
assert auth_state == app.authenticator.auth_state
@pytest.mark.gen_test
def test_auth_admin_non_admin(app):
async def test_auth_admin_non_admin(app):
"""admin should be passed through for non-admin users"""
name = 'kiwi'
user = add_user(app.db, app, name=name, admin=False)
assert user.admin is False
cookies = yield app.login_user(name)
cookies = await app.login_user(name)
assert user.admin is False
@pytest.mark.gen_test
def test_auth_admin_is_admin(app):
async def test_auth_admin_is_admin(app):
"""admin should be passed through for admin users"""
# Admin user defined in MockPAMAuthenticator.
name = 'admin'
user = add_user(app.db, app, name=name, admin=False)
assert user.admin is False
cookies = yield app.login_user(name)
cookies = await app.login_user(name)
assert user.admin is True
@pytest.mark.gen_test
def test_auth_admin_retained_if_unset(app):
async def test_auth_admin_retained_if_unset(app):
"""admin should be unchanged if authenticator doesn't return admin value"""
name = 'kiwi'
# Add user as admin.
user = add_user(app.db, app, name=name, admin=True)
assert user.admin is True
# User should remain unchanged.
cookies = yield app.login_user(name)
cookies = await app.login_user(name)
assert user.admin is True
@@ -384,56 +369,53 @@ def auth_state_unavailable(auth_state_enabled):
yield
@pytest.mark.gen_test
def test_auth_state_disabled(app, auth_state_unavailable):
async def test_auth_state_disabled(app, auth_state_unavailable):
name = 'driebus'
user = add_user(app.db, app, name=name)
assert user.encrypted_auth_state is None
with pytest.raises(HTTPError):
cookies = yield app.login_user(name)
auth_state = yield user.get_auth_state()
cookies = await app.login_user(name)
auth_state = await user.get_auth_state()
assert auth_state is None
@pytest.mark.gen_test
def test_normalize_names():
async def test_normalize_names():
a = MockPAMAuthenticator()
authorized = yield a.get_authenticated_user(None, {
authorized = await a.get_authenticated_user(None, {
'username': 'ZOE',
'password': 'ZOE',
})
assert authorized['name'] == 'zoe'
authorized = yield a.get_authenticated_user(None, {
authorized = await a.get_authenticated_user(None, {
'username': 'Glenn',
'password': 'Glenn',
})
assert authorized['name'] == 'glenn'
authorized = yield a.get_authenticated_user(None, {
authorized = await a.get_authenticated_user(None, {
'username': 'hExi',
'password': 'hExi',
})
assert authorized['name'] == 'hexi'
authorized = yield a.get_authenticated_user(None, {
authorized = await a.get_authenticated_user(None, {
'username': 'Test',
'password': 'Test',
})
assert authorized['name'] == 'test'
@pytest.mark.gen_test
def test_username_map():
async def test_username_map():
a = MockPAMAuthenticator(username_map={'wash': 'alpha'})
authorized = yield a.get_authenticated_user(None, {
authorized = await a.get_authenticated_user(None, {
'username': 'WASH',
'password': 'WASH',
})
assert authorized['name'] == 'alpha'
authorized = yield a.get_authenticated_user(None, {
authorized = await a.get_authenticated_user(None, {
'username': 'Inara',
'password': 'Inara',
})

View File

@@ -11,6 +11,7 @@ keys = [('%i' % i).encode('ascii') * 32 for i in range(3)]
hex_keys = [ b2a_hex(key).decode('ascii') for key in keys ]
b64_keys = [ b2a_base64(key).decode('ascii').strip() for key in keys ]
@pytest.mark.parametrize("key_env, keys", [
(hex_keys[0], [keys[0]]),
(';'.join([b64_keys[0], hex_keys[1]]), keys[:2]),
@@ -52,30 +53,27 @@ def crypt_keeper():
ck.keys = save_keys
@pytest.mark.gen_test
def test_roundtrip(crypt_keeper):
async def test_roundtrip(crypt_keeper):
data = {'key': 'value'}
encrypted = yield encrypt(data)
decrypted = yield decrypt(encrypted)
encrypted = await encrypt(data)
decrypted = await decrypt(encrypted)
assert decrypted == data
@pytest.mark.gen_test
def test_missing_crypto(crypt_keeper):
async def test_missing_crypto(crypt_keeper):
with patch.object(crypto, 'cryptography', None):
with pytest.raises(crypto.CryptographyUnavailable):
yield encrypt({})
await encrypt({})
with pytest.raises(crypto.CryptographyUnavailable):
yield decrypt(b'whatever')
await decrypt(b'whatever')
@pytest.mark.gen_test
def test_missing_keys(crypt_keeper):
async def test_missing_keys(crypt_keeper):
crypt_keeper.keys = []
with pytest.raises(crypto.NoEncryptionKeys):
yield encrypt({})
await encrypt({})
with pytest.raises(crypto.NoEncryptionKeys):
yield decrypt(b'whatever')
await decrypt(b'whatever')

View File

@@ -40,8 +40,7 @@ def generate_old_db(env_dir, hub_version, db_url):
'0.8.1',
],
)
@pytest.mark.gen_test
def test_upgrade(tmpdir, hub_version):
async def test_upgrade(tmpdir, hub_version):
db_url = os.getenv('JUPYTERHUB_TEST_DB_URL')
if db_url:
db_url += '_upgrade_' + hub_version.replace('.', '')
@@ -69,7 +68,7 @@ def test_upgrade(tmpdir, hub_version):
assert len(sqlite_files) == 1
upgradeapp = UpgradeDB(config=cfg)
yield upgradeapp.initialize([])
upgradeapp.initialize([])
upgradeapp.start()
# check that backup was created:

View File

@@ -7,48 +7,45 @@ import pytest
from jupyterhub.auth import DummyAuthenticator
@pytest.mark.gen_test
def test_dummy_auth_without_global_password():
async def test_dummy_auth_without_global_password():
authenticator = DummyAuthenticator()
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'test_user',
'password': 'test_pass',
})
assert authorized['name'] == 'test_user'
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'test_user',
'password': '',
})
assert authorized['name'] == 'test_user'
@pytest.mark.gen_test
def test_dummy_auth_without_username():
async def test_dummy_auth_without_username():
authenticator = DummyAuthenticator()
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': '',
'password': 'test_pass',
})
assert authorized is None
@pytest.mark.gen_test
def test_dummy_auth_with_global_password():
async def test_dummy_auth_with_global_password():
authenticator = DummyAuthenticator()
authenticator.password = "test_password"
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'test_user',
'password': 'test_password',
})
assert authorized['name'] == 'test_user'
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'test_user',
'password': 'qwerty',
})
assert authorized is None
authorized = yield authenticator.get_authenticated_user(None, {
authorized = await authenticator.get_authenticated_user(None, {
'username': 'some_other_user',
'password': 'test_password',
})

View File

@@ -39,39 +39,36 @@ def wait_for_spawner(spawner, timeout=10):
yield wait()
@pytest.mark.gen_test
def test_connection_hub_wrong_certs(app):
async def test_connection_hub_wrong_certs(app):
"""Connecting to the internal hub url fails without correct certs"""
with pytest.raises(SSLError):
kwargs = {'verify': False}
r = yield async_requests.get(app.hub.url, **kwargs)
r = await async_requests.get(app.hub.url, **kwargs)
r.raise_for_status()
@pytest.mark.gen_test
def test_connection_proxy_api_wrong_certs(app):
async def test_connection_proxy_api_wrong_certs(app):
"""Connecting to the proxy api fails without correct certs"""
with pytest.raises(SSLError):
kwargs = {'verify': False}
r = yield async_requests.get(app.proxy.api_url, **kwargs)
r = await async_requests.get(app.proxy.api_url, **kwargs)
r.raise_for_status()
@pytest.mark.gen_test
def test_connection_notebook_wrong_certs(app):
async def test_connection_notebook_wrong_certs(app):
"""Connecting to a notebook fails without correct certs"""
with mock.patch.dict(
app.config.LocalProcessSpawner,
{'cmd': [sys.executable, '-m', 'jupyterhub.tests.mocksu']}
):
user = add_user(app.db, app, name='foo')
yield user.spawn()
yield wait_for_spawner(user.spawner)
await user.spawn()
await wait_for_spawner(user.spawner)
spawner = user.spawner
status = yield spawner.poll()
status = await spawner.poll()
assert status is None
with pytest.raises(SSLError):
kwargs = {'verify': False}
r = yield async_requests.get(spawner.server.url, **kwargs)
r = await async_requests.get(spawner.server.url, **kwargs)
r.raise_for_status()

View File

@@ -17,16 +17,15 @@ def named_servers(app):
yield
@pytest.mark.gen_test
def test_default_server(app, named_servers):
async def test_default_server(app, named_servers):
"""Test the default /users/:user/server handler when named servers are enabled"""
username = 'rosie'
user = add_user(app.db, app, name=username)
r = yield api_request(app, 'users', username, 'server', method='post')
r = await api_request(app, 'users', username, 'server', method='post')
assert r.status_code == 201
assert r.text == ''
r = yield api_request(app, 'users', username)
r = await api_request(app, 'users', username)
r.raise_for_status()
user_model = normalize_user(r.json())
@@ -51,11 +50,11 @@ def test_default_server(app, named_servers):
# now stop the server
r = yield api_request(app, 'users', username, 'server', method='delete')
r = await api_request(app, 'users', username, 'server', method='delete')
assert r.status_code == 204
assert r.text == ''
r = yield api_request(app, 'users', username)
r = await api_request(app, 'users', username)
r.raise_for_status()
user_model = normalize_user(r.json())
@@ -66,20 +65,19 @@ def test_default_server(app, named_servers):
})
@pytest.mark.gen_test
def test_create_named_server(app, named_servers):
async def test_create_named_server(app, named_servers):
username = 'walnut'
user = add_user(app.db, app, name=username)
# assert user.allow_named_servers == True
cookies = yield app.login_user(username)
cookies = await app.login_user(username)
servername = 'trevor'
r = yield api_request(app, 'users', username, 'servers', servername, method='post')
r = await api_request(app, 'users', username, 'servers', servername, method='post')
r.raise_for_status()
assert r.status_code == 201
assert r.text == ''
url = url_path_join(public_url(app, user), servername, 'env')
r = yield async_requests.get(url, cookies=cookies)
r = await async_requests.get(url, cookies=cookies)
r.raise_for_status()
assert r.url == url
env = r.json()
@@ -87,7 +85,7 @@ def test_create_named_server(app, named_servers):
assert prefix == user.spawners[servername].server.base_url
assert prefix.endswith('/user/%s/%s/' % (username, servername))
r = yield api_request(app, 'users', username)
r = await api_request(app, 'users', username)
r.raise_for_status()
user_model = normalize_user(r.json())
@@ -111,22 +109,21 @@ def test_create_named_server(app, named_servers):
})
@pytest.mark.gen_test
def test_delete_named_server(app, named_servers):
async def test_delete_named_server(app, named_servers):
username = 'donaar'
user = add_user(app.db, app, name=username)
assert user.allow_named_servers
cookies = app.login_user(username)
servername = 'splugoth'
r = yield api_request(app, 'users', username, 'servers', servername, method='post')
r = await api_request(app, 'users', username, 'servers', servername, method='post')
r.raise_for_status()
assert r.status_code == 201
r = yield api_request(app, 'users', username, 'servers', servername, method='delete')
r = await api_request(app, 'users', username, 'servers', servername, method='delete')
r.raise_for_status()
assert r.status_code == 204
r = yield api_request(app, 'users', username)
r = await api_request(app, 'users', username)
r.raise_for_status()
user_model = normalize_user(r.json())
@@ -140,7 +137,7 @@ def test_delete_named_server(app, named_servers):
# low-level record still exists
assert servername in user.orm_spawners
r = yield api_request(
r = await api_request(
app, 'users', username, 'servers', servername,
method='delete',
data=json.dumps({'remove': True}),
@@ -151,11 +148,10 @@ def test_delete_named_server(app, named_servers):
assert servername not in user.orm_spawners
@pytest.mark.gen_test
def test_named_server_disabled(app):
async def test_named_server_disabled(app):
username = 'user'
servername = 'okay'
r = yield api_request(app, 'users', username, 'servers', servername, method='post')
r = await api_request(app, 'users', username, 'servers', servername, method='post')
assert r.status_code == 400
r = yield api_request(app, 'users', username, 'servers', servername, method='delete')
r = await api_request(app, 'users', username, 'servers', servername, method='delete')
assert r.status_code == 400

View File

@@ -205,8 +205,7 @@ def test_token_find(db):
assert found is None
@pytest.mark.gen_test
def test_spawn_fails(db):
async def test_spawn_fails(db):
orm_user = orm.User(name='aeofel')
db.add(orm_user)
db.commit()
@@ -223,7 +222,7 @@ def test_spawn_fails(db):
})
with pytest.raises(RuntimeError) as exc:
yield user.spawn()
await user.spawn()
assert user.spawners[''].server is None
assert not user.running
@@ -246,8 +245,7 @@ def test_groups(db):
assert group.users == []
@pytest.mark.gen_test
def test_auth_state(db):
async def test_auth_state(db):
orm_user = orm.User(name='eve')
db.add(orm_user)
db.commit()
@@ -262,51 +260,51 @@ def test_auth_state(db):
state = {'key': 'value'}
ck.keys = []
with pytest.raises(crypto.EncryptionUnavailable):
yield user.save_auth_state(state)
await user.save_auth_state(state)
assert user.encrypted_auth_state is None
# saving/loading None doesn't require keys
yield user.save_auth_state(None)
current = yield user.get_auth_state()
await user.save_auth_state(None)
current = await user.get_auth_state()
assert current is None
first_key = os.urandom(32)
second_key = os.urandom(32)
ck.keys = [first_key]
yield user.save_auth_state(state)
await user.save_auth_state(state)
assert user.encrypted_auth_state is not None
decrypted_state = yield user.get_auth_state()
decrypted_state = await user.get_auth_state()
assert decrypted_state == state
# can't read auth_state without keys
ck.keys = []
auth_state = yield user.get_auth_state()
auth_state = await user.get_auth_state()
assert auth_state is None
# key rotation works
db.rollback()
ck.keys = [second_key, first_key]
decrypted_state = yield user.get_auth_state()
decrypted_state = await user.get_auth_state()
assert decrypted_state == state
new_state = {'key': 'newvalue'}
yield user.save_auth_state(new_state)
await user.save_auth_state(new_state)
db.commit()
ck.keys = [first_key]
db.rollback()
# can't read anymore with new-key after encrypting with second-key
decrypted_state = yield user.get_auth_state()
decrypted_state = await user.get_auth_state()
assert decrypted_state is None
yield user.save_auth_state(new_state)
decrypted_state = yield user.get_auth_state()
await user.save_auth_state(new_state)
decrypted_state = await user.get_auth_state()
assert decrypted_state == new_state
ck.keys = []
db.rollback()
decrypted_state = yield user.get_auth_state()
decrypted_state = await user.get_auth_state()
assert decrypted_state is None

View File

@@ -30,91 +30,81 @@ def get_page(path, app, hub=True, **kw):
return async_requests.get(ujoin(base_url, path), **kw)
@pytest.mark.gen_test
def test_root_no_auth(app):
async def test_root_no_auth(app):
url = ujoin(public_host(app), app.hub.base_url)
r = yield async_requests.get(url)
r = await async_requests.get(url)
r.raise_for_status()
assert r.url == ujoin(url, 'login')
@pytest.mark.gen_test
def test_root_auth(app):
cookies = yield app.login_user('river')
r = yield async_requests.get(public_url(app), cookies=cookies)
async def test_root_auth(app):
cookies = await app.login_user('river')
r = await async_requests.get(public_url(app), cookies=cookies)
r.raise_for_status()
assert r.url.startswith(public_url(app, app.users['river']))
@pytest.mark.gen_test
def test_root_redirect(app):
async def test_root_redirect(app):
name = 'wash'
cookies = yield app.login_user(name)
cookies = await app.login_user(name)
next_url = ujoin(app.base_url, 'user/other/test.ipynb')
url = '/?' + urlencode({'next': next_url})
r = yield get_page(url, app, cookies=cookies)
r = await get_page(url, app, cookies=cookies)
r.raise_for_status()
path = urlparse(r.url).path
assert path == ujoin(app.base_url, 'user/%s/test.ipynb' % name)
@pytest.mark.gen_test
def test_root_default_url_noauth(app):
async def test_root_default_url_noauth(app):
with mock.patch.dict(app.tornado_settings,
{'default_url': '/foo/bar'}):
r = yield get_page('/', app, allow_redirects=False)
r = await get_page('/', app, allow_redirects=False)
r.raise_for_status()
url = r.headers.get('Location', '')
path = urlparse(url).path
assert path == '/foo/bar'
@pytest.mark.gen_test
def test_root_default_url_auth(app):
async def test_root_default_url_auth(app):
name = 'wash'
cookies = yield app.login_user(name)
cookies = await app.login_user(name)
with mock.patch.dict(app.tornado_settings,
{'default_url': '/foo/bar'}):
r = yield get_page('/', app, cookies=cookies, allow_redirects=False)
r = await get_page('/', app, cookies=cookies, allow_redirects=False)
r.raise_for_status()
url = r.headers.get('Location', '')
path = urlparse(url).path
assert path == '/foo/bar'
@pytest.mark.gen_test
def test_home_no_auth(app):
r = yield get_page('home', app, allow_redirects=False)
async def test_home_no_auth(app):
r = await get_page('home', app, allow_redirects=False)
r.raise_for_status()
assert r.status_code == 302
assert '/hub/login' in r.headers['Location']
@pytest.mark.gen_test
def test_home_auth(app):
cookies = yield app.login_user('river')
r = yield get_page('home', app, cookies=cookies)
async def test_home_auth(app):
cookies = await app.login_user('river')
r = await get_page('home', app, cookies=cookies)
r.raise_for_status()
assert r.url.endswith('home')
@pytest.mark.gen_test
def test_admin_no_auth(app):
r = yield get_page('admin', app)
async def test_admin_no_auth(app):
r = await get_page('admin', app)
assert r.status_code == 403
@pytest.mark.gen_test
def test_admin_not_admin(app):
cookies = yield app.login_user('wash')
r = yield get_page('admin', app, cookies=cookies)
async def test_admin_not_admin(app):
cookies = await app.login_user('wash')
r = await get_page('admin', app, cookies=cookies)
assert r.status_code == 403
@pytest.mark.gen_test
def test_admin(app):
cookies = yield app.login_user('admin')
r = yield get_page('admin', app, cookies=cookies, allow_redirects=False)
async def test_admin(app):
cookies = await app.login_user('admin')
r = await get_page('admin', app, cookies=cookies, allow_redirects=False)
r.raise_for_status()
assert r.url.endswith('/admin')
@@ -125,127 +115,120 @@ def test_admin(app):
'admin',
'name',
])
@pytest.mark.gen_test
def test_admin_sort(app, sort):
cookies = yield app.login_user('admin')
r = yield get_page('admin?sort=%s' % sort, app, cookies=cookies)
async def test_admin_sort(app, sort):
cookies = await app.login_user('admin')
r = await get_page('admin?sort=%s' % sort, app, cookies=cookies)
r.raise_for_status()
assert r.status_code == 200
@pytest.mark.gen_test
def test_spawn_redirect(app):
async def test_spawn_redirect(app):
name = 'wash'
cookies = yield app.login_user(name)
cookies = await app.login_user(name)
u = app.users[orm.User.find(app.db, name)]
status = yield u.spawner.poll()
status = await u.spawner.poll()
assert status is not None
# test spawn page when no server is running
r = yield get_page('spawn', app, cookies=cookies)
r = await get_page('spawn', app, cookies=cookies)
r.raise_for_status()
print(urlparse(r.url))
path = urlparse(r.url).path
assert path == ujoin(app.base_url, 'user/%s/' % name)
# should have started server
status = yield u.spawner.poll()
status = await u.spawner.poll()
assert status is None
# test spawn page when server is already running (just redirect)
r = yield get_page('spawn', app, cookies=cookies)
r = await get_page('spawn', app, cookies=cookies)
r.raise_for_status()
print(urlparse(r.url))
path = urlparse(r.url).path
assert path == ujoin(app.base_url, '/user/%s/' % name)
# stop server to ensure /user/name is handled by the Hub
r = yield api_request(app, 'users', name, 'server', method='delete', cookies=cookies)
r = await api_request(app, 'users', name, 'server', method='delete', cookies=cookies)
r.raise_for_status()
# test handing of trailing slash on `/user/name`
r = yield get_page('user/' + name, app, hub=False, cookies=cookies)
r = await get_page('user/' + name, app, hub=False, cookies=cookies)
r.raise_for_status()
path = urlparse(r.url).path
assert path == ujoin(app.base_url, '/user/%s/' % name)
@pytest.mark.gen_test
def test_spawn_handler_access(app):
async def test_spawn_handler_access(app):
name = 'winston'
cookies = yield app.login_user(name)
cookies = await app.login_user(name)
u = app.users[orm.User.find(app.db, name)]
status = yield u.spawner.poll()
status = await u.spawner.poll()
assert status is not None
# spawn server via browser link with ?arg=value
r = yield get_page('spawn', app, cookies=cookies, params={'arg': 'value'})
r = await get_page('spawn', app, cookies=cookies, params={'arg': 'value'})
r.raise_for_status()
# verify that request params got passed down
# implemented in MockSpawner
r = yield async_requests.get(ujoin(public_url(app, u), 'env'))
r = await async_requests.get(ujoin(public_url(app, u), 'env'))
env = r.json()
assert 'HANDLER_ARGS' in env
assert env['HANDLER_ARGS'] == 'arg=value'
# stop server
r = yield api_request(app, 'users', name, 'server', method='delete')
r = await api_request(app, 'users', name, 'server', method='delete')
r.raise_for_status()
@pytest.mark.gen_test
def test_spawn_admin_access(app, admin_access):
async def test_spawn_admin_access(app, admin_access):
"""GET /user/:name as admin with admin-access spawns user's server"""
cookies = yield app.login_user('admin')
cookies = await app.login_user('admin')
name = 'mariel'
user = add_user(app.db, app=app, name=name)
app.db.commit()
r = yield get_page('user/' + name, app, cookies=cookies)
r = await get_page('user/' + name, app, cookies=cookies)
r.raise_for_status()
assert (r.url.split('?')[0] + '/').startswith(public_url(app, user))
r = yield get_page('user/{}/env'.format(name), app, hub=False, cookies=cookies)
r = await get_page('user/{}/env'.format(name), app, hub=False, cookies=cookies)
r.raise_for_status()
env = r.json()
assert env['JUPYTERHUB_USER'] == name
@pytest.mark.gen_test
def test_spawn_page(app):
async def test_spawn_page(app):
with mock.patch.dict(app.users.settings, {'spawner_class': FormSpawner}):
cookies = yield app.login_user('jones')
r = yield get_page('spawn', app, cookies=cookies)
cookies = await app.login_user('jones')
r = await get_page('spawn', app, cookies=cookies)
assert r.url.endswith('/spawn')
assert FormSpawner.options_form in r.text
r = yield get_page('spawn?next=foo', app, cookies=cookies)
r = await get_page('spawn?next=foo', app, cookies=cookies)
assert r.url.endswith('/spawn?next=foo')
assert FormSpawner.options_form in r.text
@pytest.mark.gen_test
def test_spawn_page_admin(app, admin_access):
async def test_spawn_page_admin(app, admin_access):
with mock.patch.dict(app.users.settings, {'spawner_class': FormSpawner}):
cookies = yield app.login_user('admin')
cookies = await app.login_user('admin')
u = add_user(app.db, app=app, name='melanie')
r = yield get_page('spawn/' + u.name, app, cookies=cookies)
r = await get_page('spawn/' + u.name, app, cookies=cookies)
assert r.url.endswith('/spawn/' + u.name)
assert FormSpawner.options_form in r.text
assert "Spawning server for {}".format(u.name) in r.text
@pytest.mark.gen_test
def test_spawn_form(app):
async def test_spawn_form(app):
with mock.patch.dict(app.users.settings, {'spawner_class': FormSpawner}):
base_url = ujoin(public_host(app), app.hub.base_url)
cookies = yield app.login_user('jones')
cookies = await app.login_user('jones')
orm_u = orm.User.find(app.db, 'jones')
u = app.users[orm_u]
yield u.stop()
await u.stop()
next_url = ujoin(app.base_url, 'user/jones/tree')
r = yield async_requests.post(
r = await async_requests.post(
url_concat(ujoin(base_url, 'spawn'), {'next': next_url}),
cookies=cookies,
data={'bounds': ['-1', '1'], 'energy': '511keV'},
@@ -259,15 +242,14 @@ def test_spawn_form(app):
}
@pytest.mark.gen_test
def test_spawn_form_admin_access(app, admin_access):
async def test_spawn_form_admin_access(app, admin_access):
with mock.patch.dict(app.tornado_settings, {'spawner_class': FormSpawner}):
base_url = ujoin(public_host(app), app.hub.base_url)
cookies = yield app.login_user('admin')
cookies = await app.login_user('admin')
u = add_user(app.db, app=app, name='martha')
next_url = ujoin(app.base_url, 'user', u.name, 'tree')
r = yield async_requests.post(
r = await async_requests.post(
url_concat(ujoin(base_url, 'spawn', u.name), {'next': next_url}),
cookies=cookies,
data={'bounds': ['-3', '3'], 'energy': '938MeV'},
@@ -282,16 +264,15 @@ def test_spawn_form_admin_access(app, admin_access):
}
@pytest.mark.gen_test
def test_spawn_form_with_file(app):
async def test_spawn_form_with_file(app):
with mock.patch.dict(app.tornado_settings, {'spawner_class': FormSpawner}):
base_url = ujoin(public_host(app), app.hub.base_url)
cookies = yield app.login_user('jones')
cookies = await app.login_user('jones')
orm_u = orm.User.find(app.db, 'jones')
u = app.users[orm_u]
yield u.stop()
await u.stop()
r = yield async_requests.post(ujoin(base_url, 'spawn'),
r = await async_requests.post(ujoin(base_url, 'spawn'),
cookies=cookies,
data={
'bounds': ['-1', '1'],
@@ -310,12 +291,11 @@ def test_spawn_form_with_file(app):
}
@pytest.mark.gen_test
def test_user_redirect(app):
async def test_user_redirect(app):
name = 'wash'
cookies = yield app.login_user(name)
cookies = await app.login_user(name)
r = yield get_page('/user-redirect/tree/top/', app)
r = await get_page('/user-redirect/tree/top/', app)
r.raise_for_status()
print(urlparse(r.url))
path = urlparse(r.url).path
@@ -325,32 +305,31 @@ def test_user_redirect(app):
'next': ujoin(app.hub.base_url, '/user-redirect/tree/top/')
})
r = yield get_page('/user-redirect/notebooks/test.ipynb', app, cookies=cookies)
r = await get_page('/user-redirect/notebooks/test.ipynb', app, cookies=cookies)
r.raise_for_status()
print(urlparse(r.url))
path = urlparse(r.url).path
assert path == ujoin(app.base_url, '/user/%s/notebooks/test.ipynb' % name)
@pytest.mark.gen_test
def test_user_redirect_deprecated(app):
async def test_user_redirect_deprecated(app):
"""redirecting from /user/someonelse/ URLs (deprecated)"""
name = 'wash'
cookies = yield app.login_user(name)
cookies = await app.login_user(name)
r = yield get_page('/user/baduser', app, cookies=cookies, hub=False)
r = await get_page('/user/baduser', app, cookies=cookies, hub=False)
r.raise_for_status()
print(urlparse(r.url))
path = urlparse(r.url).path
assert path == ujoin(app.base_url, '/user/%s/' % name)
r = yield get_page('/user/baduser/test.ipynb', app, cookies=cookies, hub=False)
r = await get_page('/user/baduser/test.ipynb', app, cookies=cookies, hub=False)
r.raise_for_status()
print(urlparse(r.url))
path = urlparse(r.url).path
assert path == ujoin(app.base_url, '/user/%s/test.ipynb' % name)
r = yield get_page('/user/baduser/test.ipynb', app, hub=False)
r = await get_page('/user/baduser/test.ipynb', app, hub=False)
r.raise_for_status()
print(urlparse(r.url))
path = urlparse(r.url).path
@@ -361,11 +340,10 @@ def test_user_redirect_deprecated(app):
})
@pytest.mark.gen_test
def test_login_fail(app):
async def test_login_fail(app):
name = 'wash'
base_url = public_url(app)
r = yield async_requests.post(base_url + 'hub/login',
r = await async_requests.post(base_url + 'hub/login',
data={
'username': name,
'password': 'wrong',
@@ -375,8 +353,7 @@ def test_login_fail(app):
assert not r.cookies
@pytest.mark.gen_test
def test_login_strip(app):
async def test_login_strip(app):
"""Test that login form doesn't strip whitespace from passwords"""
form_data = {
'username': 'spiff',
@@ -389,7 +366,7 @@ def test_login_strip(app):
called_with.append(data)
with mock.patch.object(app.authenticator, 'authenticate', mock_authenticate):
yield async_requests.post(base_url + 'hub/login',
await async_requests.post(base_url + 'hub/login',
data=form_data,
allow_redirects=False,
)
@@ -415,9 +392,8 @@ def test_login_strip(app):
(False, '//other.domain', ''),
]
)
@pytest.mark.gen_test
def test_login_redirect(app, running, next_url, location):
cookies = yield app.login_user('river')
async def test_login_redirect(app, running, next_url, location):
cookies = await app.login_user('river')
user = app.users['river']
if location:
location = ujoin(app.base_url, location)
@@ -433,18 +409,17 @@ def test_login_redirect(app, running, next_url, location):
if running and not user.active:
# ensure running
yield user.spawn()
await user.spawn()
elif user.active and not running:
# ensure not running
yield user.stop()
r = yield get_page(url, app, cookies=cookies, allow_redirects=False)
await user.stop()
r = await get_page(url, app, cookies=cookies, allow_redirects=False)
r.raise_for_status()
assert r.status_code == 302
assert location == r.headers['Location']
@pytest.mark.gen_test
def test_auto_login(app, request):
async def test_auto_login(app, request):
class DummyLoginHandler(BaseHandler):
def get(self):
self.write('ok!')
@@ -453,7 +428,7 @@ def test_auto_login(app, request):
(ujoin(app.hub.base_url, 'dummy'), DummyLoginHandler),
])
# no auto_login: end up at /hub/login
r = yield async_requests.get(base_url)
r = await async_requests.get(base_url)
assert r.url == public_url(app, path='hub/login')
# enable auto_login: redirect from /hub/login to /hub/dummy
authenticator = Authenticator(auto_login=True)
@@ -462,28 +437,28 @@ def test_auto_login(app, request):
with mock.patch.dict(app.tornado_settings, {
'authenticator': authenticator,
}):
r = yield async_requests.get(base_url)
r = await async_requests.get(base_url)
assert r.url == public_url(app, path='hub/dummy')
@pytest.mark.gen_test
def test_auto_login_logout(app):
async def test_auto_login_logout(app):
name = 'burnham'
cookies = yield app.login_user(name)
cookies = await app.login_user(name)
with mock.patch.dict(app.tornado_settings, {
'authenticator': Authenticator(auto_login=True),
}):
r = yield async_requests.get(public_host(app) + app.tornado_settings['logout_url'], cookies=cookies)
r = await async_requests.get(public_host(app) + app.tornado_settings['logout_url'], cookies=cookies)
r.raise_for_status()
logout_url = public_host(app) + app.tornado_settings['logout_url']
assert r.url == logout_url
assert r.cookies == {}
@pytest.mark.gen_test
def test_logout(app):
async def test_logout(app):
name = 'wash'
cookies = yield app.login_user(name)
r = yield async_requests.get(public_host(app) + app.tornado_settings['logout_url'], cookies=cookies)
cookies = await app.login_user(name)
r = await async_requests.get(public_host(app) + app.tornado_settings['logout_url'], cookies=cookies)
r.raise_for_status()
login_url = public_host(app) + app.tornado_settings['login_url']
assert r.url == login_url
@@ -491,7 +466,6 @@ def test_logout(app):
@pytest.mark.parametrize('shutdown_on_logout', [True, False])
@pytest.mark.gen_test
async def test_shutdown_on_logout(app, shutdown_on_logout):
name = 'shutitdown'
cookies = await app.login_user(name)
@@ -535,50 +509,46 @@ async def test_shutdown_on_logout(app, shutdown_on_logout):
assert spawner.ready == (not shutdown_on_logout)
@pytest.mark.gen_test
def test_login_no_whitelist_adds_user(app):
async def test_login_no_whitelist_adds_user(app):
auth = app.authenticator
mock_add_user = mock.Mock()
with mock.patch.object(auth, 'add_user', mock_add_user):
cookies = yield app.login_user('jubal')
cookies = await app.login_user('jubal')
user = app.users['jubal']
assert mock_add_user.mock_calls == [mock.call(user)]
@pytest.mark.gen_test
def test_static_files(app):
async def test_static_files(app):
base_url = ujoin(public_host(app), app.hub.base_url)
r = yield async_requests.get(ujoin(base_url, 'logo'))
r = await async_requests.get(ujoin(base_url, 'logo'))
r.raise_for_status()
assert r.headers['content-type'] == 'image/png'
r = yield async_requests.get(ujoin(base_url, 'static', 'images', 'jupyter.png'))
r = await async_requests.get(ujoin(base_url, 'static', 'images', 'jupyter.png'))
r.raise_for_status()
assert r.headers['content-type'] == 'image/png'
r = yield async_requests.get(ujoin(base_url, 'static', 'css', 'style.min.css'))
r = await async_requests.get(ujoin(base_url, 'static', 'css', 'style.min.css'))
r.raise_for_status()
assert r.headers['content-type'] == 'text/css'
@pytest.mark.gen_test
def test_token_auth(app):
cookies = yield app.login_user('token')
r = yield get_page('token', app, cookies=cookies)
async def test_token_auth(app):
cookies = await app.login_user('token')
r = await get_page('token', app, cookies=cookies)
r.raise_for_status()
assert r.status_code == 200
@pytest.mark.gen_test
def test_oauth_token_page(app):
async def test_oauth_token_page(app):
name = 'token'
cookies = yield app.login_user(name)
cookies = await app.login_user(name)
user = app.users[orm.User.find(app.db, name)]
client = orm.OAuthClient(identifier='token')
app.db.add(client)
oauth_token = orm.OAuthAccessToken(client=client, user=user, grant_type=orm.GrantType.authorization_code)
app.db.add(oauth_token)
app.db.commit()
r = yield get_page('token', app, cookies=cookies)
r = await get_page('token', app, cookies=cookies)
r.raise_for_status()
assert r.status_code == 200
@@ -587,14 +557,11 @@ def test_oauth_token_page(app):
503,
404,
])
@pytest.mark.gen_test
def test_proxy_error(app, error_status):
r = yield get_page('/error/%i' % error_status, app)
async def test_proxy_error(app, error_status):
r = await get_page('/error/%i' % error_status, app)
assert r.status_code == 200
@pytest.mark.gen_test
@pytest.mark.parametrize(
"announcements",
[
@@ -604,7 +571,7 @@ def test_proxy_error(app, error_status):
"login,logout",
]
)
def test_announcements(app, announcements):
async def test_announcements(app, announcements):
"""Test announcements on various pages"""
# Default announcement - same on all pages
ann01 = "ANNOUNCE01"
@@ -620,26 +587,26 @@ def test_announcements(app, announcements):
else:
assert ann01 in text
cookies = yield app.login_user("jones")
cookies = await app.login_user("jones")
with mock.patch.dict(
app.tornado_settings,
{"template_vars": template_vars, "spawner_class": FormSpawner},
):
r = yield get_page("login", app)
r = await get_page("login", app)
r.raise_for_status()
assert_announcement("login", r.text)
r = yield get_page("spawn", app, cookies=cookies)
r = await get_page("spawn", app, cookies=cookies)
r.raise_for_status()
assert_announcement("spawn", r.text)
r = yield get_page("home", app, cookies=cookies) # hub/home
r = await get_page("home", app, cookies=cookies) # hub/home
r.raise_for_status()
assert_announcement("home", r.text)
# need auto_login=True to get logout page
auto_login = app.authenticator.auto_login
app.authenticator.auto_login = True
try:
r = yield get_page("logout", app, cookies=cookies)
r = await get_page("logout", app, cookies=cookies)
finally:
app.authenticator.auto_login = auto_login
r.raise_for_status()
@@ -654,18 +621,16 @@ def test_announcements(app, announcements):
"redirect_uri=ok&client_id=nosuchthing",
]
)
@pytest.mark.gen_test
def test_bad_oauth_get(app, params):
cookies = yield app.login_user("authorizer")
r = yield get_page("hub/api/oauth2/authorize?" + params, app, hub=False, cookies=cookies)
async def test_bad_oauth_get(app, params):
cookies = await app.login_user("authorizer")
r = await get_page("hub/api/oauth2/authorize?" + params, app, hub=False, cookies=cookies)
assert r.status_code == 400
@pytest.mark.gen_test
def test_token_page(app):
async def test_token_page(app):
name = "cake"
cookies = yield app.login_user(name)
r = yield get_page("token", app, cookies=cookies)
cookies = await app.login_user(name)
r = await get_page("token", app, cookies=cookies)
r.raise_for_status()
assert urlparse(r.url).path.endswith('/hub/token')
def extract_body(r):
@@ -684,7 +649,7 @@ def test_token_page(app):
token = user.new_api_token(expires_in=60, note="my-test-token")
app.db.commit()
r = yield get_page("token", app, cookies=cookies)
r = await get_page("token", app, cookies=cookies)
r.raise_for_status()
body = extract_body(r)
assert "API Tokens" in body, body
@@ -695,10 +660,10 @@ def test_token_page(app):
# spawn the user to trigger oauth, etc.
# request an oauth token
user.spawner.cmd = [sys.executable, '-m', 'jupyterhub.singleuser']
r = yield get_page("spawn", app, cookies=cookies)
r = await get_page("spawn", app, cookies=cookies)
r.raise_for_status()
r = yield get_page("token", app, cookies=cookies)
r = await get_page("token", app, cookies=cookies)
r.raise_for_status()
body = extract_body(r)
assert "API Tokens" in body, body
@@ -706,31 +671,27 @@ def test_token_page(app):
assert "Authorized Applications" in body, body
@pytest.mark.gen_test
def test_server_not_running_api_request(app):
cookies = yield app.login_user("bees")
r = yield get_page("user/bees/api/status", app, hub=False, cookies=cookies)
async def test_server_not_running_api_request(app):
cookies = await app.login_user("bees")
r = await get_page("user/bees/api/status", app, hub=False, cookies=cookies)
assert r.status_code == 404
assert r.headers["content-type"] == "application/json"
assert r.json() == {"message": "bees is not running"}
@pytest.mark.gen_test
def test_metrics_no_auth(app):
r = yield get_page("metrics", app)
async def test_metrics_no_auth(app):
r = await get_page("metrics", app)
assert r.status_code == 403
@pytest.mark.gen_test
def test_metrics_auth(app):
cookies = yield app.login_user('river')
async def test_metrics_auth(app):
cookies = await app.login_user('river')
metrics_url = ujoin(public_host(app), app.hub.base_url, 'metrics')
r = yield get_page("metrics", app, cookies=cookies)
r = await get_page("metrics", app, cookies=cookies)
assert r.status_code == 200
assert r.url == metrics_url
@pytest.mark.gen_test
def test_health_check_request(app):
r = yield get_page('health', app)
async def test_health_check_request(app):
r = await get_page('health', app)
assert r.status_code == 200

View File

@@ -16,6 +16,7 @@ from .mocking import MockHub
from .test_api import api_request, add_user
from ..utils import wait_for_http_server, url_path_join as ujoin
@pytest.fixture
def disable_check_routes(app):
# disable periodic check_routes while we are testing
@@ -25,9 +26,8 @@ def disable_check_routes(app):
finally:
app.last_activity_callback.start()
@pytest.mark.gen_test
def test_external_proxy(request):
async def test_external_proxy(request):
auth_token = 'secret!'
proxy_ip = '127.0.0.1'
proxy_port = 54321
@@ -71,23 +71,23 @@ def test_external_proxy(request):
def wait_for_proxy():
return wait_for_http_server('http://%s:%i' % (proxy_ip, proxy_port))
yield wait_for_proxy()
await wait_for_proxy()
yield app.initialize([])
yield app.start()
await app.initialize([])
await app.start()
assert app.proxy.proxy_process is None
# test if api service has a root route '/'
routes = yield app.proxy.get_all_routes()
routes = await app.proxy.get_all_routes()
assert list(routes.keys()) == [app.hub.routespec]
# add user to the db and start a single user server
name = 'river'
add_user(app.db, app, name=name)
r = yield api_request(app, 'users', name, 'server', method='post')
r = await api_request(app, 'users', name, 'server', method='post')
r.raise_for_status()
routes = yield app.proxy.get_all_routes()
routes = await app.proxy.get_all_routes()
# sets the desired path result
user_path = ujoin(app.base_url, 'user/river') + '/'
print(app.base_url, user_path)
@@ -101,18 +101,18 @@ def test_external_proxy(request):
proxy.terminate()
proxy.wait(timeout=10)
proxy = Popen(cmd, env=env)
yield wait_for_proxy()
await wait_for_proxy()
routes = yield app.proxy.get_all_routes()
routes = await app.proxy.get_all_routes()
assert list(routes.keys()) == []
# poke the server to update the proxy
r = yield api_request(app, 'proxy', method='post')
r = await api_request(app, 'proxy', method='post')
r.raise_for_status()
# check that the routes are correct
routes = yield app.proxy.get_all_routes()
routes = await app.proxy.get_all_routes()
assert sorted(routes.keys()) == [app.hub.routespec, user_spec]
# teardown the proxy, and start a new one with different auth and port
@@ -131,11 +131,11 @@ def test_external_proxy(request):
if app.subdomain_host:
cmd.append('--host-routing')
proxy = Popen(cmd, env=env)
yield wait_for_proxy()
await wait_for_proxy()
# tell the hub where the new proxy is
new_api_url = 'http://{}:{}'.format(proxy_ip, proxy_port)
r = yield api_request(app, 'proxy', method='patch', data=json.dumps({
r = await api_request(app, 'proxy', method='patch', data=json.dumps({
'api_url': new_api_url,
'auth_token': new_auth_token,
}))
@@ -145,11 +145,10 @@ def test_external_proxy(request):
assert app.proxy.auth_token == new_auth_token
# check that the routes are correct
routes = yield app.proxy.get_all_routes()
routes = await app.proxy.get_all_routes()
assert sorted(routes.keys()) == [app.hub.routespec, user_spec]
@pytest.mark.gen_test
@pytest.mark.parametrize("username", [
'zoe',
'50fia',
@@ -157,27 +156,27 @@ def test_external_proxy(request):
'~TestJH',
'has@',
])
def test_check_routes(app, username, disable_check_routes):
async def test_check_routes(app, username, disable_check_routes):
proxy = app.proxy
test_user = add_user(app.db, app, name=username)
r = yield api_request(app, 'users/%s/server' % username, method='post')
r = await api_request(app, 'users/%s/server' % username, method='post')
r.raise_for_status()
# check a valid route exists for user
routes = yield app.proxy.get_all_routes()
routes = await app.proxy.get_all_routes()
before = sorted(routes)
assert test_user.proxy_spec in before
# check if a route is removed when user deleted
yield app.proxy.check_routes(app.users, app._service_map)
yield proxy.delete_user(test_user)
routes = yield app.proxy.get_all_routes()
await app.proxy.check_routes(app.users, app._service_map)
await proxy.delete_user(test_user)
routes = await app.proxy.get_all_routes()
during = sorted(routes)
assert test_user.proxy_spec not in during
# check if a route exists for user
yield app.proxy.check_routes(app.users, app._service_map)
routes = yield app.proxy.get_all_routes()
await app.proxy.check_routes(app.users, app._service_map)
routes = await app.proxy.get_all_routes()
after = sorted(routes)
assert test_user.proxy_spec in after
@@ -185,7 +184,6 @@ def test_check_routes(app, username, disable_check_routes):
assert before == after
@pytest.mark.gen_test
@pytest.mark.parametrize("routespec", [
'/has%20space/foo/',
'/missing-trailing/slash',
@@ -194,7 +192,7 @@ def test_check_routes(app, username, disable_check_routes):
'host.name/path/',
'other.host/path/no/slash',
])
def test_add_get_delete(app, routespec, disable_check_routes):
async def test_add_get_delete(app, routespec, disable_check_routes):
arg = routespec
if not routespec.endswith('/'):
routespec = routespec + '/'
@@ -213,26 +211,25 @@ def test_add_get_delete(app, routespec, disable_check_routes):
proxy = app.proxy
target = 'https://localhost:1234'
with context():
yield proxy.add_route(arg, target, {})
routes = yield proxy.get_all_routes()
await proxy.add_route(arg, target, {})
routes = await proxy.get_all_routes()
if not expect_value_error:
assert routespec in routes.keys()
with context():
route = yield proxy.get_route(arg)
route = await proxy.get_route(arg)
assert route == {
'target': target,
'routespec': routespec,
'data': route.get('data'),
}
with context():
yield proxy.delete_route(arg)
await proxy.delete_route(arg)
with context():
route = yield proxy.get_route(arg)
route = await proxy.get_route(arg)
assert route is None
@pytest.mark.gen_test
@pytest.mark.parametrize("test_data", [None, 'notjson', json.dumps([])])
def test_proxy_patch_bad_request_data(app, test_data):
r = yield api_request(app, 'proxy', method='patch', data=test_data)
async def test_proxy_patch_bad_request_data(app, test_data):
r = await api_request(app, 'proxy', method='patch', data=test_data)
assert r.status_code == 400

View File

@@ -1,5 +1,6 @@
"""Tests for services"""
import asyncio
from binascii import hexlify
from contextlib import contextmanager
import os
@@ -8,13 +9,14 @@ import sys
from threading import Event
import time
from async_generator import asynccontextmanager
import pytest
import requests
from tornado import gen
from tornado.ioloop import IOLoop
from .mocking import public_url
from ..utils import url_path_join, wait_for_http_server, random_port
from ..utils import url_path_join, wait_for_http_server, random_port, maybe_future
from .utils import async_requests
mockservice_path = os.path.dirname(os.path.abspath(__file__))
@@ -22,8 +24,8 @@ mockservice_py = os.path.join(mockservice_path, 'mockservice.py')
mockservice_cmd = [sys.executable, mockservice_py]
@contextmanager
def external_service(app, name='mockservice'):
@asynccontextmanager
async def external_service(app, name='mockservice'):
env = {
'JUPYTERHUB_API_TOKEN': hexlify(os.urandom(5)),
'JUPYTERHUB_SERVICE_NAME': name,
@@ -31,17 +33,14 @@ def external_service(app, name='mockservice'):
'JUPYTERHUB_SERVICE_URL': 'http://127.0.0.1:%i' % random_port(),
}
proc = Popen(mockservice_cmd, env=env)
IOLoop().run_sync(
lambda: wait_for_http_server(env['JUPYTERHUB_SERVICE_URL'])
)
try:
await wait_for_http_server(env['JUPYTERHUB_SERVICE_URL'])
yield env
finally:
proc.terminate()
@pytest.mark.gen_test
def test_managed_service(mockservice):
async def test_managed_service(mockservice):
service = mockservice
proc = service.proc
assert isinstance(proc.pid, object)
@@ -58,19 +57,18 @@ def test_managed_service(mockservice):
if service.proc is not proc:
break
else:
yield gen.sleep(0.2)
await asyncio.sleep(0.2)
assert service.proc.pid != first_pid
assert service.proc.poll() is None
@pytest.mark.gen_test
def test_proxy_service(app, mockservice_url):
async def test_proxy_service(app, mockservice_url):
service = mockservice_url
name = service.name
yield app.proxy.get_all_routes()
await app.proxy.get_all_routes()
url = public_url(app, service) + '/foo'
r = yield async_requests.get(url, allow_redirects=False)
r = await async_requests.get(url, allow_redirects=False)
path = '/services/{}/foo'.format(name)
r.raise_for_status()
@@ -78,23 +76,22 @@ def test_proxy_service(app, mockservice_url):
assert r.text.endswith(path)
@pytest.mark.gen_test
def test_external_service(app):
async def test_external_service(app):
name = 'external'
with external_service(app, name=name) as env:
async with external_service(app, name=name) as env:
app.services = [{
'name': name,
'admin': True,
'url': env['JUPYTERHUB_SERVICE_URL'],
'api_token': env['JUPYTERHUB_API_TOKEN'],
}]
yield app.init_services()
yield app.init_api_tokens()
yield app.proxy.add_all_services(app._service_map)
await maybe_future(app.init_services())
await app.init_api_tokens()
await app.proxy.add_all_services(app._service_map)
service = app._service_map[name]
url = public_url(app, service) + '/api/users'
r = yield async_requests.get(url, allow_redirects=False)
r = await async_requests.get(url, allow_redirects=False)
r.raise_for_status()
assert r.status_code == 200
resp = r.json()

View File

@@ -227,11 +227,10 @@ def test_hub_authenticated(request):
assert r.status_code == 403
@pytest.mark.gen_test
def test_hubauth_cookie(app, mockservice_url):
async def test_hubauth_cookie(app, mockservice_url):
"""Test HubAuthenticated service with user cookies"""
cookies = yield app.login_user('badger')
r = yield async_requests.get(public_url(app, mockservice_url) + '/whoami/', cookies=cookies)
cookies = await app.login_user('badger')
r = await async_requests.get(public_url(app, mockservice_url) + '/whoami/', cookies=cookies)
r.raise_for_status()
print(r.text)
reply = r.json()
@@ -242,15 +241,14 @@ def test_hubauth_cookie(app, mockservice_url):
}
@pytest.mark.gen_test
def test_hubauth_token(app, mockservice_url):
async def test_hubauth_token(app, mockservice_url):
"""Test HubAuthenticated service with user API tokens"""
u = add_user(app.db, name='river')
token = u.new_api_token()
app.db.commit()
# token in Authorization header
r = yield async_requests.get(public_url(app, mockservice_url) + '/whoami/',
r = await async_requests.get(public_url(app, mockservice_url) + '/whoami/',
headers={
'Authorization': 'token %s' % token,
})
@@ -262,7 +260,7 @@ def test_hubauth_token(app, mockservice_url):
}
# token in ?token parameter
r = yield async_requests.get(public_url(app, mockservice_url) + '/whoami/?token=%s' % token)
r = await async_requests.get(public_url(app, mockservice_url) + '/whoami/?token=%s' % token)
r.raise_for_status()
reply = r.json()
sub_reply = { key: reply.get(key, 'missing') for key in ['name', 'admin']}
@@ -271,7 +269,7 @@ def test_hubauth_token(app, mockservice_url):
'admin': False,
}
r = yield async_requests.get(public_url(app, mockservice_url) + '/whoami/?token=no-such-token',
r = await async_requests.get(public_url(app, mockservice_url) + '/whoami/?token=no-such-token',
allow_redirects=False,
)
assert r.status_code == 302
@@ -281,17 +279,16 @@ def test_hubauth_token(app, mockservice_url):
assert path.endswith('/hub/login')
@pytest.mark.gen_test
def test_hubauth_service_token(app, mockservice_url):
async def test_hubauth_service_token(app, mockservice_url):
"""Test HubAuthenticated service with service API tokens"""
token = hexlify(os.urandom(5)).decode('utf8')
name = 'test-api-service'
app.service_tokens[token] = name
yield app.init_api_tokens()
await app.init_api_tokens()
# token in Authorization header
r = yield async_requests.get(public_url(app, mockservice_url) + '/whoami/',
r = await async_requests.get(public_url(app, mockservice_url) + '/whoami/',
headers={
'Authorization': 'token %s' % token,
})
@@ -305,7 +302,7 @@ def test_hubauth_service_token(app, mockservice_url):
assert not r.cookies
# token in ?token parameter
r = yield async_requests.get(public_url(app, mockservice_url) + '/whoami/?token=%s' % token)
r = await async_requests.get(public_url(app, mockservice_url) + '/whoami/?token=%s' % token)
r.raise_for_status()
reply = r.json()
assert reply == {
@@ -314,7 +311,7 @@ def test_hubauth_service_token(app, mockservice_url):
'admin': False,
}
r = yield async_requests.get(public_url(app, mockservice_url) + '/whoami/?token=no-such-token',
r = await async_requests.get(public_url(app, mockservice_url) + '/whoami/?token=no-such-token',
allow_redirects=False,
)
assert r.status_code == 302
@@ -324,16 +321,15 @@ def test_hubauth_service_token(app, mockservice_url):
assert path.endswith('/hub/login')
@pytest.mark.gen_test
def test_oauth_service(app, mockservice_url):
async def test_oauth_service(app, mockservice_url):
service = mockservice_url
url = url_path_join(public_url(app, mockservice_url) + 'owhoami/?arg=x')
# first request is only going to login and get us to the oauth form page
s = AsyncSession()
name = 'link'
s.cookies = yield app.login_user(name)
s.cookies = await app.login_user(name)
r = yield s.get(url)
r = await s.get(url)
r.raise_for_status()
# we should be looking at the oauth confirmation page
assert urlparse(r.url).path == app.base_url + 'hub/api/oauth2/authorize'
@@ -341,7 +337,7 @@ def test_oauth_service(app, mockservice_url):
assert set(r.history[0].cookies.keys()) == {'service-%s-oauth-state' % service.name}
# submit the oauth form to complete authorization
r = yield s.post(r.url, data={'scopes': ['identify']}, headers={'Referer': r.url})
r = await s.post(r.url, data={'scopes': ['identify']}, headers={'Referer': r.url})
r.raise_for_status()
assert r.url == url
# verify oauth cookie is set
@@ -350,7 +346,7 @@ def test_oauth_service(app, mockservice_url):
assert 'service-%s-oauth-state' % service.name not in set(s.cookies.keys())
# second request should be authenticated, which means no redirects
r = yield s.get(url, allow_redirects=False)
r = await s.get(url, allow_redirects=False)
r.raise_for_status()
assert r.status_code == 200
reply = r.json()
@@ -363,7 +359,7 @@ def test_oauth_service(app, mockservice_url):
# token-authenticated request to HubOAuth
token = app.users[name].new_api_token()
# token in ?token parameter
r = yield async_requests.get(url_concat(url, {'token': token}))
r = await async_requests.get(url_concat(url, {'token': token}))
r.raise_for_status()
reply = r.json()
assert reply['name'] == name
@@ -371,7 +367,7 @@ def test_oauth_service(app, mockservice_url):
# verify that ?token= requests set a cookie
assert len(r.cookies) != 0
# ensure cookie works in future requests
r = yield async_requests.get(
r = await async_requests.get(
url,
cookies=r.cookies,
allow_redirects=False,
@@ -382,17 +378,16 @@ def test_oauth_service(app, mockservice_url):
assert reply['name'] == name
@pytest.mark.gen_test
def test_oauth_cookie_collision(app, mockservice_url):
async def test_oauth_cookie_collision(app, mockservice_url):
service = mockservice_url
url = url_path_join(public_url(app, mockservice_url), 'owhoami/')
print(url)
s = AsyncSession()
name = 'mypha'
s.cookies = yield app.login_user(name)
s.cookies = await app.login_user(name)
state_cookie_name = 'service-%s-oauth-state' % service.name
service_cookie_name = 'service-%s' % service.name
oauth_1 = yield s.get(url)
oauth_1 = await s.get(url)
print(oauth_1.headers)
print(oauth_1.cookies, oauth_1.url, url)
assert state_cookie_name in s.cookies
@@ -402,7 +397,7 @@ def test_oauth_cookie_collision(app, mockservice_url):
state_1 = s.cookies[state_cookie_name]
# start second oauth login before finishing the first
oauth_2 = yield s.get(url)
oauth_2 = await s.get(url)
state_cookies = [ c for c in s.cookies.keys() if c.startswith(state_cookie_name) ]
assert len(state_cookies) == 2
# get the random-suffix cookie name
@@ -412,7 +407,7 @@ def test_oauth_cookie_collision(app, mockservice_url):
# finish oauth 2
# submit the oauth form to complete authorization
r = yield s.post(
r = await s.post(
oauth_2.url,
data={'scopes': ['identify']},
headers={'Referer': oauth_2.url},
@@ -426,7 +421,7 @@ def test_oauth_cookie_collision(app, mockservice_url):
service_cookie_2 = s.cookies[service_cookie_name]
# finish oauth 1
r = yield s.post(
r = await s.post(
oauth_1.url,
data={'scopes': ['identify']},
headers={'Referer': oauth_1.url},
@@ -445,8 +440,7 @@ def test_oauth_cookie_collision(app, mockservice_url):
assert state_cookies == []
@pytest.mark.gen_test
def test_oauth_logout(app, mockservice_url):
async def test_oauth_logout(app, mockservice_url):
"""Verify that logout via the Hub triggers logout for oauth services
1. clears session id cookie
@@ -471,18 +465,18 @@ def test_oauth_logout(app, mockservice_url):
# ensure we start empty
assert auth_tokens() == []
s.cookies = yield app.login_user(name)
s.cookies = await app.login_user(name)
assert 'jupyterhub-session-id' in s.cookies
r = yield s.get(url)
r = await s.get(url)
r.raise_for_status()
assert urlparse(r.url).path.endswith('oauth2/authorize')
# submit the oauth form to complete authorization
r = yield s.post(r.url, data={'scopes': ['identify']}, headers={'Referer': r.url})
r = await s.post(r.url, data={'scopes': ['identify']}, headers={'Referer': r.url})
r.raise_for_status()
assert r.url == url
# second request should be authenticated
r = yield s.get(url, allow_redirects=False)
r = await s.get(url, allow_redirects=False)
r.raise_for_status()
assert r.status_code == 200
reply = r.json()
@@ -501,13 +495,13 @@ def test_oauth_logout(app, mockservice_url):
assert len(auth_tokens()) == 1
# hit hub logout URL
r = yield s.get(public_url(app, path='hub/logout'))
r = await s.get(public_url(app, path='hub/logout'))
r.raise_for_status()
# verify that all cookies other than the service cookie are cleared
assert list(s.cookies.keys()) == [service_cookie_name]
# verify that clearing session id invalidates service cookie
# i.e. redirect back to login page
r = yield s.get(url)
r = await s.get(url)
r.raise_for_status()
assert r.url.split('?')[0] == public_url(app, path='hub/login')
@@ -524,7 +518,7 @@ def test_oauth_logout(app, mockservice_url):
# check that we got the old session id back
assert session_id == s.cookies['jupyterhub-session-id']
r = yield s.get(url, allow_redirects=False)
r = await s.get(url, allow_redirects=False)
r.raise_for_status()
assert r.status_code == 200
reply = r.json()

View File

@@ -13,42 +13,41 @@ from ..utils import url_path_join
from .utils import async_requests, AsyncSession
@pytest.mark.gen_test
def test_singleuser_auth(app):
async def test_singleuser_auth(app):
# use StubSingleUserSpawner to launch a single-user app in a thread
app.spawner_class = StubSingleUserSpawner
app.tornado_settings['spawner_class'] = StubSingleUserSpawner
# login, start the server
cookies = yield app.login_user('nandy')
cookies = await app.login_user('nandy')
user = app.users['nandy']
if not user.running:
yield user.spawn()
await user.spawn()
url = public_url(app, user)
# no cookies, redirects to login page
r = yield async_requests.get(url)
r = await async_requests.get(url)
r.raise_for_status()
assert '/hub/login' in r.url
# with cookies, login successful
r = yield async_requests.get(url, cookies=cookies)
r = await async_requests.get(url, cookies=cookies)
r.raise_for_status()
assert urlparse(r.url).path.rstrip('/').endswith('/user/nandy/tree')
assert r.status_code == 200
# logout
r = yield async_requests.get(url_path_join(url, 'logout'), cookies=cookies)
r = await async_requests.get(url_path_join(url, 'logout'), cookies=cookies)
assert len(r.cookies) == 0
# accessing another user's server hits the oauth confirmation page
cookies = yield app.login_user('burgess')
cookies = await app.login_user('burgess')
s = AsyncSession()
s.cookies = cookies
r = yield s.get(url)
r = await s.get(url)
assert urlparse(r.url).path.endswith('/oauth2/authorize')
# submit the oauth form to complete authorization
r = yield s.post(
r = await s.post(
r.url,
data={'scopes': ['identify']},
headers={'Referer': r.url},
@@ -59,28 +58,27 @@ def test_singleuser_auth(app):
assert 'burgess' in r.text
@pytest.mark.gen_test
def test_disable_user_config(app):
async def test_disable_user_config(app):
# use StubSingleUserSpawner to launch a single-user app in a thread
app.spawner_class = StubSingleUserSpawner
app.tornado_settings['spawner_class'] = StubSingleUserSpawner
# login, start the server
cookies = yield app.login_user('nandy')
cookies = await app.login_user('nandy')
user = app.users['nandy']
# stop spawner, if running:
if user.running:
print("stopping")
yield user.stop()
await user.stop()
# start with new config:
user.spawner.debug = True
user.spawner.disable_user_config = True
yield user.spawn()
yield app.proxy.add_user(user)
await user.spawn()
await app.proxy.add_user(user)
url = public_url(app, user)
# with cookies, login successful
r = yield async_requests.get(url, cookies=cookies)
r = await async_requests.get(url, cookies=cookies)
r.raise_for_status()
assert r.url.rstrip('/').endswith('/user/nandy/tree')
assert r.status_code == 200
@@ -90,6 +88,7 @@ def test_help_output():
out = check_output([sys.executable, '-m', 'jupyterhub.singleuser', '--help-all']).decode('utf8', 'replace')
assert 'JupyterHub' in out
def test_version():
out = check_output([sys.executable, '-m', 'jupyterhub.singleuser', '--version']).decode('utf8', 'replace')
assert jupyterhub.__version__ in out

View File

@@ -60,10 +60,9 @@ def new_spawner(db, **kwargs):
return user._new_spawner('', spawner_class=LocalProcessSpawner, **kwargs)
@pytest.mark.gen_test
def test_spawner(db, request):
async def test_spawner(db, request):
spawner = new_spawner(db)
ip, port = yield spawner.start()
ip, port = await spawner.start()
assert ip == '127.0.0.1'
assert isinstance(port, int)
assert port > 0
@@ -72,15 +71,14 @@ def test_spawner(db, request):
# wait for the process to get to the while True: loop
time.sleep(1)
status = yield spawner.poll()
status = await spawner.poll()
assert status is None
yield spawner.stop()
status = yield spawner.poll()
await spawner.stop()
status = await spawner.poll()
assert status == 1
@gen.coroutine
def wait_for_spawner(spawner, timeout=10):
async def wait_for_spawner(spawner, timeout=10):
"""Wait for an http server to show up
polling at shorter intervals for early termination
@@ -89,72 +87,68 @@ def wait_for_spawner(spawner, timeout=10):
def wait():
return spawner.server.wait_up(timeout=1, http=True)
while time.monotonic() < deadline:
status = yield spawner.poll()
status = await spawner.poll()
assert status is None
try:
yield wait()
await wait()
except TimeoutError:
continue
else:
break
yield wait()
await wait()
@pytest.mark.gen_test
def test_single_user_spawner(app, request):
async def test_single_user_spawner(app, request):
user = next(iter(app.users.values()), None)
spawner = user.spawner
spawner.cmd = ['jupyterhub-singleuser']
yield user.spawn()
await user.spawn()
assert spawner.server.ip == '127.0.0.1'
assert spawner.server.port > 0
yield wait_for_spawner(spawner)
status = yield spawner.poll()
await wait_for_spawner(spawner)
status = await spawner.poll()
assert status is None
yield spawner.stop()
status = yield spawner.poll()
await spawner.stop()
status = await spawner.poll()
assert status == 0
@pytest.mark.gen_test
def test_stop_spawner_sigint_fails(db):
async def test_stop_spawner_sigint_fails(db):
spawner = new_spawner(db, cmd=[sys.executable, '-c', _uninterruptible])
yield spawner.start()
await spawner.start()
# wait for the process to get to the while True: loop
yield gen.sleep(1)
await gen.sleep(1)
status = yield spawner.poll()
status = await spawner.poll()
assert status is None
yield spawner.stop()
status = yield spawner.poll()
await spawner.stop()
status = await spawner.poll()
assert status == -signal.SIGTERM
@pytest.mark.gen_test
def test_stop_spawner_stop_now(db):
async def test_stop_spawner_stop_now(db):
spawner = new_spawner(db)
yield spawner.start()
await spawner.start()
# wait for the process to get to the while True: loop
yield gen.sleep(1)
await gen.sleep(1)
status = yield spawner.poll()
status = await spawner.poll()
assert status is None
yield spawner.stop(now=True)
status = yield spawner.poll()
await spawner.stop(now=True)
status = await spawner.poll()
assert status == -signal.SIGTERM
@pytest.mark.gen_test
def test_spawner_poll(db):
async def test_spawner_poll(db):
first_spawner = new_spawner(db)
user = first_spawner.user
yield first_spawner.start()
await first_spawner.start()
proc = first_spawner.proc
status = yield first_spawner.poll()
status = await first_spawner.poll()
assert status is None
if user.state is None:
user.state = {}
@@ -166,21 +160,21 @@ def test_spawner_poll(db):
spawner.start_polling()
# wait for the process to get to the while True: loop
yield gen.sleep(1)
status = yield spawner.poll()
await gen.sleep(1)
status = await spawner.poll()
assert status is None
# kill the process
proc.terminate()
for i in range(10):
if proc.poll() is None:
yield gen.sleep(1)
await gen.sleep(1)
else:
break
assert proc.poll() is not None
yield gen.sleep(2)
status = yield spawner.poll()
await gen.sleep(2)
status = await spawner.poll()
assert status is not None
@@ -213,8 +207,7 @@ def test_string_formatting(db):
assert s.format_string(s.default_url) == '/base/%s' % name
@pytest.mark.gen_test
def test_popen_kwargs(db):
async def test_popen_kwargs(db):
mock_proc = mock.Mock(spec=Popen)
def mock_popen(*args, **kwargs):
mock_proc.args = args
@@ -224,14 +217,13 @@ def test_popen_kwargs(db):
s = new_spawner(db, popen_kwargs={'shell': True}, cmd='jupyterhub-singleuser')
with mock.patch.object(spawnermod, 'Popen', mock_popen):
yield s.start()
await s.start()
assert mock_proc.kwargs['shell'] == True
assert mock_proc.args[0][:1] == (['jupyterhub-singleuser'])
@pytest.mark.gen_test
def test_shell_cmd(db, tmpdir, request):
async def test_shell_cmd(db, tmpdir, request):
f = tmpdir.join('bashrc')
f.write('export TESTVAR=foo\n')
s = new_spawner(db,
@@ -243,17 +235,17 @@ def test_shell_cmd(db, tmpdir, request):
db.commit()
s.server = Server.from_orm(server)
db.commit()
(ip, port) = yield s.start()
(ip, port) = await s.start()
request.addfinalizer(s.stop)
s.server.ip = ip
s.server.port = port
db.commit()
yield wait_for_spawner(s)
r = yield async_requests.get('http://%s:%i/env' % (ip, port))
await wait_for_spawner(s)
r = await async_requests.get('http://%s:%i/env' % (ip, port))
r.raise_for_status()
env = r.json()
assert env['TESTVAR'] == 'foo'
yield s.stop()
await s.stop()
def test_inherit_overwrite():
@@ -277,8 +269,7 @@ def test_inherit_ok():
pass
@pytest.mark.gen_test
def test_spawner_reuse_api_token(db, app):
async def test_spawner_reuse_api_token(db, app):
# setup: user with no tokens, whose spawner has set the .will_resume flag
user = add_user(app.db, app, name='snoopy')
spawner = user.spawner
@@ -286,26 +277,25 @@ def test_spawner_reuse_api_token(db, app):
# will_resume triggers reuse of tokens
spawner.will_resume = True
# first start: gets a new API token
yield user.spawn()
await user.spawn()
api_token = spawner.api_token
found = orm.APIToken.find(app.db, api_token)
assert found
assert found.user.name == user.name
assert user.api_tokens == [found]
yield user.stop()
await user.stop()
# stop now deletes unused spawners.
# put back the mock spawner!
user.spawners[''] = spawner
# second start: should reuse the token
yield user.spawn()
await user.spawn()
# verify re-use of API token
assert spawner.api_token == api_token
# verify that a new token was not created
assert user.api_tokens == [found]
@pytest.mark.gen_test
def test_spawner_insert_api_token(app):
async def test_spawner_insert_api_token(app):
"""Token provided by spawner is not in the db
Insert token into db as a user-provided token.
@@ -322,17 +312,16 @@ def test_spawner_insert_api_token(app):
# The spawner's provided API token would already be in the db
# unless there is a bug somewhere else (in the Spawner),
# but handle it anyway.
yield user.spawn()
await user.spawn()
assert spawner.api_token == api_token
found = orm.APIToken.find(app.db, api_token)
assert found
assert found.user.name == user.name
assert user.api_tokens == [found]
yield user.stop()
await user.stop()
@pytest.mark.gen_test
def test_spawner_bad_api_token(app):
async def test_spawner_bad_api_token(app):
"""Tokens are revoked when a Spawner gets another user's token"""
# we need two users for this one
user = add_user(app.db, app, name='antimone')
@@ -349,13 +338,12 @@ def test_spawner_bad_api_token(app):
# starting a user's server with another user's token
# should revoke it
with pytest.raises(ValueError):
yield user.spawn()
await user.spawn()
assert orm.APIToken.find(app.db, other_token) is None
assert other_user.api_tokens == []
@pytest.mark.gen_test
def test_spawner_delete_server(app):
async def test_spawner_delete_server(app):
"""Test deleting spawner.server
This can occur during app startup if their server has been deleted.
@@ -393,22 +381,21 @@ def test_spawner_delete_server(app):
"has%40x",
]
)
@pytest.mark.gen_test
def test_spawner_routing(app, name):
async def test_spawner_routing(app, name):
"""Test routing of names with special characters"""
db = app.db
with mock.patch.dict(app.config.LocalProcessSpawner, {'cmd': [sys.executable, '-m', 'jupyterhub.tests.mocksu']}):
user = add_user(app.db, app, name=name)
yield user.spawn()
yield wait_for_spawner(user.spawner)
yield app.proxy.add_user(user)
await user.spawn()
await wait_for_spawner(user.spawner)
await app.proxy.add_user(user)
kwargs = {'allow_redirects': False}
if app.internal_ssl:
kwargs['cert'] = (app.internal_ssl_cert, app.internal_ssl_key)
kwargs["verify"] = app.internal_ssl_ca
url = url_path_join(public_url(app, user), "test/url")
r = yield async_requests.get(url, **kwargs)
r = await async_requests.get(url, **kwargs)
r.raise_for_status()
assert r.url == url
assert r.text == urlparse(url).path
yield user.stop()
await user.stop()

View File

@@ -26,7 +26,6 @@ def schedule_future(io_loop, *, delay, result=None):
return f
@pytest.mark.gen_test
@pytest.mark.parametrize("deadline, n, delay, expected", [
(0, 3, 1, []),
(0, 3, 0, [0, 1, 2]),
@@ -43,7 +42,6 @@ async def test_iterate_until(io_loop, deadline, n, delay, expected):
assert yielded == expected
@pytest.mark.gen_test
async def test_iterate_until_ready_after_deadline(io_loop):
f = schedule_future(io_loop, delay=0)