Skip to content

Commit 60eb8fc

Browse files
committed
Role management
Adding and deleting roles
1 parent 457aa9d commit 60eb8fc

File tree

10 files changed

+598
-0
lines changed

10 files changed

+598
-0
lines changed

tests/unit/manage/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License");
2+
# you may not use this file except in compliance with the License.
3+
# You may obtain a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS,
9+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
# See the License for the specific language governing permissions and
11+
# limitations under the License.

tests/unit/manage/test_forms.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License");
2+
# you may not use this file except in compliance with the License.
3+
# You may obtain a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS,
9+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
# See the License for the specific language governing permissions and
11+
# limitations under the License.
12+
13+
import pretend
14+
import pytest
15+
import wtforms
16+
17+
from webob.multidict import MultiDict
18+
19+
from warehouse.manage import forms
20+
21+
22+
class TestCreateRoleForm:
23+
24+
def test_creation(self):
25+
user_service = pretend.stub()
26+
form = forms.CreateRoleForm(user_service=user_service)
27+
28+
assert form.user_service is user_service
29+
30+
def test_validate_username_with_no_user(self):
31+
user_service = pretend.stub(
32+
find_userid=pretend.call_recorder(lambda userid: None),
33+
)
34+
form = forms.CreateRoleForm(user_service=user_service)
35+
field = pretend.stub(data="my_username")
36+
37+
with pytest.raises(wtforms.validators.ValidationError):
38+
form.validate_username(field)
39+
40+
assert user_service.find_userid.calls == [pretend.call("my_username")]
41+
42+
def test_validate_username_with_user(self):
43+
user_service = pretend.stub(
44+
find_userid=pretend.call_recorder(lambda userid: 1),
45+
)
46+
form = forms.CreateRoleForm(user_service=user_service)
47+
field = pretend.stub(data="my_username")
48+
49+
form.validate_username(field)
50+
51+
assert user_service.find_userid.calls == [pretend.call("my_username")]
52+
53+
@pytest.mark.parametrize(("value", "expected"), [
54+
("", "Must select a role"),
55+
("invalid", "Not a valid choice"),
56+
(None, "Not a valid choice"),
57+
])
58+
def test_validate_role_name_fails(self, value, expected):
59+
user_service = pretend.stub(
60+
find_userid=pretend.call_recorder(lambda userid: 1),
61+
)
62+
form = forms.CreateRoleForm(
63+
MultiDict({
64+
'role_name': value,
65+
'username': 'valid_username',
66+
}),
67+
user_service=user_service,
68+
)
69+
70+
assert not form.validate()
71+
assert form.role_name.errors == [expected]

tests/unit/manage/test_views.py

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,18 @@
1010
# See the License for the specific language governing permissions and
1111
# limitations under the License.
1212

13+
import uuid
14+
1315
import pretend
1416

17+
from pyramid.httpexceptions import HTTPSeeOther
18+
from webob.multidict import MultiDict
19+
1520
from warehouse.manage import views
21+
from warehouse.accounts.interfaces import IUserService
22+
from warehouse.packaging.models import Role
23+
24+
from ...common.db.packaging import ProjectFactory, RoleFactory, UserFactory
1625

1726

