##// END OF EJS Templates
skip permission -> 403 test on Windows...
Min RK -
Show More
@@ -1,461 +1,464 b''
1 1 # coding: utf-8
2 2 """Tests for the notebook manager."""
3 3 from __future__ import print_function
4 4
5 5 import os
6 import sys
6 7 import time
7 8
8 9 from nose import SkipTest
9 10 from tornado.web import HTTPError
10 11 from unittest import TestCase
11 12 from tempfile import NamedTemporaryFile
12 13
13 14 from IPython.nbformat import v4 as nbformat
14 15
15 16 from IPython.utils.tempdir import TemporaryDirectory
16 17 from IPython.utils.traitlets import TraitError
17 18 from IPython.html.utils import url_path_join
18 19 from IPython.testing import decorators as dec
19 20
20 21 from ..filemanager import FileContentsManager
21 22
22 23
23 24 def _make_dir(contents_manager, api_path):
24 25 """
25 26 Make a directory.
26 27 """
27 28 os_path = contents_manager._get_os_path(api_path)
28 29 try:
29 30 os.makedirs(os_path)
30 31 except OSError:
31 32 print("Directory already exists: %r" % os_path)
32 33
33 34
34 35 class TestFileContentsManager(TestCase):
35 36
36 37 def symlink(self, contents_manager, src, dst):
37 38 """Make a symlink to src from dst
38 39
39 40 src and dst are api_paths
40 41 """
41 42 src_os_path = contents_manager._get_os_path(src)
42 43 dst_os_path = contents_manager._get_os_path(dst)
43 44 print(src_os_path, dst_os_path, os.path.isfile(src_os_path))
44 45 os.symlink(src_os_path, dst_os_path)
45 46
46 47 def test_root_dir(self):
47 48 with TemporaryDirectory() as td:
48 49 fm = FileContentsManager(root_dir=td)
49 50 self.assertEqual(fm.root_dir, td)
50 51
51 52 def test_missing_root_dir(self):
52 53 with TemporaryDirectory() as td:
53 54 root = os.path.join(td, 'notebook', 'dir', 'is', 'missing')
54 55 self.assertRaises(TraitError, FileContentsManager, root_dir=root)
55 56
56 57 def test_invalid_root_dir(self):
57 58 with NamedTemporaryFile() as tf:
58 59 self.assertRaises(TraitError, FileContentsManager, root_dir=tf.name)
59 60
60 61 def test_get_os_path(self):
61 62 # full filesystem path should be returned with correct operating system
62 63 # separators.
63 64 with TemporaryDirectory() as td:
64 65 root = td
65 66 fm = FileContentsManager(root_dir=root)
66 67 path = fm._get_os_path('/path/to/notebook/test.ipynb')
67 68 rel_path_list = '/path/to/notebook/test.ipynb'.split('/')
68 69 fs_path = os.path.join(fm.root_dir, *rel_path_list)
69 70 self.assertEqual(path, fs_path)
70 71
71 72 fm = FileContentsManager(root_dir=root)
72 73 path = fm._get_os_path('test.ipynb')
73 74 fs_path = os.path.join(fm.root_dir, 'test.ipynb')
74 75 self.assertEqual(path, fs_path)
75 76
76 77 fm = FileContentsManager(root_dir=root)
77 78 path = fm._get_os_path('////test.ipynb')
78 79 fs_path = os.path.join(fm.root_dir, 'test.ipynb')
79 80 self.assertEqual(path, fs_path)
80 81
81 82 def test_checkpoint_subdir(self):
82 83 subd = u'sub βˆ‚ir'
83 84 cp_name = 'test-cp.ipynb'
84 85 with TemporaryDirectory() as td:
85 86 root = td
86 87 os.mkdir(os.path.join(td, subd))
87 88 fm = FileContentsManager(root_dir=root)
88 89 cpm = fm.checkpoints
89 90 cp_dir = cpm.checkpoint_path(
90 91 'cp', 'test.ipynb'
91 92 )
92 93 cp_subdir = cpm.checkpoint_path(
93 94 'cp', '/%s/test.ipynb' % subd
94 95 )
95 96 self.assertNotEqual(cp_dir, cp_subdir)
96 97 self.assertEqual(cp_dir, os.path.join(root, cpm.checkpoint_dir, cp_name))
97 98 self.assertEqual(cp_subdir, os.path.join(root, subd, cpm.checkpoint_dir, cp_name))
98 99
99 100 @dec.skip_win32
100 101 def test_bad_symlink(self):
101 102 with TemporaryDirectory() as td:
102 103 cm = FileContentsManager(root_dir=td)
103 104 path = 'test bad symlink'
104 105 _make_dir(cm, path)
105 106
106 107 file_model = cm.new_untitled(path=path, ext='.txt')
107 108
108 109 # create a broken symlink
109 110 self.symlink(cm, "target", '%s/%s' % (path, 'bad symlink'))
110 111 model = cm.get(path)
111 112 self.assertEqual(model['content'], [file_model])
112 113
113 114 @dec.skip_win32
114 115 def test_good_symlink(self):
115 116 with TemporaryDirectory() as td:
116 117 cm = FileContentsManager(root_dir=td)
117 118 parent = 'test good symlink'
118 119 name = 'good symlink'
119 120 path = '{0}/{1}'.format(parent, name)
120 121 _make_dir(cm, parent)
121 122
122 123 file_model = cm.new(path=parent + '/zfoo.txt')
123 124
124 125 # create a good symlink
125 126 self.symlink(cm, file_model['path'], path)
126 127 symlink_model = cm.get(path, content=False)
127 128 dir_model = cm.get(parent)
128 129 self.assertEqual(
129 130 sorted(dir_model['content'], key=lambda x: x['name']),
130 131 [symlink_model, file_model],
131 132 )
132 133
133 134 def test_403(self):
134 135 if hasattr(os, 'getuid'):
135 136 if os.getuid() == 0:
136 137 raise SkipTest("Can't test permissions as root")
138 if sys.platform.startswith('win'):
139 raise SkipTest("Can't test permissions on Windows")
137 140
138 141 with TemporaryDirectory() as td:
139 142 cm = FileContentsManager(root_dir=td)
140 143 model = cm.new_untitled(type='file')
141 144 os_path = cm._get_os_path(model['path'])
142 145
143 146 os.chmod(os_path, 0o400)
144 147 try:
145 148 with cm.open(os_path, 'w') as f:
146 149 f.write(u"don't care")
147 150 except HTTPError as e:
148 151 self.assertEqual(e.status_code, 403)
149 152 else:
150 153 self.fail("Should have raised HTTPError(403)")
151 154
152 155
153 156 class TestContentsManager(TestCase):
154 157
155 158 def setUp(self):
156 159 self._temp_dir = TemporaryDirectory()
157 160 self.td = self._temp_dir.name
158 161 self.contents_manager = FileContentsManager(
159 162 root_dir=self.td,
160 163 )
161 164
162 165 def tearDown(self):
163 166 self._temp_dir.cleanup()
164 167
165 168 def make_dir(self, api_path):
166 169 """make a subdirectory at api_path
167 170
168 171 override in subclasses if contents are not on the filesystem.
169 172 """
170 173 _make_dir(self.contents_manager, api_path)
171 174
172 175 def add_code_cell(self, nb):
173 176 output = nbformat.new_output("display_data", {'application/javascript': "alert('hi');"})
174 177 cell = nbformat.new_code_cell("print('hi')", outputs=[output])
175 178 nb.cells.append(cell)
176 179
177 180 def new_notebook(self):
178 181 cm = self.contents_manager
179 182 model = cm.new_untitled(type='notebook')
180 183 name = model['name']
181 184 path = model['path']
182 185
183 186 full_model = cm.get(path)
184 187 nb = full_model['content']
185 188 nb['metadata']['counter'] = int(1e6 * time.time())
186 189 self.add_code_cell(nb)
187 190
188 191 cm.save(full_model, path)
189 192 return nb, name, path
190 193
191 194 def test_new_untitled(self):
192 195 cm = self.contents_manager
193 196 # Test in root directory
194 197 model = cm.new_untitled(type='notebook')
195 198 assert isinstance(model, dict)
196 199 self.assertIn('name', model)
197 200 self.assertIn('path', model)
198 201 self.assertIn('type', model)
199 202 self.assertEqual(model['type'], 'notebook')
200 203 self.assertEqual(model['name'], 'Untitled.ipynb')
201 204 self.assertEqual(model['path'], 'Untitled.ipynb')
202 205
203 206 # Test in sub-directory
204 207 model = cm.new_untitled(type='directory')
205 208 assert isinstance(model, dict)
206 209 self.assertIn('name', model)
207 210 self.assertIn('path', model)
208 211 self.assertIn('type', model)
209 212 self.assertEqual(model['type'], 'directory')
210 213 self.assertEqual(model['name'], 'Untitled Folder')
211 214 self.assertEqual(model['path'], 'Untitled Folder')
212 215 sub_dir = model['path']
213 216
214 217 model = cm.new_untitled(path=sub_dir)
215 218 assert isinstance(model, dict)
216 219 self.assertIn('name', model)
217 220 self.assertIn('path', model)
218 221 self.assertIn('type', model)
219 222 self.assertEqual(model['type'], 'file')
220 223 self.assertEqual(model['name'], 'untitled')
221 224 self.assertEqual(model['path'], '%s/untitled' % sub_dir)
222 225
223 226 def test_get(self):
224 227 cm = self.contents_manager
225 228 # Create a notebook
226 229 model = cm.new_untitled(type='notebook')
227 230 name = model['name']
228 231 path = model['path']
229 232
230 233 # Check that we 'get' on the notebook we just created
231 234 model2 = cm.get(path)
232 235 assert isinstance(model2, dict)
233 236 self.assertIn('name', model2)
234 237 self.assertIn('path', model2)
235 238 self.assertEqual(model['name'], name)
236 239 self.assertEqual(model['path'], path)
237 240
238 241 nb_as_file = cm.get(path, content=True, type='file')
239 242 self.assertEqual(nb_as_file['path'], path)
240 243 self.assertEqual(nb_as_file['type'], 'file')
241 244 self.assertEqual(nb_as_file['format'], 'text')
242 245 self.assertNotIsInstance(nb_as_file['content'], dict)
243 246
244 247 nb_as_bin_file = cm.get(path, content=True, type='file', format='base64')
245 248 self.assertEqual(nb_as_bin_file['format'], 'base64')
246 249
247 250 # Test in sub-directory
248 251 sub_dir = '/foo/'
249 252 self.make_dir('foo')
250 253 model = cm.new_untitled(path=sub_dir, ext='.ipynb')
251 254 model2 = cm.get(sub_dir + name)
252 255 assert isinstance(model2, dict)
253 256 self.assertIn('name', model2)
254 257 self.assertIn('path', model2)
255 258 self.assertIn('content', model2)
256 259 self.assertEqual(model2['name'], 'Untitled.ipynb')
257 260 self.assertEqual(model2['path'], '{0}/{1}'.format(sub_dir.strip('/'), name))
258 261
259 262 # Test with a regular file.
260 263 file_model_path = cm.new_untitled(path=sub_dir, ext='.txt')['path']
261 264 file_model = cm.get(file_model_path)
262 265 self.assertDictContainsSubset(
263 266 {
264 267 'content': u'',
265 268 'format': u'text',
266 269 'mimetype': u'text/plain',
267 270 'name': u'untitled.txt',
268 271 'path': u'foo/untitled.txt',
269 272 'type': u'file',
270 273 'writable': True,
271 274 },
272 275 file_model,
273 276 )
274 277 self.assertIn('created', file_model)
275 278 self.assertIn('last_modified', file_model)
276 279
277 280 # Test getting directory model
278 281
279 282 # Create a sub-sub directory to test getting directory contents with a
280 283 # subdir.
281 284 self.make_dir('foo/bar')
282 285 dirmodel = cm.get('foo')
283 286 self.assertEqual(dirmodel['type'], 'directory')
284 287 self.assertIsInstance(dirmodel['content'], list)
285 288 self.assertEqual(len(dirmodel['content']), 3)
286 289 self.assertEqual(dirmodel['path'], 'foo')
287 290 self.assertEqual(dirmodel['name'], 'foo')
288 291
289 292 # Directory contents should match the contents of each individual entry
290 293 # when requested with content=False.
291 294 model2_no_content = cm.get(sub_dir + name, content=False)
292 295 file_model_no_content = cm.get(u'foo/untitled.txt', content=False)
293 296 sub_sub_dir_no_content = cm.get('foo/bar', content=False)
294 297 self.assertEqual(sub_sub_dir_no_content['path'], 'foo/bar')
295 298 self.assertEqual(sub_sub_dir_no_content['name'], 'bar')
296 299
297 300 for entry in dirmodel['content']:
298 301 # Order isn't guaranteed by the spec, so this is a hacky way of
299 302 # verifying that all entries are matched.
300 303 if entry['path'] == sub_sub_dir_no_content['path']:
301 304 self.assertEqual(entry, sub_sub_dir_no_content)
302 305 elif entry['path'] == model2_no_content['path']:
303 306 self.assertEqual(entry, model2_no_content)
304 307 elif entry['path'] == file_model_no_content['path']:
305 308 self.assertEqual(entry, file_model_no_content)
306 309 else:
307 310 self.fail("Unexpected directory entry: %s" % entry())
308 311
309 312 with self.assertRaises(HTTPError):
310 313 cm.get('foo', type='file')
311 314
312 315 def test_update(self):
313 316 cm = self.contents_manager
314 317 # Create a notebook
315 318 model = cm.new_untitled(type='notebook')
316 319 name = model['name']
317 320 path = model['path']
318 321
319 322 # Change the name in the model for rename
320 323 model['path'] = 'test.ipynb'
321 324 model = cm.update(model, path)
322 325 assert isinstance(model, dict)
323 326 self.assertIn('name', model)
324 327 self.assertIn('path', model)
325 328 self.assertEqual(model['name'], 'test.ipynb')
326 329
327 330 # Make sure the old name is gone
328 331 self.assertRaises(HTTPError, cm.get, path)
329 332
330 333 # Test in sub-directory
331 334 # Create a directory and notebook in that directory
332 335 sub_dir = '/foo/'
333 336 self.make_dir('foo')
334 337 model = cm.new_untitled(path=sub_dir, type='notebook')
335 338 path = model['path']
336 339
337 340 # Change the name in the model for rename
338 341 d = path.rsplit('/', 1)[0]
339 342 new_path = model['path'] = d + '/test_in_sub.ipynb'
340 343 model = cm.update(model, path)
341 344 assert isinstance(model, dict)
342 345 self.assertIn('name', model)
343 346 self.assertIn('path', model)
344 347 self.assertEqual(model['name'], 'test_in_sub.ipynb')
345 348 self.assertEqual(model['path'], new_path)
346 349
347 350 # Make sure the old name is gone
348 351 self.assertRaises(HTTPError, cm.get, path)
349 352
350 353 def test_save(self):
351 354 cm = self.contents_manager
352 355 # Create a notebook
353 356 model = cm.new_untitled(type='notebook')
354 357 name = model['name']
355 358 path = model['path']
356 359
357 360 # Get the model with 'content'
358 361 full_model = cm.get(path)
359 362
360 363 # Save the notebook
361 364 model = cm.save(full_model, path)
362 365 assert isinstance(model, dict)
363 366 self.assertIn('name', model)
364 367 self.assertIn('path', model)
365 368 self.assertEqual(model['name'], name)
366 369 self.assertEqual(model['path'], path)
367 370
368 371 # Test in sub-directory
369 372 # Create a directory and notebook in that directory
370 373 sub_dir = '/foo/'
371 374 self.make_dir('foo')
372 375 model = cm.new_untitled(path=sub_dir, type='notebook')
373 376 name = model['name']
374 377 path = model['path']
375 378 model = cm.get(path)
376 379
377 380 # Change the name in the model for rename
378 381 model = cm.save(model, path)
379 382 assert isinstance(model, dict)
380 383 self.assertIn('name', model)
381 384 self.assertIn('path', model)
382 385 self.assertEqual(model['name'], 'Untitled.ipynb')
383 386 self.assertEqual(model['path'], 'foo/Untitled.ipynb')
384 387
385 388 def test_delete(self):
386 389 cm = self.contents_manager
387 390 # Create a notebook
388 391 nb, name, path = self.new_notebook()
389 392
390 393 # Delete the notebook
391 394 cm.delete(path)
392 395
393 396 # Check that deleting a non-existent path raises an error.
394 397 self.assertRaises(HTTPError, cm.delete, path)
395 398
396 399 # Check that a 'get' on the deleted notebook raises and error
397 400 self.assertRaises(HTTPError, cm.get, path)
398 401
399 402 def test_copy(self):
400 403 cm = self.contents_manager
401 404 parent = u'Γ₯ b'
402 405 name = u'nb √.ipynb'
403 406 path = u'{0}/{1}'.format(parent, name)
404 407 self.make_dir(parent)
405 408
406 409 orig = cm.new(path=path)
407 410 # copy with unspecified name
408 411 copy = cm.copy(path)
409 412 self.assertEqual(copy['name'], orig['name'].replace('.ipynb', '-Copy1.ipynb'))
410 413
411 414 # copy with specified name
412 415 copy2 = cm.copy(path, u'Γ₯ b/copy 2.ipynb')
413 416 self.assertEqual(copy2['name'], u'copy 2.ipynb')
414 417 self.assertEqual(copy2['path'], u'Γ₯ b/copy 2.ipynb')
415 418 # copy with specified path
416 419 copy2 = cm.copy(path, u'/')
417 420 self.assertEqual(copy2['name'], name)
418 421 self.assertEqual(copy2['path'], name)
419 422
420 423 def test_trust_notebook(self):
421 424 cm = self.contents_manager
422 425 nb, name, path = self.new_notebook()
423 426
424 427 untrusted = cm.get(path)['content']
425 428 assert not cm.notary.check_cells(untrusted)
426 429
427 430 # print(untrusted)
428 431 cm.trust_notebook(path)
429 432 trusted = cm.get(path)['content']
430 433 # print(trusted)
431 434 assert cm.notary.check_cells(trusted)
432 435
433 436 def test_mark_trusted_cells(self):
434 437 cm = self.contents_manager
435 438 nb, name, path = self.new_notebook()
436 439
437 440 cm.mark_trusted_cells(nb, path)
438 441 for cell in nb.cells:
439 442 if cell.cell_type == 'code':
440 443 assert not cell.metadata.trusted
441 444
442 445 cm.trust_notebook(path)
443 446 nb = cm.get(path)['content']
444 447 for cell in nb.cells:
445 448 if cell.cell_type == 'code':
446 449 assert cell.metadata.trusted
447 450
448 451 def test_check_and_sign(self):
449 452 cm = self.contents_manager
450 453 nb, name, path = self.new_notebook()
451 454
452 455 cm.mark_trusted_cells(nb, path)
453 456 cm.check_and_sign(nb, path)
454 457 assert not cm.notary.check_signature(nb)
455 458
456 459 cm.trust_notebook(path)
457 460 nb = cm.get(path)['content']
458 461 cm.mark_trusted_cells(nb, path)
459 462 cm.check_and_sign(nb, path)
460 463 assert cm.notary.check_signature(nb)
461 464
General Comments 0
You need to be logged in to leave comments. Login now