123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- """Test the kernels service API."""
- import json
- import time
- from tornado.httpclient import HTTPRequest
- from tornado.ioloop import IOLoop
- from tornado.websocket import websocket_connect
- from jupyter_client.kernelspec import NATIVE_KERNEL_NAME
- from notebook.utils import url_path_join
- from notebook.tests.launchnotebook import NotebookTestBase, assert_http_error
- class KernelAPI(object):
- """Wrapper for kernel REST API requests"""
- def __init__(self, request, base_url, headers):
- self.request = request
- self.base_url = base_url
- self.headers = headers
- def _req(self, verb, path, body=None):
- response = self.request(verb,
- url_path_join('api/kernels', path), data=body)
- if 400 <= response.status_code < 600:
- try:
- response.reason = response.json()['message']
- except:
- pass
- response.raise_for_status()
- return response
- def list(self):
- return self._req('GET', '')
- def get(self, id):
- return self._req('GET', id)
- def start(self, name=NATIVE_KERNEL_NAME):
- body = json.dumps({'name': name})
- return self._req('POST', '', body)
- def shutdown(self, id):
- return self._req('DELETE', id)
- def interrupt(self, id):
- return self._req('POST', url_path_join(id, 'interrupt'))
- def restart(self, id):
- return self._req('POST', url_path_join(id, 'restart'))
- def websocket(self, id):
- loop = IOLoop()
- loop.make_current()
- req = HTTPRequest(
- url_path_join(self.base_url.replace('http', 'ws', 1), 'api/kernels', id, 'channels'),
- headers=self.headers,
- )
- f = websocket_connect(req)
- return loop.run_sync(lambda : f)
- class KernelAPITest(NotebookTestBase):
- """Test the kernels web service API"""
- def setUp(self):
- self.kern_api = KernelAPI(self.request,
- base_url=self.base_url(),
- headers=self.auth_headers(),
- )
- def tearDown(self):
- for k in self.kern_api.list().json():
- self.kern_api.shutdown(k['id'])
- def test_no_kernels(self):
- """Make sure there are no kernels running at the start"""
- kernels = self.kern_api.list().json()
- self.assertEqual(kernels, [])
- def test_default_kernel(self):
- # POST request
- r = self.kern_api._req('POST', '')
- kern1 = r.json()
- self.assertEqual(r.headers['location'], url_path_join(self.url_prefix, 'api/kernels', kern1['id']))
- self.assertEqual(r.status_code, 201)
- self.assertIsInstance(kern1, dict)
- report_uri = url_path_join(self.url_prefix, 'api/security/csp-report')
- expected_csp = '; '.join([
- "frame-ancestors 'self'",
- 'report-uri ' + report_uri,
- "default-src 'none'"
- ])
- self.assertEqual(r.headers['Content-Security-Policy'], expected_csp)
- def test_main_kernel_handler(self):
- # POST request
- r = self.kern_api.start()
- kern1 = r.json()
- self.assertEqual(r.headers['location'], url_path_join(self.url_prefix, 'api/kernels', kern1['id']))
- self.assertEqual(r.status_code, 201)
- self.assertIsInstance(kern1, dict)
- report_uri = url_path_join(self.url_prefix, 'api/security/csp-report')
- expected_csp = '; '.join([
- "frame-ancestors 'self'",
- 'report-uri ' + report_uri,
- "default-src 'none'"
- ])
- self.assertEqual(r.headers['Content-Security-Policy'], expected_csp)
- # GET request
- r = self.kern_api.list()
- self.assertEqual(r.status_code, 200)
- assert isinstance(r.json(), list)
- self.assertEqual(r.json()[0]['id'], kern1['id'])
- self.assertEqual(r.json()[0]['name'], kern1['name'])
- # create another kernel and check that they both are added to the
- # list of kernels from a GET request
- kern2 = self.kern_api.start().json()
- assert isinstance(kern2, dict)
- r = self.kern_api.list()
- kernels = r.json()
- self.assertEqual(r.status_code, 200)
- assert isinstance(kernels, list)
- self.assertEqual(len(kernels), 2)
- # Interrupt a kernel
- r = self.kern_api.interrupt(kern2['id'])
- self.assertEqual(r.status_code, 204)
- # Restart a kernel
- r = self.kern_api.restart(kern2['id'])
- rekern = r.json()
- self.assertEqual(rekern['id'], kern2['id'])
- self.assertEqual(rekern['name'], kern2['name'])
- def test_kernel_handler(self):
- # GET kernel with given id
- kid = self.kern_api.start().json()['id']
- r = self.kern_api.get(kid)
- kern1 = r.json()
- self.assertEqual(r.status_code, 200)
- assert isinstance(kern1, dict)
- self.assertIn('id', kern1)
- self.assertEqual(kern1['id'], kid)
- # Request a bad kernel id and check that a JSON
- # message is returned!
- bad_id = '111-111-111-111-111'
- with assert_http_error(404, 'Kernel does not exist: ' + bad_id):
- self.kern_api.get(bad_id)
- # DELETE kernel with id
- r = self.kern_api.shutdown(kid)
- self.assertEqual(r.status_code, 204)
- kernels = self.kern_api.list().json()
- self.assertEqual(kernels, [])
- # Request to delete a non-existent kernel id
- bad_id = '111-111-111-111-111'
- with assert_http_error(404, 'Kernel does not exist: ' + bad_id):
- self.kern_api.shutdown(bad_id)
- def test_connections(self):
- kid = self.kern_api.start().json()['id']
- model = self.kern_api.get(kid).json()
- self.assertEqual(model['connections'], 0)
- ws = self.kern_api.websocket(kid)
- model = self.kern_api.get(kid).json()
- self.assertEqual(model['connections'], 1)
- ws.close()
- # give it some time to close on the other side:
- for i in range(10):
- model = self.kern_api.get(kid).json()
- if model['connections'] > 0:
- time.sleep(0.1)
- else:
- break
- model = self.kern_api.get(kid).json()
- self.assertEqual(model['connections'], 0)
|