Skip to content

Commit 91ba5d7

Browse files
authored
+tests, increased coverage (#7)
* provisioning_api: edit group tests * exposed `check_error` to use * polished UsersGroupsAPI --------- Signed-off-by: Alexander Piskun <bigcat88@icloud.com>
1 parent b1bf6b0 commit 91ba5d7

File tree

8 files changed

+167
-50
lines changed

8 files changed

+167
-50
lines changed

.readthedocs.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
version: 2
2+
3+
build:
4+
os: ubuntu-22.04
5+
tools:
6+
python: "3.10"
7+
8+
python:
9+
install:
10+
- method: pip
11+
path: .
12+
extra_requirements:
13+
- docs

nc_py_api/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"""
44

55
from .constants import ApiScope, LogLvl
6-
from .exceptions import NextcloudException
6+
from .exceptions import NextcloudException, check_error
77
from .files import FsNodeInfo
88
from .integration_fastapi import nc_app, set_enabled_handler, set_scopes
99
from .nextcloud import Nextcloud, NextcloudApp

nc_py_api/exceptions.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,17 @@ def __str__(self):
2020
return f"[{self.status_code}]{reason}{info}"
2121

2222

23-
def check_error(code: int, info: str):
23+
def check_error(code: int, info: str = ""):
24+
if 996 <= code <= 999:
25+
if code == 996:
26+
phrase = "Server error"
27+
elif code == 997:
28+
phrase = "Unauthorised"
29+
elif code == 998:
30+
phrase = "Not found"
31+
else:
32+
phrase = "Unknown error"
33+
raise NextcloudException(code, reason=phrase, info=info)
2434
if not codes.is_error(code):
2535
return
2636
raise NextcloudException(code, reason=codes(code).phrase, info=info)

nc_py_api/users_groups.py

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,66 @@
22
Nextcloud API for working with user groups.
33
"""
44

5-
from typing import Optional
5+
from typing import Optional, TypedDict
66

77
from ._session import NcSessionBasic
88
from .misc import kwargs_to_dict
99

1010
ENDPOINT = "/ocs/v1.php/cloud/groups"
1111

1212

13+
class GroupDetails(TypedDict):
14+
id: str
15+
display_name: str
16+
user_count: int
17+
disabled: bool
18+
can_add: bool
19+
can_remove: bool
20+
21+
1322
class UsersGroupsAPI:
1423
def __init__(self, session: NcSessionBasic):
1524
self._session = session
1625

17-
def list(self, mask: Optional[str] = None, limit: Optional[int] = None, offset: Optional[int] = None) -> dict:
26+
def get(self, mask: Optional[str] = None, limit: Optional[int] = None, offset: Optional[int] = None) -> list[str]:
1827
data = kwargs_to_dict(["search", "limit", "offset"], search=mask, limit=limit, offset=offset)
1928
response_data = self._session.ocs(method="GET", path=ENDPOINT, params=data)
20-
return response_data["groups"] if response_data else {}
29+
return response_data["groups"] if response_data else []
2130

22-
def create(self, group_id: str) -> dict:
23-
return self._session.ocs(method="POST", path=f"{ENDPOINT}", params={"groupid": group_id})
31+
def get_details(
32+
self, mask: Optional[str] = None, limit: Optional[int] = None, offset: Optional[int] = None
33+
) -> list[GroupDetails]:
34+
data = kwargs_to_dict(["search", "limit", "offset"], search=mask, limit=limit, offset=offset)
35+
response_data = self._session.ocs(method="GET", path=f"{ENDPOINT}/details", params=data)
36+
return [self._to_group_details(i) for i in response_data["groups"]] if response_data else []
2437

25-
def edit(self, group_id: str, **kwargs) -> dict:
26-
return self._session.ocs(method="PUT", path=f"{ENDPOINT}/{group_id}", params={**kwargs})
38+
def create(self, group_id: str, display_name: Optional[str] = None) -> None:
39+
params = {"groupid": group_id}
40+
if display_name is not None:
41+
params["displayname"] = display_name
42+
self._session.ocs(method="POST", path=f"{ENDPOINT}", params=params)
2743

28-
def delete(self, group_id: str) -> dict:
29-
return self._session.ocs(method="DELETE", path=f"{ENDPOINT}/{group_id}")
44+
def edit(self, group_id: str, display_name: str) -> None:
45+
params = {"key": "displayname", "value": display_name}
46+
self._session.ocs(method="PUT", path=f"{ENDPOINT}/{group_id}", params=params)
47+
48+
def delete(self, group_id: str) -> None:
49+
self._session.ocs(method="DELETE", path=f"{ENDPOINT}/{group_id}")
3050

3151
def get_members(self, group_id: str) -> dict:
3252
response_data = self._session.ocs(method="GET", path=f"{ENDPOINT}/{group_id}")
3353
return response_data["users"] if response_data else {}
3454

3555
def get_subadmins(self, group_id: str) -> dict:
3656
return self._session.ocs(method="GET", path=f"{ENDPOINT}/{group_id}/subadmins")
57+
58+
@staticmethod
59+
def _to_group_details(reply: dict) -> GroupDetails:
60+
return {
61+
"id": reply["id"],
62+
"display_name": reply["displayname"],
63+
"user_count": reply["usercount"],
64+
"disabled": bool(reply["disabled"]),
65+
"can_add": reply["canAdd"],
66+
"can_remove": reply["canRemove"],
67+
}

nc_py_api/users_statuses.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,15 @@ def get(self, user_id: str) -> Optional[UserStatus]:
6464
raise e from None
6565

6666
def get_predefined(self) -> list[PredefinedStatus]:
67-
require_capabilities("user_status", self._session.capabilities)
6867
if self._session.nc_version["major"] < 27:
6968
return []
69+
require_capabilities("user_status", self._session.capabilities)
7070
return self._session.ocs(method="GET", path=f"{ENDPOINT}/predefined_statuses")
7171

7272
def set_predefined(self, message_id: str, clear_at: int = 0) -> None:
73-
require_capabilities("user_status", self._session.capabilities)
7473
if self._session.nc_version["major"] < 27:
7574
return
75+
require_capabilities("user_status", self._session.capabilities)
7676
params: dict[str, Union[int, str]] = {"messageId": message_id}
7777
if clear_at:
7878
params["clearAt"] = clear_at

tests/groups_test.py

Lines changed: 66 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,46 +4,93 @@
44

55
from gfixture import NC_TO_TEST
66

7-
TEST_GROUP_NAME = "test_coverage_group"
7+
TEST_GROUP_NAME = "test_coverage_group1"
8+
TEST_GROUP_NAME2 = "test_coverage_group2"
89

910

1011
@pytest.mark.skipif(not isinstance(NC_TO_TEST[:1][0], Nextcloud), reason="Not available for NextcloudApp.")
1112
@pytest.mark.parametrize("nc", NC_TO_TEST[:1])
12-
def test_create_delete_group(nc):
13+
@pytest.mark.parametrize("params", ((TEST_GROUP_NAME, ), (TEST_GROUP_NAME, "display name")))
14+
def test_create_delete_group(nc, params):
15+
test_group_name = params[0]
1316
try:
14-
nc.users_groups.delete(TEST_GROUP_NAME)
17+
nc.users_groups.delete(test_group_name)
1518
except NextcloudException:
1619
pass
17-
nc.users_groups.create(TEST_GROUP_NAME)
20+
nc.users_groups.create(*params)
1821
with pytest.raises(NextcloudException):
19-
nc.users_groups.create(TEST_GROUP_NAME)
20-
nc.users_groups.delete(TEST_GROUP_NAME)
22+
nc.users_groups.create(*params)
23+
nc.users_groups.delete(test_group_name)
2124
with pytest.raises(NextcloudException):
22-
nc.users_groups.delete(TEST_GROUP_NAME)
25+
nc.users_groups.delete(test_group_name)
26+
27+
28+
@pytest.mark.skipif(not isinstance(NC_TO_TEST[:1][0], Nextcloud), reason="Not available for NextcloudApp.")
29+
@pytest.mark.parametrize("nc", NC_TO_TEST[:1])
30+
def test_get_group(nc):
31+
for i in (TEST_GROUP_NAME, TEST_GROUP_NAME2):
32+
try:
33+
nc.users_groups.create(i)
34+
except NextcloudException:
35+
pass
36+
groups = nc.users_groups.get()
37+
assert isinstance(groups, list)
38+
assert len(groups) >= 2
39+
assert TEST_GROUP_NAME in groups
40+
assert TEST_GROUP_NAME2 in groups
41+
groups = nc.users_groups.get(mask=TEST_GROUP_NAME)
42+
assert len(groups) == 1
43+
groups = nc.users_groups.get(limit=1)
44+
assert len(groups) == 1
45+
assert groups[0] != nc.users_groups.get(limit=1, offset=1)[0]
46+
nc.users_groups.delete(TEST_GROUP_NAME)
47+
nc.users_groups.delete(TEST_GROUP_NAME2)
48+
49+
50+
@pytest.mark.skipif(not isinstance(NC_TO_TEST[:1][0], Nextcloud), reason="Not available for NextcloudApp.")
51+
@pytest.mark.parametrize("nc", NC_TO_TEST[:1])
52+
def test_get_non_existing_group(nc):
53+
groups = nc.users_groups.get(mask="Such group should not be present")
54+
assert isinstance(groups, list)
55+
assert not groups
2356

2457

2558
@pytest.mark.skipif(not isinstance(NC_TO_TEST[:1][0], Nextcloud), reason="Not available for NextcloudApp.")
2659
@pytest.mark.parametrize("nc", NC_TO_TEST[:1])
27-
def test_list_group(nc):
60+
def test_get_group_details(nc):
2861
try:
29-
nc.users_groups.create(TEST_GROUP_NAME)
62+
nc.users_groups.delete(TEST_GROUP_NAME)
3063
except NextcloudException:
3164
pass
3265
try:
33-
nc.users_groups.create(TEST_GROUP_NAME + "2")
66+
nc.users_groups.create(TEST_GROUP_NAME)
3467
except NextcloudException:
3568
pass
36-
groups = nc.users_groups.list()
37-
assert len(groups) >= 2
38-
assert TEST_GROUP_NAME in groups
39-
assert TEST_GROUP_NAME + "2" in groups
40-
groups = nc.users_groups.list(mask=TEST_GROUP_NAME)
41-
assert len(groups) == 2
42-
groups = nc.users_groups.list(limit=1)
69+
groups = nc.users_groups.get_details(mask=TEST_GROUP_NAME)
4370
assert len(groups) == 1
44-
assert groups[0] != nc.users_groups.list(limit=1, offset=1)[0]
71+
group = groups[0]
72+
assert group["id"] == TEST_GROUP_NAME
73+
assert group["display_name"] == TEST_GROUP_NAME
74+
assert not group["disabled"]
75+
assert isinstance(group["user_count"], int)
76+
assert isinstance(group["can_add"], bool)
77+
assert isinstance(group["can_remove"], bool)
78+
nc.users_groups.delete(TEST_GROUP_NAME)
79+
80+
81+
@pytest.mark.skipif(not isinstance(NC_TO_TEST[:1][0], Nextcloud), reason="Not available for NextcloudApp.")
82+
@pytest.mark.parametrize("nc", NC_TO_TEST[:1])
83+
def test_group_edit(nc):
84+
try:
85+
nc.users_groups.create(TEST_GROUP_NAME)
86+
except NextcloudException:
87+
pass
88+
nc.users_groups.edit(TEST_GROUP_NAME, display_name="earth people")
89+
assert nc.users_groups.get_details(mask=TEST_GROUP_NAME)[0]["display_name"] == "earth people"
4590
nc.users_groups.delete(TEST_GROUP_NAME)
46-
nc.users_groups.delete(TEST_GROUP_NAME + "2")
91+
with pytest.raises(NextcloudException) as exc_info:
92+
nc.users_groups.edit(TEST_GROUP_NAME, display_name="earth people")
93+
assert exc_info.value.status_code == 996
4794

4895

4996
@pytest.mark.skipif(not isinstance(NC_TO_TEST[:1][0], Nextcloud), reason="Not available for NextcloudApp.")

tests/misc_test.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import pytest
2+
3+
from nc_py_api import check_error, NextcloudException
4+
5+
6+
@pytest.mark.parametrize("code", (995, 996, 997, 998, 999, 1000))
7+
def test_check_error(code):
8+
if 996 <= code <= 999:
9+
with pytest.raises(NextcloudException):
10+
check_error(code)
11+
else:
12+
check_error(code)

tests/users_statuses_test.py

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from time import time
44

55

6-
from gfixture import NC_TO_TEST, NC_VERSION
6+
from gfixture import NC_TO_TEST
77

88

99
@pytest.mark.parametrize("nc", NC_TO_TEST)
@@ -36,17 +36,19 @@ def test_get_status(nc, message):
3636
assert not r1["messageIsPredefined"]
3737

3838

39-
@pytest.mark.skipif(NC_VERSION.get("major", 0) < 27, reason="NC27 required.")
4039
@pytest.mark.parametrize("nc", NC_TO_TEST)
4140
def test_get_predefined(nc):
4241
r = nc.users_statuses.get_predefined()
43-
assert isinstance(r, list)
44-
assert r
45-
for i in r:
46-
assert isinstance(i["id"], str)
47-
assert isinstance(i["message"], str)
48-
assert isinstance(i["icon"], str)
49-
assert isinstance(i["clearAt"], dict) or i["clearAt"] is None
42+
if nc.srv_version["major"] < 27:
43+
assert r == []
44+
else:
45+
assert isinstance(r, list)
46+
assert r
47+
for i in r:
48+
assert isinstance(i["id"], str)
49+
assert isinstance(i["message"], str)
50+
assert isinstance(i["icon"], str)
51+
assert isinstance(i["clearAt"], dict) or i["clearAt"] is None
5052

5153

5254
@pytest.mark.parametrize("nc", NC_TO_TEST)
@@ -89,15 +91,17 @@ def test_set_status_type(nc, value):
8991
assert r["statusIsUserDefined"]
9092

9193

92-
@pytest.mark.skipif(NC_VERSION.get("major", 0) < 27, reason="NC27 required.")
9394
@pytest.mark.parametrize("nc", NC_TO_TEST)
9495
@pytest.mark.parametrize("clear_at", (None, int(time()) + 360))
9596
def test_set_predefined(nc, clear_at):
96-
predefined_statuses = nc.users_statuses.get_predefined()
97-
for i in predefined_statuses:
98-
nc.users_statuses.set_predefined(i["id"], clear_at)
99-
r = nc.users_statuses.get_current()
100-
assert r["message"] == i["message"]
101-
assert r["messageId"] == i["id"]
102-
assert r["messageIsPredefined"]
103-
assert r["clearAt"] == clear_at
97+
if nc.srv_version["major"] < 27:
98+
nc.users_statuses.set_predefined("meeting")
99+
else:
100+
predefined_statuses = nc.users_statuses.get_predefined()
101+
for i in predefined_statuses:
102+
nc.users_statuses.set_predefined(i["id"], clear_at)
103+
r = nc.users_statuses.get_current()
104+
assert r["message"] == i["message"]
105+
assert r["messageId"] == i["id"]
106+
assert r["messageIsPredefined"]
107+
assert r["clearAt"] == clear_at

0 commit comments

Comments
 (0)