1827
class TestManageProfile:
@@ -40,3 +49,245 @@ def test_manage_project_settings(self):
4049
assert views.manage_project_settings(project, request) == {
4150
"project": project,
4251
}
52+
53+
54+
class TestManageProjectRoles:
55+
56+
def test_get_manage_project_roles(self, db_request):
57+
user_service = pretend.stub()
58+
db_request.find_service = pretend.call_recorder(
59+
lambda iface, context: user_service
60+
)
61+
form_obj = pretend.stub()
62+
form_class = pretend.call_recorder(lambda d, user_service: form_obj)
63+
64+
project = ProjectFactory.create(name="foobar")
65+
user = UserFactory.create()
66+
role = RoleFactory.create(user=user, project=project)
67+
68+
result = views.manage_project_roles(
69+
project, db_request, _form_class=form_class
70+
)
71+
72+
assert db_request.find_service.calls == [
73+
pretend.call(IUserService, context=None),
74+
]
75+
assert form_class.calls == [
76+
pretend.call(db_request.POST, user_service=user_service),
77+
]
78+
assert result == {
79+
"project": project,
80+
"roles": [role],
81+
"form": form_obj,
82+
}
83+
84+
def test_post_new_role_validation_fails(self, db_request):
85+
project = ProjectFactory.create(name="foobar")
86+
user = UserFactory.create(username="testuser")
87+
role = RoleFactory.create(user=user, project=project)
88+
89+
user_service = pretend.stub()
90+
db_request.find_service = pretend.call_recorder(
91+
lambda iface, context: user_service
92+
)
93+
db_request.method = "POST"
94+
form_obj = pretend.stub(validate=pretend.call_recorder(lambda: False))
95+
form_class = pretend.call_recorder(lambda d, user_service: form_obj)
96+
97+
result = views.manage_project_roles(
98+
project, db_request, _form_class=form_class
99+
)
100+
101+
assert db_request.find_service.calls == [
102+
pretend.call(IUserService, context=None),
103+
]
104+
assert form_class.calls == [
105+
pretend.call(db_request.POST, user_service=user_service),
106+
]
107+
assert form_obj.validate.calls == [pretend.call()]
108+
assert result == {
109+
"project": project,
110+
"roles": [role],
111+
"form": form_obj,
112+
}
113+
114+
def test_post_new_role(self, db_request):
115+
project = ProjectFactory.create(name="foobar")
116+
user = UserFactory.create(username="testuser")
117+
118+
user_service = pretend.stub(
119+
find_userid=lambda username: user.id,
120+
get_user=lambda userid: user,
121+
)
122+
db_request.find_service = pretend.call_recorder(
123+
lambda iface, context: user_service
124+
)
125+
db_request.method = "POST"
126+
db_request.POST = pretend.stub()
127+
form_obj = pretend.stub(
128+
validate=pretend.call_recorder(lambda: True),
129+
username=pretend.stub(data=user.username),
130+
role_name=pretend.stub(data="Owner"),
131+
)
132+
form_class = pretend.call_recorder(lambda *a, **kw: form_obj)
133+
db_request.session = pretend.stub(
134+
flash=pretend.call_recorder(lambda *a, **kw: None),
135+
)
136+
137+
result = views.manage_project_roles(
138+
project, db_request, _form_class=form_class
139+
)
140+
141+
assert db_request.find_service.calls == [
142+
pretend.call(IUserService, context=None),
143+
]
144+
assert form_obj.validate.calls == [pretend.call()]
145+
assert form_class.calls == [
146+
pretend.call(db_request.POST, user_service=user_service),
147+
pretend.call(user_service=user_service),
148+
]
149+
assert db_request.session.flash.calls == [
150+
pretend.call("Added collaborator 'testuser'", queue="success"),
151+
]
152+
153+
# Only one role is created
154+
role = db_request.db.query(Role).one()
155+
156+
assert result == {
157+
"project": project,
158+
"roles": [role],
159+
"form": form_obj,
160+
}
161+
162+
def test_post_duplicate_role(self, db_request):
163+
project = ProjectFactory.create(name="foobar")
164+
user = UserFactory.create(username="testuser")
165+
role = RoleFactory.create(
166+
user=user, project=project, role_name="Owner"
167+
)
168+
169+
user_service = pretend.stub(
170+
find_userid=lambda username: user.id,
171+
get_user=lambda userid: user,
172+
)
173+
db_request.find_service = pretend.call_recorder(
174+
lambda iface, context: user_service
175+
)
176+
db_request.method = "POST"
177+
db_request.POST = pretend.stub()
178+
form_obj = pretend.stub(
179+
validate=pretend.call_recorder(lambda: True),
180+
username=pretend.stub(data=user.username),
181+
role_name=pretend.stub(data=role.role_name),
182+
)
183+
form_class = pretend.call_recorder(lambda *a, **kw: form_obj)
184+
db_request.session = pretend.stub(
185+
flash=pretend.call_recorder(lambda *a, **kw: None),
186+
)
187+
188+
result = views.manage_project_roles(
189+
project, db_request, _form_class=form_class
190+
)
191+
192+
assert db_request.find_service.calls == [
193+
pretend.call(IUserService, context=None),
194+
]
195+
assert form_obj.validate.calls == [pretend.call()]
196+
assert form_class.calls == [
197+
pretend.call(db_request.POST, user_service=user_service),
198+
pretend.call(user_service=user_service),
199+
]
200+
assert db_request.session.flash.calls == [
201+
pretend.call(
202+
"User 'testuser' already has Owner role for project",
203+
queue="error",
204+
),
205+
]
206+
207+
# No additional roles are created
208+
assert role == db_request.db.query(Role).one()
209+
210+
assert result == {
211+
"project": project,
212+
"roles": [role],
213+
"form": form_obj,
214+
}
215+
216+
217+
class TestDeleteProjectRoles:
218+
219+
def test_delete_role(self, db_request):
220+
project = ProjectFactory.create(name="foobar")
221+
user = UserFactory.create(username="testuser")
222+
role = RoleFactory.create(
223+
user=user, project=project, role_name="Owner"
224+
)
225+
226+
db_request.method = "POST"
227+
db_request.user = pretend.stub()
228+
db_request.POST = MultiDict({"role_id": role.id})
229+
db_request.session = pretend.stub(
230+
flash=pretend.call_recorder(lambda *a, **kw: None),
231+
)
232+
db_request.route_path = pretend.call_recorder(
233+
lambda *a, **kw: "/the-redirect"
234+
)
235+
236+
result = views.delete_project_role(project, db_request)
237+
238+
assert db_request.route_path.calls == [
239+
pretend.call('manage.project.roles', name=project.name),
240+
]
241+
assert db_request.db.query(Role).all() == []
242+
assert db_request.session.flash.calls == [
243+
pretend.call("Successfully removed role", queue="success"),
244+
]
245+
assert isinstance(result, HTTPSeeOther)
246+
assert result.headers["Location"] == "/the-redirect"
247+
248+
def test_delete_missing_role(self, db_request):
249+
project = ProjectFactory.create(name="foobar")
250+
missing_role_id = str(uuid.uuid4())
251+
252+
db_request.method = "POST"
253+
db_request.user = pretend.stub()
254+
db_request.POST = MultiDict({"role_id": missing_role_id})
255+
db_request.session = pretend.stub(
256+
flash=pretend.call_recorder(lambda *a, **kw: None),
257+
)
258+
db_request.route_path = pretend.call_recorder(
259+
lambda *a, **kw: "/the-redirect"
260+
)
261+
262+
result = views.delete_project_role(project, db_request)
263+
264+
assert db_request.session.flash.calls == [
265+
pretend.call("Could not find role", queue="error"),
266+
]
267+
assert isinstance(result, HTTPSeeOther)
268+
assert result.headers["Location"] == "/the-redirect"
269+
270+
def test_delete_own_owner_role(self, db_request):
271+
project = ProjectFactory.create(name="foobar")
272+
user = UserFactory.create(username="testuser")
273+
role = RoleFactory.create(
274+
user=user, project=project, role_name="Owner"
275+
)
276+
277+
db_request.method = "POST"
278+
db_request.user = user
279+
db_request.POST = MultiDict({"role_id": role.id})
280+
db_request.session = pretend.stub(
281+
flash=pretend.call_recorder(lambda *a, **kw: None),
282+
)
283+
db_request.route_path = pretend.call_recorder(
284+
lambda *a, **kw: "/the-redirect"
285+
)
286+
287+
result = views.delete_project_role(project, db_request)
288+
289+
assert db_request.session.flash.calls == [
290+
pretend.call("Cannot remove yourself as Owner", queue="error"),
291+
]
292+
assert isinstance(result, HTTPSeeOther)
293+
assert result.headers["Location"] == "/the-redirect"

tests/unit/test_routes.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,20 @@ def add_policy(name, filename):
132132
traverse="/{name}",
133133
domain=warehouse,
134134
),
135+
pretend.call(
136+
"manage.project.roles",
137+
"/project/{name}/collaboration/",
138+
factory="warehouse.packaging.models:ProjectFactory",
139+
traverse="/{name}",
140+
domain=warehouse,
141+
),
142+
pretend.call(
143+
"manage.project.delete_role",
144+
"/project/{name}/collaboration/delete/",
145+
factory="warehouse.packaging.models:ProjectFactory",
146+
traverse="/{name}",
147+
domain=warehouse,
148+
),
135149
pretend.call(
136150
"packaging.project",
137151
"/project/{name}/",

warehouse/manage/__init__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,15 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License");
2+
# you may not use this file except in compliance with the License.
3+
# You may obtain a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS,
9+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
# See the License for the specific language governing permissions and
11+
# limitations under the License.
12+
13+
114
def includeme(config):
215
pass

0 commit comments

Comments
 (0)