Skip to content

Commit 5d7213b

Browse files
authored
more tests, docs. (#60)
1 parent 5f0f41f commit 5d7213b

File tree

9 files changed

+181
-31
lines changed

9 files changed

+181
-31
lines changed

nc_py_api/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
fs_filter_by,
1414
fs_list_directory,
1515
fs_node_info,
16+
fs_nodes_info,
1617
fs_sort_by_id,
1718
)
1819
from .log import cpa_logger

nc_py_api/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
""" Version of Nc_Py_Api """
22

3-
__version__ = "0.0.10"
3+
__version__ = "0.0.11.dev0"

nc_py_api/files.py

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,18 +52,15 @@ class FsNodeInfo(TypedDict):
5252
"""A value from the config that defines the maximum file size allowed to be requested from php."""
5353

5454

55-
def fs_node_info(obj: Union[list[int], int, str], user_id=USER_ID) -> Union[list[FsNodeInfo], Optional[FsNodeInfo]]:
56-
"""Gets `FsNodeInfo` by list of ids, id or path.
55+
def fs_node_info(obj: Union[int, str], user_id=USER_ID) -> Optional[FsNodeInfo]:
56+
"""Gets `FsNodeInfo` by `fileid` or path.
5757
58-
:param obj: for the list of ints or one int it is a `fileid` value. For ``str`` type it is the
59-
relative path to file/directory. `path` field from NC DB, without `files/` prefix.
58+
:param obj: `fileid` value or ``str`` type if it is the
59+
relative path to file/directory(`path` field from NC DB, without `files/` prefix).
6060
:param user_id: `uid` of user. Optional, in most cases you should not specify it.
6161
62-
:returns: list of :py:data:`FsNodeInfo`, :py:data:`FsNodeInfo` or None in case of error.
63-
Depends on the type of `obj` parameter."""
62+
:returns: :py:data:`FsNodeInfo` or None in case of error."""
6463

