Refactor to share fixtures with option tests

This commit is contained in:
Peter Parente
2017-11-28 23:57:05 -05:00
parent 79ad94ead4
commit 8a59d74813
3 changed files with 135 additions and 55 deletions

View File

@@ -1,16 +1,38 @@
# Copyright (c) Jupyter Development Team. # Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License. # Distributed under the terms of the Modified BSD License.
import time
import pytest import pytest
@pytest.mark.skip('placeholder') def test_cli_args(container, http_client):
def test_cli_args(): """Container should respect notebook server command line args
pass (e.g., disabling token security)"""
container.run(
command=['start-notebook.sh', '--NotebookApp.token=""']
)
resp = http_client.get('http://localhost:8888')
resp.raise_for_status()
assert 'login_submit' not in resp.text
@pytest.mark.skip('placeholder') @pytest.mark.filterwarnings('ignore:Unverified HTTPS request')
def test_unsigned_ssl(): def test_unsigned_ssl(container, http_client):
pass """Container should generate a self-signed SSL certificate
and notebook server should use it to enable HTTPS.
"""
c = container.run(
environment=['GEN_CERT=yes']
)
# NOTE: The requests.Session backing the http_client fixture does not retry
# properly while the server is booting up. An SSL handshake error seems to
# abort the retry logic. Forcing a long sleep for the moment until I have
# time to dig more.
time.sleep(5)
resp = http_client.get('https://localhost:8888', verify=False)
resp.raise_for_status()
assert 'login_submit' in resp.text
@pytest.mark.skip('placeholder') @pytest.mark.skip('placeholder')
def test_uid_change(): def test_uid_change():

101
conftest.py Normal file
View File

@@ -0,0 +1,101 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import os
import docker
import pytest
import requests
from requests.packages.urllib3.util.retry import Retry
from requests.adapters import HTTPAdapter
@pytest.fixture(scope='session')
def http_client():
"""Requests session with retries and backoff."""
s = requests.Session()
retries = Retry(total=5, backoff_factor=1)
s.mount('http://', HTTPAdapter(max_retries=retries))
s.mount('https://', HTTPAdapter(max_retries=retries))
return s
@pytest.fixture(scope='session')
def docker_client():
"""Docker client configured based on the host environment"""
return docker.from_env()
@pytest.fixture(scope='session')
def image_name():
"""Image name to test"""
return os.getenv('TEST_IMAGE', 'jupyter/base-notebook')
class TrackedContainer(object):
"""Wrapper that collects docker container configuration and delays
container creation/execution.
Parameters
----------
docker_client: docker.DockerClient
Docker client instance
image_name: str
Name of the docker image to launch
**kwargs: dict, optional
Default keyword arguments to pass to docker.DockerClient.containers.run
"""
def __init__(self, docker_client, image_name, **kwargs):
self.container = None
self.docker_client = docker_client
self.image_name = image_name
self.kwargs = kwargs
def run(self, **kwargs):
"""Runs a docker container using the preconfigured image name
and a mix of the preconfigured container options and those passed
to this method.
Keeps track of the docker.Container instance spawned to kill it
later.
Parameters
----------
**kwargs: dict, optional
Keyword arguments to pass to docker.DockerClient.containers.run
extending and/or overriding key/value pairs passed to the constructor
Returns
-------
docker.Container
"""
all_kwargs = {}
all_kwargs.update(self.kwargs)
all_kwargs.update(kwargs)
self.container = self.docker_client.containers.run(self.image_name, **all_kwargs)
return self.container
def kill(self):
"""Kills the tracked docker container."""
if self.container:
self.container.kill()
@pytest.fixture(scope='function')
def container(docker_client, image_name):
"""Notebook container with initial configuration appropriate for testing
(e.g., HTTP port exposed to the host for HTTP calls).
Yields the container instance and kills it when the caller is done with it.
"""
container = TrackedContainer(
docker_client,
image_name,
detach=True,
auto_remove=False,
ports={
'8888/tcp': 8888
}
)
yield container
container.kill()

View File

@@ -1,52 +1,9 @@
# Copyright (c) Jupyter Development Team. # Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License. # Distributed under the terms of the Modified BSD License.
import os
import time
import docker def test_secured_server(container, http_client):
import pytest """Notebook server should eventually request user login."""
import requests container.run()
resp = http_client.get('http://localhost:8888')
resp.raise_for_status()
@pytest.fixture(scope='session') assert 'login_submit' in resp.text
def docker_client():
"""Docker client to use"""
return docker.from_env()
@pytest.fixture(scope='session')
def image_name():
"""Image name to test"""
return os.getenv('TEST_IMAGE', 'jupyter/base-notebook')
@pytest.fixture(scope='function')
def nb_container(docker_client, image_name):
"""Notebook container to test"""
container = docker_client.containers.run(
image_name,
detach=True,
auto_remove=True,
ports={
'8888/tcp': 8888
}
)
yield container
container.kill()
def test_server_liveliness(nb_container):
"""Notebook server should eventually respond with HTTP 200 OK."""
for i in range(10):
try:
resp = requests.get('http://localhost:8888')
except requests.exceptions.ConnectionError:
# Wait a bit and try again. Just because the docker container
# is running doesn't mean the notebook server is ready to accept
# connections inside it.
time.sleep(i)
else:
assert resp.status_code == 200
break
else:
assert False, 'could not connect to server'