22import sys
33import time
44import pytest
5- import functools
65from traitlets import TraitError
76from tornado .web import HTTPError
87from itertools import combinations
98
10-
119from nbformat import v4 as nbformat
1210
1311from jupyter_server .services .contents .filemanager import AsyncFileContentsManager , FileContentsManager
1412from jupyter_server .utils import ensure_async
1513from ...utils import expected_http_error
1614
15+
1716@pytest .fixture (params = [(FileContentsManager , True ),
1817 (FileContentsManager , False ),
1918 (AsyncFileContentsManager , True ),
@@ -29,6 +28,7 @@ def file_contents_manager_class(request, tmp_path):
2928
3029# -------------- Functions ----------------------------
3130
31+
3232def _make_dir (jp_contents_manager , api_path ):
3333 """
3434 Make a directory.
@@ -99,6 +99,7 @@ async def check_populated_dir_files(jp_contents_manager, api_path):
9999
100100# ----------------- Tests ----------------------------------
101101
102+
102103def test_root_dir (file_contents_manager_class , tmp_path ):
103104 fm = file_contents_manager_class (root_dir = str (tmp_path ))
104105 assert fm .root_dir == str (tmp_path )
@@ -116,6 +117,7 @@ def test_invalid_root_dir(file_contents_manager_class, tmp_path):
116117 with pytest .raises (TraitError ):
117118 file_contents_manager_class (root_dir = str (temp_file ))
118119
120+
119121def test_get_os_path (file_contents_manager_class , tmp_path ):
120122 fm = file_contents_manager_class (root_dir = str (tmp_path ))
121123 path = fm ._get_os_path ('/path/to/notebook/test.ipynb' )
@@ -146,10 +148,6 @@ def test_checkpoint_subdir(file_contents_manager_class, tmp_path):
146148 assert cp_dir == os .path .join (str (tmp_path ), cpm .checkpoint_dir , cp_name )
147149
148150
149- @pytest .mark .skipif (
150- sys .platform == 'win32' and sys .version_info [0 ] < 3 ,
151- reason = "System platform is Windows, version < 3"
152- )
153151async def test_bad_symlink (file_contents_manager_class , tmp_path ):
154152 td = str (tmp_path )
155153
@@ -172,9 +170,31 @@ async def test_bad_symlink(file_contents_manager_class, tmp_path):
172170
173171
174172@pytest .mark .skipif (
175- sys .platform == 'win32' and sys . version_info [ 0 ] < 3 ,
176- reason = "System platform is Windows, version < 3 "
173+ sys .platform . startswith ( 'win' ) ,
174+ reason = "Windows doesn't detect symlink loops "
177175)
176+ async def test_recursive_symlink (file_contents_manager_class , tmp_path ):
177+ td = str (tmp_path )
178+
179+ cm = file_contents_manager_class (root_dir = td )
180+ path = 'test recursive symlink'
181+ _make_dir (cm , path )
182+
183+ file_model = await ensure_async (cm .new_untitled (path = path , ext = '.txt' ))
184+
185+ # create recursive symlink
186+ symlink (cm , '%s/%s' % (path , "recursive" ), '%s/%s' % (path , "recursive" ))
187+ model = await ensure_async (cm .get (path ))
188+
189+ contents = {
190+ content ['name' ]: content for content in model ['content' ]
191+ }
192+ assert 'untitled.txt' in contents
193+ assert contents ['untitled.txt' ] == file_model
194+ # recursive symlinks should not be shown in the contents manager
195+ assert 'recursive' not in contents
196+
197+
178198async def test_good_symlink (file_contents_manager_class , tmp_path ):
179199 td = str (tmp_path )
180200 cm = file_contents_manager_class (root_dir = td )
@@ -213,6 +233,7 @@ async def test_403(file_contents_manager_class, tmp_path):
213233 except HTTPError as e :
214234 assert e .status_code == 403
215235
236+
216237async def test_escape_root (file_contents_manager_class , tmp_path ):
217238 td = str (tmp_path )
218239 cm = file_contents_manager_class (root_dir = td )
0 commit comments