65-
if isinstance(obj, list):
66-
return [db_record_to_fs_node(i) for i in get_fileids_info(obj)]
6764
if isinstance(obj, int):
6865
raw_result = get_fileid_info(obj)
6966
else:
@@ -77,6 +74,16 @@ def fs_node_info(obj: Union[list[int], int, str], user_id=USER_ID) -> Union[list
7774
return None
7875

7976

77+
def fs_nodes_info(file_ids: list[int]) -> list[FsNodeInfo]:
78+
"""Gets list of `FsNodeInfo` by list of file ids.
79+
80+
:param file_ids: list of `fileid`s for which get info.
81+
82+
:returns: list of :py:data:`FsNodeInfo`."""
83+
84+
return [db_record_to_fs_node(i) for i in get_fileids_info(file_ids)]
85+
86+
8087
def fs_list_directory(file_id: Optional[Union[int, FsNodeInfo]] = None, user_id=USER_ID) -> list[FsNodeInfo]:
8188
"""Gets listing of the directory.
8289
@@ -107,7 +114,11 @@ def fs_list_directory(file_id: Optional[Union[int, FsNodeInfo]] = None, user_id=
107114

108115

109116
def fs_apply_exclude_lists(fs_objs: list[FsNodeInfo], excl_file_ids: list[int], excl_mask: list[str]) -> None:
110-
"""Purge all records according to exclude_(mask/fileid) from `where_to_purge`(or from fs_records)."""
117+
"""Purge all records according to exclude_(mask/fileid).
118+
119+
:param fs_objs: list of :py:data:`FsNodeInfo` to which exclusion lists apply.
120+
:param excl_file_ids: list of int, representing exclude masks fileids.
121+
:param excl_mask: list of str, representing exclude masks. Comparison done using `fnmatch`."""
111122

112123
indexes_to_purge = []
113124
for index, fs_obj in enumerate(fs_objs):
@@ -120,6 +131,11 @@ def fs_apply_exclude_lists(fs_objs: list[FsNodeInfo], excl_file_ids: list[int],
120131

121132

122133
def fs_extract_sub_dirs(fs_objs: list[FsNodeInfo]) -> list[FsNodeInfo]:
134+
"""Extracts and return all records with ``mimetype`` equal to ``DIR``.
135+
136+
:param fs_objs: list of :py:data:`FsNodeInfo` from which extract directory records. Will be edite in place.
137+
:return: list of :py:data:`FsNodeInfo` that are directories."""
138+
123139
sub_dirs = []
124140
indexes_to_purge = []
125141
for index, fs_obj in enumerate(fs_objs):
@@ -132,13 +148,26 @@ def fs_extract_sub_dirs(fs_objs: list[FsNodeInfo]) -> list[FsNodeInfo]:
132148

133149

134150
def fs_apply_ignore_flags(fs_objs: list[FsNodeInfo]) -> None:
151+
"""Check for ``.noimage``/``.nomedia`` flag and removes all records with `mimetype` equal to ``IMAGE/VIDEO``.
152+
153+
The flag also will be removed. The list will be edited in place.
154+
155+
:param fs_objs: list of :py:data:`FsNodeInfo`."""
156+
135157
ignore_flag = any(fs_obj["name"] in (".noimage", ".nomedia") for fs_obj in fs_objs)
136158
if ignore_flag:
137159
fs_filter_by(fs_objs, "mimepart", [mimetype.IMAGE, mimetype.VIDEO], reverse_filter=True)
138160
fs_apply_exclude_lists(fs_objs, [], [".noimage", ".nomedia"])
139161

140162

141163
def fs_filter_by(fs_objs: list[FsNodeInfo], field: FsNodeInfoField, values: list, reverse_filter=False) -> None:
164+
"""Filter elements in FS list(in-place) by specified mask.
165+
166+
:param fs_objs: directory listing returned by :py:func:`~nc_py_api.fs_list_directory`.
167+
:param field: value from :py:data:`FsNodeInfoField` on which the filter will be applied.
168+
:param values: list of values to compare.
169+
:param reverse_filter: a boolean indicating that the entry should be filtered if the value is present."""
170+
142171
indexes_to_purge = []
143172
if reverse_filter:
144173
for index, fs_obj in enumerate(fs_objs):
@@ -153,6 +182,11 @@ def fs_filter_by(fs_objs: list[FsNodeInfo], field: FsNodeInfoField, values: list
153182

154183

155184
def fs_sort_by_id(fs_objs: list[FsNodeInfo]) -> list[FsNodeInfo]:
185+
"""Helper function, to sort list of `FsNodeInfo` in ascending order by `fileid`.
186+
187+
:param fs_objs: list of :py:data:`FsNodeInfo`.
188+
:return: list of :py:data:`FsNodeInfo`."""
189+
156190
return sorted(fs_objs, key=lambda i: i["id"])
157191

158192

nc_py_api/mimetype.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from .db_requests import get_mimetype_id
22

33
DIR = get_mimetype_id("httpd/unix-directory")
4+
AUDIO = get_mimetype_id("audio")
45
IMAGE = get_mimetype_id("image")
6+
TEXT = get_mimetype_id("text")
57
VIDEO = get_mimetype_id("video")

tests/nc_py_api/etc_test.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from unittest.mock import patch
2+
3+
import nc_py_api
4+
5+
6+
def test_remote_filesize_limit():
7+
assert nc_py_api.db_requests.get_non_direct_access_filesize_limit() == 512 * 1024 * 1024
8+
9+
10+
@patch("nc_py_api.db_requests.execute_fetchall", autospec=True)
11+
def test_no_remote_filesize_limit(mock_execute_fetchall):
12+
def execute_fetchall_side_effect(*args, **kwargs):
13+
query = str(args[0]).replace("remote_filesize_limit", "invalid_remote_filesize_limit")
14+
return nc_py_api.db_api.execute_fetchall(query, *args[1:], **kwargs)
15+
16+
mock_execute_fetchall.side_effect = execute_fetchall_side_effect
17+
assert nc_py_api.db_requests.get_non_direct_access_filesize_limit() == 256 * 1024 * 1024

tests/nc_py_api/fs_test.py

Lines changed: 117 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,21 @@
44
import pytest
55

66
from nc_py_api import (
7+
fs_apply_exclude_lists,
8+
fs_apply_ignore_flags,
9+
fs_extract_sub_dirs,
710
fs_file_data,
11+
fs_filter_by,
812
fs_list_directory,
913
fs_node_info,
14+
fs_nodes_info,
15+
fs_sort_by_id,
1016
get_mimetype_id,
1117
mimetype,
1218
)
1319

20+
INVALID_FILEID = 18446744073709551610
21+
1422

1523
@pytest.mark.parametrize("test_path", ["", "/"])
1624
@pytest.mark.parametrize("user_id", ["admin"])
@@ -20,11 +28,12 @@ def test_node_info_root(test_path, user_id):
2028
assert root_dir_info["mimetype"] == mimetype.DIR
2129
assert root_dir_info["is_dir"]
2230
assert root_dir_info["name"] == "files"
31+
assert root_dir_info["ownerName"] == user_id
2332

2433

2534
@pytest.mark.parametrize("test_path", ["Documents", "Photos", "Templates", "test_dir"])
2635
def test_node_info_path_dirs(test_path):
27-
dir_info = fs_node_info(test_path, user_id="admin")
36+
dir_info = fs_node_info(test_path)
2837
assert dir_info
2938
assert dir_info["mimetype"] == mimetype.DIR
3039
assert dir_info["is_dir"]
@@ -33,15 +42,15 @@ def test_node_info_path_dirs(test_path):
3342

3443
@pytest.mark.parametrize("test_path", ["test_dir/hopper.png", "/test_dir/复杂 目录 Í/empty_file.bin"])
3544
def test_node_info_path_files(test_path):
36-
file_info = fs_node_info(test_path, user_id="admin")
45+
file_info = fs_node_info(test_path)
3746
assert file_info
3847
assert not file_info["is_dir"]
3948
assert file_info["name"] == Path(test_path).parts[-1:][0]
4049

4150

4251
@pytest.mark.parametrize("test_path", ["/test_dir/empty_dir/", "test_dir/empty_dir/", "/test_dir/empty_dir"])
4352
def test_node_info_path_slashes(test_path):
44-
dir_info = fs_node_info(test_path, user_id="admin")
53+
dir_info = fs_node_info(test_path)
4554
assert dir_info
4655
assert dir_info["mimetype"] == mimetype.DIR
4756
assert dir_info["is_dir"]
@@ -50,14 +59,14 @@ def test_node_info_path_slashes(test_path):
5059

5160
@pytest.mark.parametrize("test_path", ["/test_dir/复杂 目录 Í/", "test_dir/复杂 目录 Í/", "/test_dir/复杂 目录 Í"])
5261
def test_node_info_path_diff_symbols(test_path):
53-
dir_info = fs_node_info(test_path, user_id="admin")
62+
dir_info = fs_node_info(test_path)
5463
assert dir_info
5564
assert dir_info["mimetype"] == mimetype.DIR
5665
assert dir_info["is_dir"]
5766
assert dir_info["name"] == "复杂 目录 Í"
5867

5968

60-
@pytest.mark.parametrize("test_path", ["*-1", "no path", "/no path", "no path/"])
69+
@pytest.mark.parametrize("test_path", ["*-1", "no path", "/no path", "no path/", INVALID_FILEID])
6170
@pytest.mark.parametrize("user_id", ["", None, "non_exist"])
6271
def test_node_info_invalid_input(test_path, user_id):
6372
logging.disable(logging.CRITICAL)
@@ -66,6 +75,14 @@ def test_node_info_invalid_input(test_path, user_id):
6675
assert path_info is None
6776

6877

78+
@pytest.mark.parametrize("test_ids", [[], [0], [INVALID_FILEID], [0, INVALID_FILEID]])
79+
def test_nodes_info_invalid_input(test_ids):
80+
logging.disable(logging.CRITICAL)
81+
path_info = fs_nodes_info(test_ids)
82+
logging.disable(logging.NOTSET)
83+
assert len(path_info) == 0
84+
85+
6986
@pytest.mark.parametrize("user_id", ["admin"])
7087
def test_list_directory_root(user_id):
7188
root_dir_listing = fs_list_directory(user_id=user_id)
@@ -77,7 +94,13 @@ def test_list_directory_root(user_id):
7794
assert any(fs_obj["name"] == "test_dir" for fs_obj in root_dir_listing)
7895

7996

80-
@pytest.mark.parametrize("file_id", [None, 0, 18446744073709551610])
97+
def test_list_directory_by_fileid():
98+
root_dir_listing = fs_list_directory()
99+
test_dir_id = [fs_obj["id"] for fs_obj in root_dir_listing if fs_obj["name"] == "test_dir"][0]
100+
assert fs_list_directory(test_dir_id) == fs_list_directory(fs_node_info("test_dir"))
101+
102+
103+
@pytest.mark.parametrize("file_id", [None, 0, INVALID_FILEID])
81104
@pytest.mark.parametrize("user_id", ["", None, "non_exist"])
82105
def test_list_directory_invalid_input(file_id, user_id):
83106
logging.disable(logging.CRITICAL)
@@ -88,46 +111,49 @@ def test_list_directory_invalid_input(file_id, user_id):
88111

89112

90113
def test_list_directory_test_dir():
91-
test_dir_listing = fs_list_directory(fs_node_info("test_dir", user_id="admin"), user_id="admin")
92-
assert len(test_dir_listing) == 4
114+
test_dir_listing = fs_list_directory(fs_node_info("test_dir"))
115+
n = len(test_dir_listing)
116+
assert n == 5
93117
assert any(fs_obj["name"] == "empty_dir" for fs_obj in test_dir_listing)
94118
assert any(fs_obj["name"] == "复杂 目录 Í" for fs_obj in test_dir_listing)
95119
assert any(fs_obj["name"] == "hopper.png" for fs_obj in test_dir_listing)
96120
assert any(fs_obj["name"] == "test.txt" for fs_obj in test_dir_listing)
121+
assert any(fs_obj["name"] == "відео та картинки" for fs_obj in test_dir_listing)
97122

98123

99124
@pytest.mark.parametrize("test_path", ["test_dir", "test_dir/hopper.png", "/test_dir/复杂 目录 Í/empty_file.bin"])
100125
def test_node_info_id(test_path):
101-
file_info1 = fs_node_info(test_path, user_id="admin")
126+
file_info1 = fs_node_info(test_path)
102127
file_info2 = fs_node_info(file_info1["id"])
103128
assert isinstance(file_info1, dict)
104129
assert file_info1 == file_info2
105130

106131

107-
def test_node_info_ids():
108-
test_dir_listing = fs_list_directory(fs_node_info("test_dir", user_id="admin"), user_id="admin")
132+
def test_nodes_info_ids():
133+
test_dir_listing = fs_list_directory(fs_node_info("test_dir"))
109134
ids = [i["id"] for i in test_dir_listing]
110-
objs_info = fs_node_info(ids)
135+
objs_info = fs_nodes_info(ids)
111136
assert isinstance(objs_info, list)
112-
assert len(objs_info) == 4
137+
n = len(objs_info)
138+
assert n == 5
113139
assert all(obj["id"] for obj in objs_info)
114140

115141

116142
def test_parent_field():
117-
dir_info = fs_node_info("test_dir/empty_dir", user_id="admin")
143+
dir_info = fs_node_info("test_dir/empty_dir")
118144
parent_info = fs_node_info(dir_info["parent_id"])
119145
assert parent_info["name"] == "test_dir"
120-
dir_info = fs_node_info("test_dir/hopper.png", user_id="admin")
146+
dir_info = fs_node_info("test_dir/hopper.png")
121147
parent_info = fs_node_info(dir_info["parent_id"])
122148
assert parent_info["name"] == "test_dir"
123-
dir_info = fs_node_info("test_dir/empty_dir/empty_file.bin", user_id="admin")
149+
dir_info = fs_node_info("test_dir/empty_dir/empty_file.bin")
124150
parent_info = fs_node_info(dir_info["parent_id"])
125151
assert parent_info["name"] == "empty_dir"
126152

127153

128154
def test_node_dir_fields():
129-
dir_info = fs_node_info("test_dir/empty_dir", user_id="admin")
130-
_ = fs_list_directory(fs_node_info("test_dir", user_id="admin"), user_id="admin")
155+
dir_info = fs_node_info("test_dir/empty_dir")
156+
_ = fs_list_directory(fs_node_info("test_dir"))
131157
dir_info2 = [i for i in _ if i["name"] == "empty_dir"][0]
132158
assert dir_info == dir_info2
133159
assert dir_info["is_dir"]
@@ -141,8 +167,8 @@ def test_node_dir_fields():
141167

142168

143169
def test_node_file_fields():
144-
file_info = fs_node_info("test_dir/hopper.png", user_id="admin")
145-
_ = fs_list_directory(fs_node_info("test_dir", user_id="admin"), user_id="admin")
170+
file_info = fs_node_info("test_dir/hopper.png")
171+
_ = fs_list_directory(fs_node_info("test_dir"))
146172
file_info2 = [i for i in _ if i["name"] == "hopper.png"][0]
147173
assert file_info == file_info2
148174
assert not file_info["is_dir"]
@@ -157,10 +183,80 @@ def test_node_file_fields():
157183

158184
@pytest.mark.parametrize("file", ["test_dir/hopper.png", "test_dir/test.txt", "/test_dir/复杂 目录 Í/empty_file.bin"])
159185
def test_fs_file_data(file):
160-
node_info = fs_node_info(file, user_id="admin")
186+
node_info = fs_node_info(file)
161187
file_data = fs_file_data(node_info)
162188
assert isinstance(file_data, bytes)
163189
if file.find("empty") == -1:
164190
assert file_data
165191
else:
166192
assert not len(file_data)
193+
194+
195+
def test_fs_filter_by():
196+
def len_after_filter_by(field, values, reverse=False) -> int:
197+
_ = fs_list_directory(fs_node_info("test_dir"))
198+
fs_filter_by(_, field, values, reverse_filter=reverse)
199+
return len(_)
200+
201+
# mimepart
202+
assert len_after_filter_by("mimepart", [mimetype.IMAGE]) == 1
203+
assert len_after_filter_by("mimetype", [mimetype.DIR], True) == 2
204+
# is_dir
205+
assert len_after_filter_by("is_dir", [True]) == 3
206+
# is_local
207+
assert len_after_filter_by("is_local", [True]) == 5
208+
# name
209+
assert len_after_filter_by("name", ["test.txt"]) == 1
210+
assert len_after_filter_by("name", ["test.txt"], True) == 4
211+
# direct_access
212+
assert len_after_filter_by("direct_access", [True]) == 5
213+
214+
215+
def test_fs_apply_exclude_lists():
216+
dir_list = fs_list_directory()
217+
assert any(fs_obj["name"] == "test_dir" for fs_obj in dir_list)
218+
fileid = dir_list[0]["id"]
219+
ids = [i["id"] for i in dir_list]
220+
assert fileid in ids
221+
fs_apply_exclude_lists(dir_list, [fileid], [])
222+
ids = [i["id"] for i in dir_list]
223+
assert fileid not in ids
224+
assert any(fs_obj["name"] == "test_dir" for fs_obj in dir_list)
225+
fs_apply_exclude_lists(dir_list, [], ["test_dir"])
226+
assert not any(fs_obj["name"] == "test_dir" for fs_obj in dir_list)
227+
228+
229+
def test_fs_apply_ignore_flags():
230+
dir_list = fs_list_directory(fs_node_info("test_dir/відео та картинки"))
231+
assert len(dir_list) == 3
232+
fs_apply_ignore_flags(dir_list)
233+
assert not len(dir_list)
234+
235+
236+
def test_not_initialized_user_get_files_root_node():
237+
import nc_py_api
238+
239+
logging.disable(logging.CRITICAL)
240+
assert nc_py_api.files.get_files_root_node(user_id="user") is None
241+
logging.disable(logging.NOTSET)
242+
243+
244+
def test_fs_extract_sub_dirs():
245+
dir_list = fs_list_directory()
246+
assert any(fs_obj["is_dir"] for fs_obj in dir_list)
247+
sub_dirs = fs_extract_sub_dirs(dir_list)
248+
assert not any(fs_obj["is_dir"] for fs_obj in dir_list)
249+
assert all(fs_obj["is_dir"] for fs_obj in sub_dirs)
250+
dir_list = fs_list_directory(fs_node_info("test_dir/empty_dir"))
251+
sub_dirs = fs_extract_sub_dirs(dir_list)
252+
assert not sub_dirs
253+
254+
255+
def test_fs_sort_by_id():
256+
dir1_list = fs_list_directory()
257+
dir2_list = fs_list_directory(fs_node_info("test_dir"))
258+
dir3_list = fs_list_directory(fs_node_info("test_dir/відео та картинки"))
259+
united_dir_list = [*dir2_list, *dir3_list, *dir1_list]
260+
sorted_dir_list = fs_sort_by_id(united_dir_list)
261+
assert united_dir_list != sorted_dir_list
262+
assert sorted_dir_list == fs_sort_by_id(sorted_dir_list)

tests/nc_py_api/test_dir/відео та картинки/.noimage

Whitespace-only changes.
6.26 KB
Loading
3.21 KB
Loading

0 commit comments

Comments
 (0)