##// END OF EJS Templates
Refactor tests for kernels REST API
Thomas Kluyver -
Show More
@@ -1,100 +1,113 b''
1 """Test the kernels service API."""
1 """Test the kernels service API."""
2
2
3
3
4 import os
4 import os
5 import sys
5 import sys
6 import json
6 import json
7
7
8 import requests
8 import requests
9
9
10 from IPython.html.utils import url_path_join
10 from IPython.html.utils import url_path_join
11 from IPython.html.tests.launchnotebook import NotebookTestBase
11 from IPython.html.tests.launchnotebook import NotebookTestBase, assert_http_error
12
12
13 class KernelAPI(object):
14 """Wrapper for kernel REST API requests"""
15 def __init__(self, base_url):
16 self.base_url = base_url
17
18 def _req(self, verb, path, body=None):
19 response = requests.request(verb,
20 url_path_join(self.base_url, 'api/kernels', path), data=body)
21
22 if 400 <= response.status_code < 600:
23 try:
24 response.reason = response.json()['message']
25 except:
26 pass
27 response.raise_for_status()
28
29 return response
30
31 def list(self):
32 return self._req('GET', '')
33
34 def get(self, id):
35 return self._req('GET', id)
36
37 def start(self):
38 return self._req('POST', '')
39
40 def shutdown(self, id):
41 return self._req('DELETE', id)
42
43 def interrupt(self, id):
44 return self._req('POST', url_path_join(id, 'interrupt'))
45
46 def restart(self, id):
47 return self._req('POST', url_path_join(id, 'restart'))
13
48
14 class KernelAPITest(NotebookTestBase):
49 class KernelAPITest(NotebookTestBase):
15 """Test the kernels web service API"""
50 """Test the kernels web service API"""
51 def setUp(self):
52 self.kern_api = KernelAPI(self.base_url())
16
53
17 def base_url(self):
54 def tearDown(self):
18 return url_path_join(super(KernelAPITest,self).base_url(), 'api/kernels')
55 for k in self.kern_api.list().json():
19
56 self.kern_api.shutdown(k['id'])
20 def mkkernel(self):
21 r = requests.post(self.base_url())
22 return r.json()
23
57
24 def test__no_kernels(self):
58 def test__no_kernels(self):
25 """Make sure there are no kernels running at the start"""
59 """Make sure there are no kernels running at the start"""
26 url = self.base_url()
60 kernels = self.kern_api.list().json()
27 r = requests.get(url)
61 self.assertEqual(kernels, [])
28 self.assertEqual(r.json(), [])
29
62
30 def test_main_kernel_handler(self):
63 def test_main_kernel_handler(self):
31 # POST request
64 # POST request
32 r = requests.post(self.base_url())
65 r = self.kern_api.start()
33 data = r.json()
66 kern1 = r.json()
34 status = r.status_code
67 self.assertEquals(r.headers['location'], '/api/kernels/' + kern1['id'])
35 header = r.headers
68 self.assertEquals(r.status_code, 201)
36 self.assertIn('location', header)
69 self.assertIsInstance(kern1, dict)
37 self.assertEquals(header['location'], '/api/kernels/' + data['id'])
38 self.assertEquals(status, 201)
39 assert isinstance(data, dict)
40
70
41 # GET request
71 # GET request
42 r = requests.get(self.base_url())
72 r = self.kern_api.list()
43 status = r.status_code
73 self.assertEquals(r.status_code, 200)
44 self.assertEquals(status, 200)
45 assert isinstance(r.json(), list)
74 assert isinstance(r.json(), list)
46 self.assertEqual(r.json()[0]['id'], data['id'])
75 self.assertEqual(r.json()[0]['id'], kern1['id'])
47
76
48 # create another kernel and check that they both are added to the
77 # create another kernel and check that they both are added to the
49 # list of kernels from a GET request
78 # list of kernels from a GET request
50 data2 = self.mkkernel()
79 kern2 = self.kern_api.start().json()
51 assert isinstance(data2, dict)
80 assert isinstance(kern2, dict)
52 r = requests.get(self.base_url())
81 r = self.kern_api.list()
53 kernels = r.json()
82 kernels = r.json()
54 status = r.status_code
83 self.assertEquals(r.status_code, 200)
55 self.assertEquals(status, 200)
56 assert isinstance(kernels, list)
84 assert isinstance(kernels, list)
57 self.assertEquals(len(kernels), 2)
85 self.assertEquals(len(kernels), 2)
58
86
59 def test_kernel_handler(self):
87 def test_kernel_handler(self):
60 # GET kernel with given id
88 # GET kernel with given id
61 data = self.mkkernel()
89 kid = self.kern_api.start().json()['id']
62 url = self.base_url() +'/' + data['id']
90 r = self.kern_api.get(kid)
63 r = requests.get(url)
91 kern1 = r.json()
64 data1 = r.json()
92 self.assertEquals(r.status_code, 200)
65 status = r.status_code
93 assert isinstance(kern1, dict)
66 self.assertEquals(status, 200)
94 self.assertIn('id', kern1)
67 assert isinstance(data1, dict)
95 self.assertIn('ws_url', kern1)
68 self.assertIn('id', data1)
96 self.assertEqual(kern1['id'], kid)
69 self.assertIn('ws_url', data1)
97
70 self.assertEqual(data1['id'], data['id'])
71
72 # Request a bad kernel id and check that a JSON
98 # Request a bad kernel id and check that a JSON
73 # message is returned!
99 # message is returned!
74 bad_id = '111-111-111-111-111'
100 bad_id = '111-111-111-111-111'
75 bad_url = self.base_url() + '/' + bad_id
101 with assert_http_error(404, 'Kernel does not exist: ' + bad_id):
76 r = requests.get(bad_url)
102 self.kern_api.get(bad_id)
77 status = r.status_code
103
78 message = r.json()
79 self.assertEquals(status, 404)
80 assert isinstance(message, dict)
81 self.assertIn('message', message)
82 self.assertEquals(message['message'], 'Kernel does not exist: ' + bad_id)
83
84 # DELETE kernel with id
104 # DELETE kernel with id
85 r = requests.delete(url)
105 r = self.kern_api.shutdown(kid)
86 self.assertEqual(r.status_code, 204)
106 self.assertEqual(r.status_code, 204)
87 r = requests.get(self.base_url())
107 kernels = self.kern_api.list().json()
88 self.assertEqual(r.json(), [])
108 self.assertEqual(kernels, [])
89
109
90 # Request to delete a non-existent kernel id
110 # Request to delete a non-existent kernel id
91 bad_id = '111-111-111-111-111'
111 bad_id = '111-111-111-111-111'
92 bad_url = self.base_url() + '/' + bad_id
112 with assert_http_error(404, 'Kernel does not exist: ' + bad_id):
93 r = requests.delete(bad_url)
113 self.kern_api.shutdown(bad_id)
94 status = r.status_code
95 message = r.json()
96 self.assertEquals(status, 404)
97 assert isinstance(message, dict)
98 self.assertIn('message', message)
99 self.assertEquals(message['message'], 'Kernel does not exist: ' + bad_id)
100 No newline at end of file
@@ -1,81 +1,83 b''
1 """Base class for notebook tests."""
1 """Base class for notebook tests."""
2
2
3 import sys
3 import sys
4 import time
4 import time
5 import requests
5 import requests
6 from contextlib import contextmanager
6 from contextlib import contextmanager
7 from subprocess import Popen, PIPE
7 from subprocess import Popen, PIPE
8 from unittest import TestCase
8 from unittest import TestCase
9
9
10 from IPython.utils.tempdir import TemporaryDirectory
10 from IPython.utils.tempdir import TemporaryDirectory
11
11
12 class NotebookTestBase(TestCase):
12 class NotebookTestBase(TestCase):
13 """A base class for tests that need a running notebook.
13 """A base class for tests that need a running notebook.
14
14
15 This creates an empty profile in a temp ipython_dir
15 This creates an empty profile in a temp ipython_dir
16 and then starts the notebook server with a separate temp notebook_dir.
16 and then starts the notebook server with a separate temp notebook_dir.
17 """
17 """
18
18
19 port = 12341
19 port = 12341
20
20
21 @classmethod
21 @classmethod
22 def wait_until_alive(cls):
22 def wait_until_alive(cls):
23 """Wait for the server to be alive"""
23 """Wait for the server to be alive"""
24 url = 'http://localhost:%i/api/notebooks' % cls.port
24 url = 'http://localhost:%i/api/notebooks' % cls.port
25 while True:
25 while True:
26 try:
26 try:
27 requests.get(url)
27 requests.get(url)
28 except requests.exceptions.ConnectionError:
28 except requests.exceptions.ConnectionError:
29 time.sleep(.1)
29 time.sleep(.1)
30 else:
30 else:
31 break
31 break
32
32
33 @classmethod
33 @classmethod
34 def wait_until_dead(cls):
34 def wait_until_dead(cls):
35 """Wait for the server to stop getting requests after shutdown"""
35 """Wait for the server to stop getting requests after shutdown"""
36 url = 'http://localhost:%i/api/notebooks' % cls.port
36 url = 'http://localhost:%i/api/notebooks' % cls.port
37 while True:
37 while True:
38 try:
38 try:
39 requests.get(url)
39 requests.get(url)
40 except requests.exceptions.ConnectionError:
40 except requests.exceptions.ConnectionError:
41 break
41 break
42 else:
42 else:
43 time.sleep(.1)
43 time.sleep(.1)
44
44
45 @classmethod
45 @classmethod
46 def setup_class(cls):
46 def setup_class(cls):
47 cls.ipython_dir = TemporaryDirectory()
47 cls.ipython_dir = TemporaryDirectory()
48 cls.notebook_dir = TemporaryDirectory()
48 cls.notebook_dir = TemporaryDirectory()
49 notebook_args = [
49 notebook_args = [
50 sys.executable, '-c',
50 sys.executable, '-c',
51 'from IPython.html.notebookapp import launch_new_instance; launch_new_instance()',
51 'from IPython.html.notebookapp import launch_new_instance; launch_new_instance()',
52 '--port=%d' % cls.port,
52 '--port=%d' % cls.port,
53 '--no-browser',
53 '--no-browser',
54 '--ipython-dir=%s' % cls.ipython_dir.name,
54 '--ipython-dir=%s' % cls.ipython_dir.name,
55 '--notebook-dir=%s' % cls.notebook_dir.name
55 '--notebook-dir=%s' % cls.notebook_dir.name
56 ]
56 ]
57 cls.notebook = Popen(notebook_args, stdout=PIPE, stderr=PIPE)
57 cls.notebook = Popen(notebook_args, stdout=PIPE, stderr=PIPE)
58 cls.wait_until_alive()
58 cls.wait_until_alive()
59
59
60 @classmethod
60 @classmethod
61 def teardown_class(cls):
61 def teardown_class(cls):
62 cls.notebook.terminate()
62 cls.notebook.terminate()
63 cls.ipython_dir.cleanup()
63 cls.ipython_dir.cleanup()
64 cls.notebook_dir.cleanup()
64 cls.notebook_dir.cleanup()
65 cls.wait_until_dead()
65 cls.wait_until_dead()
66
66
67 @classmethod
67 @classmethod
68 def base_url(cls):
68 def base_url(cls):
69 return 'http://localhost:%i/' % cls.port
69 return 'http://localhost:%i/' % cls.port
70
70
71
71
72 @contextmanager
72 @contextmanager
73 def assert_http_error(status):
73 def assert_http_error(status, msg=None):
74 try:
74 try:
75 yield
75 yield
76 except requests.HTTPError as e:
76 except requests.HTTPError as e:
77 real_status = e.response.status_code
77 real_status = e.response.status_code
78 assert real_status == status, \
78 assert real_status == status, \
79 "Expected status %d, got %d" % (real_status, status)
79 "Expected status %d, got %d" % (real_status, status)
80 if msg:
81 assert msg in str(e), e
80 else:
82 else:
81 assert False, "Expected HTTP error status" No newline at end of file
83 assert False, "Expected HTTP error status"
General Comments 0
You need to be logged in to leave comments. Login now