|
|
"""Test the sessions web service API."""
|
|
|
|
|
|
import errno
|
|
|
import io
|
|
|
import os
|
|
|
import json
|
|
|
import requests
|
|
|
import shutil
|
|
|
import time
|
|
|
|
|
|
pjoin = os.path.join
|
|
|
|
|
|
from IPython.html.utils import url_path_join
|
|
|
from IPython.html.tests.launchnotebook import NotebookTestBase, assert_http_error
|
|
|
from IPython.nbformat.v4 import new_notebook
|
|
|
from IPython.nbformat import write
|
|
|
|
|
|
class SessionAPI(object):
|
|
|
"""Wrapper for notebook API calls."""
|
|
|
def __init__(self, base_url):
|
|
|
self.base_url = base_url
|
|
|
|
|
|
def _req(self, verb, path, body=None):
|
|
|
response = requests.request(verb,
|
|
|
url_path_join(self.base_url, 'api/sessions', 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 create(self, path, kernel_name='python'):
|
|
|
body = json.dumps({'notebook': {'path':path},
|
|
|
'kernel': {'name': kernel_name}})
|
|
|
return self._req('POST', '', body)
|
|
|
|
|
|
def modify(self, id, path):
|
|
|
body = json.dumps({'notebook': {'path':path}})
|
|
|
return self._req('PATCH', id, body)
|
|
|
|
|
|
def delete(self, id):
|
|
|
return self._req('DELETE', id)
|
|
|
|
|
|
class SessionAPITest(NotebookTestBase):
|
|
|
"""Test the sessions web service API"""
|
|
|
def setUp(self):
|
|
|
nbdir = self.notebook_dir.name
|
|
|
try:
|
|
|
os.mkdir(pjoin(nbdir, 'foo'))
|
|
|
except OSError as e:
|
|
|
# Deleting the folder in an earlier test may have failed
|
|
|
if e.errno != errno.EEXIST:
|
|
|
raise
|
|
|
|
|
|
with io.open(pjoin(nbdir, 'foo', 'nb1.ipynb'), 'w',
|
|
|
encoding='utf-8') as f:
|
|
|
nb = new_notebook()
|
|
|
write(nb, f, version=4)
|
|
|
|
|
|
self.sess_api = SessionAPI(self.base_url())
|
|
|
|
|
|
def tearDown(self):
|
|
|
for session in self.sess_api.list().json():
|
|
|
self.sess_api.delete(session['id'])
|
|
|
# This is necessary in some situations on Windows: without it, it
|
|
|
# fails to delete the directory because something is still using it. I
|
|
|
# think there is a brief period after the kernel terminates where
|
|
|
# Windows still treats its working directory as in use. On my Windows
|
|
|
# VM, 0.01s is not long enough, but 0.1s appears to work reliably.
|
|
|
# -- TK, 15 December 2014
|
|
|
time.sleep(0.1)
|
|
|
|
|
|
shutil.rmtree(pjoin(self.notebook_dir.name, 'foo'),
|
|
|
ignore_errors=True)
|
|
|
|
|
|
def test_create(self):
|
|
|
sessions = self.sess_api.list().json()
|
|
|
self.assertEqual(len(sessions), 0)
|
|
|
|
|
|
resp = self.sess_api.create('foo/nb1.ipynb')
|
|
|
self.assertEqual(resp.status_code, 201)
|
|
|
newsession = resp.json()
|
|
|
self.assertIn('id', newsession)
|
|
|
self.assertEqual(newsession['notebook']['path'], 'foo/nb1.ipynb')
|
|
|
self.assertEqual(resp.headers['Location'], '/api/sessions/{0}'.format(newsession['id']))
|
|
|
|
|
|
sessions = self.sess_api.list().json()
|
|
|
self.assertEqual(sessions, [newsession])
|
|
|
|
|
|
# Retrieve it
|
|
|
sid = newsession['id']
|
|
|
got = self.sess_api.get(sid).json()
|
|
|
self.assertEqual(got, newsession)
|
|
|
|
|
|
def test_delete(self):
|
|
|
newsession = self.sess_api.create('foo/nb1.ipynb').json()
|
|
|
sid = newsession['id']
|
|
|
|
|
|
resp = self.sess_api.delete(sid)
|
|
|
self.assertEqual(resp.status_code, 204)
|
|
|
|
|
|
sessions = self.sess_api.list().json()
|
|
|
self.assertEqual(sessions, [])
|
|
|
|
|
|
with assert_http_error(404):
|
|
|
self.sess_api.get(sid)
|
|
|
|
|
|
def test_modify(self):
|
|
|
newsession = self.sess_api.create('foo/nb1.ipynb').json()
|
|
|
sid = newsession['id']
|
|
|
|
|
|
changed = self.sess_api.modify(sid, 'nb2.ipynb').json()
|
|
|
self.assertEqual(changed['id'], sid)
|
|
|
self.assertEqual(changed['notebook']['path'], 'nb2.ipynb')
|
|
|
|