Skip to content

Commit 1858eaa

Browse files
committed
Add disallow deletion AdminFlag
See #3218
1 parent ab3a323 commit 1858eaa

File tree

7 files changed

+269
-5
lines changed

7 files changed

+269
-5
lines changed

tests/unit/manage/test_views.py

+161-3
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import warehouse.utils.otp as otp
2828

2929
from warehouse.accounts.interfaces import IPasswordBreachedService, IUserService
30+
from warehouse.admin.flags import AdminFlagValue
3031
from warehouse.macaroons.interfaces import IMacaroonService
3132
from warehouse.manage import views
3233
from warehouse.packaging.models import (
@@ -2005,15 +2006,24 @@ def test_manage_projects(self, db_request):
20052006

20062007
class TestManageProjectSettings:
20072008
def test_manage_project_settings(self):
2008-
request = pretend.stub()
2009+
request = pretend.stub(
2010+
flags=pretend.stub(enabled=pretend.call_recorder(lambda *a: False))
2011+
)
20092012
project = pretend.stub()
20102013

2011-
assert views.manage_project_settings(project, request) == {"project": project}
2014+
assert views.manage_project_settings(project, request) == {
2015+
"disallow_deletion": False,
2016+
"project": project,
2017+
}
2018+
assert request.flags.enabled.calls == [
2019+
pretend.call(AdminFlagValue.DISALLOW_DELETION)
2020+
]
20122021

20132022
def test_delete_project_no_confirm(self):
20142023
project = pretend.stub(normalized_name="foo")
20152024
request = pretend.stub(
20162025
POST={},
2026+
flags=pretend.stub(enabled=pretend.call_recorder(lambda *a: False)),
20172027
session=pretend.stub(flash=pretend.call_recorder(lambda *a, **kw: None)),
20182028
route_path=lambda *a, **kw: "/foo/bar/",
20192029
)
@@ -2023,6 +2033,9 @@ def test_delete_project_no_confirm(self):
20232033
assert exc.value.status_code == 303
20242034
assert exc.value.headers["Location"] == "/foo/bar/"
20252035

2036+
assert request.flags.enabled.calls == [
2037+
pretend.call(AdminFlagValue.DISALLOW_DELETION)
2038+
]
20262039
assert request.session.flash.calls == [
20272040
pretend.call("Confirm the request", queue="error")
20282041
]
@@ -2031,6 +2044,7 @@ def test_delete_project_wrong_confirm(self):
20312044
project = pretend.stub(normalized_name="foo")
20322045
request = pretend.stub(
20332046
POST={"confirm_project_name": "bar"},
2047+
flags=pretend.stub(enabled=pretend.call_recorder(lambda *a: False)),
20342048
session=pretend.stub(flash=pretend.call_recorder(lambda *a, **kw: None)),
20352049
route_path=lambda *a, **kw: "/foo/bar/",
20362050
)
@@ -2040,13 +2054,46 @@ def test_delete_project_wrong_confirm(self):
20402054
assert exc.value.status_code == 303
20412055
assert exc.value.headers["Location"] == "/foo/bar/"
20422056

2057+
assert request.flags.enabled.calls == [
2058+
pretend.call(AdminFlagValue.DISALLOW_DELETION)
2059+
]
20432060
assert request.session.flash.calls == [
20442061
pretend.call(
20452062
"Could not delete project - 'bar' is not the same as 'foo'",
20462063
queue="error",
20472064
)
20482065
]
20492066

2067+
def test_delete_project_disallow_deletion(self):
2068+
project = pretend.stub(name="foo", normalized_name="foo")
2069+
request = pretend.stub(
2070+
flags=pretend.stub(enabled=pretend.call_recorder(lambda *a: True)),
2071+
route_path=pretend.call_recorder(lambda *a, **kw: "/the-redirect"),
2072+
session=pretend.stub(flash=pretend.call_recorder(lambda *a, **kw: None)),
2073+
)
2074+
2075+
result = views.delete_project(project, request)
2076+
assert isinstance(result, HTTPSeeOther)
2077+
assert result.headers["Location"] == "/the-redirect"
2078+
2079+
assert request.flags.enabled.calls == [
2080+
pretend.call(AdminFlagValue.DISALLOW_DELETION)
2081+
]
2082+
2083+
assert request.session.flash.calls == [
2084+
pretend.call(
2085+
(
2086+
"Project deletion temporarily disabled. "
2087+
"See https://pypi.org/help#admin-intervention for details."
2088+
),
2089+
queue="error",
2090+
)
2091+
]
2092+
2093+
assert request.route_path.calls == [
2094+
pretend.call("manage.project.settings", project_name="foo")
2095+
]
2096+
20502097
def test_delete_project(self, db_request):
20512098
project = ProjectFactory.create(name="foo")
20522099

@@ -2159,29 +2206,84 @@ def test_manage_project_releases(self, db_request):
21592206
filename=f"foobar-{release.version}.tar.gz",
21602207
packagetype="sdist",
21612208
)
2209+
db_request.flags = pretend.stub(enabled=pretend.call_recorder(lambda *a: False))
21622210

21632211
assert views.manage_project_releases(project, db_request) == {
2212+
"disallow_deletion": False,
21642213
"project": project,
21652214
"version_to_file_counts": {
21662215
release.version: {"total": 1, release_file.packagetype: 1}
21672216
},
21682217
}
21692218

2219+
assert db_request.flags.enabled.calls == [
2220+
pretend.call(AdminFlagValue.DISALLOW_DELETION)
2221+
]
2222+
21702223

21712224
class TestManageProjectRelease:
21722225
def test_manage_project_release(self):
21732226
files = pretend.stub()
21742227
project = pretend.stub()
21752228
release = pretend.stub(project=project, files=pretend.stub(all=lambda: files))
2176-
request = pretend.stub()
2229+
request = pretend.stub(
2230+
flags=pretend.stub(enabled=pretend.call_recorder(lambda *a: False))
2231+
)
21772232
view = views.ManageProjectRelease(release, request)
21782233

21792234
assert view.manage_project_release() == {
2235+
"disallow_deletion": False,
21802236
"project": project,
21812237
"release": release,
21822238
"files": files,
21832239
}
21842240

2241+
assert request.flags.enabled.calls == [
2242+
pretend.call(AdminFlagValue.DISALLOW_DELETION)
2243+
]
2244+
2245+
def test_delete_project_release_disallow_deletion(self, monkeypatch):
2246+
release = pretend.stub(
2247+
version="1.2.3",
2248+
canonical_version="1.2.3",
2249+
project=pretend.stub(
2250+
name="foobar", record_event=pretend.call_recorder(lambda *a, **kw: None)
2251+
),
2252+
)
2253+
request = pretend.stub(
2254+
flags=pretend.stub(enabled=pretend.call_recorder(lambda *a: True)),
2255+
method="POST",
2256+
route_path=pretend.call_recorder(lambda *a, **kw: "/the-redirect"),
2257+
session=pretend.stub(flash=pretend.call_recorder(lambda *a, **kw: None)),
2258+
)
2259+
view = views.ManageProjectRelease(release, request)
2260+
2261+
result = view.delete_project_release()
2262+
assert isinstance(result, HTTPSeeOther)
2263+
assert result.headers["Location"] == "/the-redirect"
2264+
2265+
assert request.flags.enabled.calls == [
2266+
pretend.call(AdminFlagValue.DISALLOW_DELETION)
2267+
]
2268+
2269+
assert request.session.flash.calls == [
2270+
pretend.call(
2271+
(
2272+
"Project deletion temporarily disabled. "
2273+
"See https://pypi.org/help#admin-intervention for details."
2274+
),
2275+
queue="error",
2276+
)
2277+
]
2278+
2279+
assert request.route_path.calls == [
2280+
pretend.call(
2281+
"manage.project.release",
2282+
project_name=release.project.name,
2283+
version=release.version,
2284+
)
2285+
]
2286+
21852287
def test_delete_project_release(self, monkeypatch):
21862288
release = pretend.stub(
21872289
version="1.2.3",
@@ -2197,6 +2299,7 @@ def test_delete_project_release(self, monkeypatch):
21972299
delete=pretend.call_recorder(lambda a: None),
21982300
add=pretend.call_recorder(lambda a: None),
21992301
),
2302+
flags=pretend.stub(enabled=pretend.call_recorder(lambda *a: False)),
22002303
route_path=pretend.call_recorder(lambda *a, **kw: "/the-redirect"),
22012304
session=pretend.stub(flash=pretend.call_recorder(lambda *a, **kw: None)),
22022305
user=pretend.stub(username=pretend.stub()),
@@ -2215,6 +2318,9 @@ def test_delete_project_release(self, monkeypatch):
22152318

22162319
assert request.db.delete.calls == [pretend.call(release)]
22172320
assert request.db.add.calls == [pretend.call(journal_obj)]
2321+
assert request.flags.enabled.calls == [
2322+
pretend.call(AdminFlagValue.DISALLOW_DELETION)
2323+
]
22182324
assert journal_cls.calls == [
22192325
pretend.call(
22202326
name=release.project.name,
@@ -2247,6 +2353,7 @@ def test_delete_project_release_no_confirm(self):
22472353
POST={"confirm_version": ""},
22482354
method="POST",
22492355
db=pretend.stub(delete=pretend.call_recorder(lambda a: None)),
2356+
flags=pretend.stub(enabled=pretend.call_recorder(lambda *a: False)),
22502357
route_path=pretend.call_recorder(lambda *a, **kw: "/the-redirect"),
22512358
session=pretend.stub(flash=pretend.call_recorder(lambda *a, **kw: None)),
22522359
)
@@ -2261,6 +2368,9 @@ def test_delete_project_release_no_confirm(self):
22612368
assert request.session.flash.calls == [
22622369
pretend.call("Confirm the request", queue="error")
22632370
]
2371+
assert request.flags.enabled.calls == [
2372+
pretend.call(AdminFlagValue.DISALLOW_DELETION)
2373+
]
22642374
assert request.route_path.calls == [
22652375
pretend.call(
22662376
"manage.project.release",
@@ -2275,6 +2385,7 @@ def test_delete_project_release_bad_confirm(self):
22752385
POST={"confirm_version": "invalid"},
22762386
method="POST",
22772387
db=pretend.stub(delete=pretend.call_recorder(lambda a: None)),
2388+
flags=pretend.stub(enabled=pretend.call_recorder(lambda *a: False)),
22782389
route_path=pretend.call_recorder(lambda *a, **kw: "/the-redirect"),
22792390
session=pretend.stub(flash=pretend.call_recorder(lambda *a, **kw: None)),
22802391
)
@@ -2286,6 +2397,9 @@ def test_delete_project_release_bad_confirm(self):
22862397
assert result.headers["Location"] == "/the-redirect"
22872398

22882399
assert request.db.delete.calls == []
2400+
assert request.flags.enabled.calls == [
2401+
pretend.call(AdminFlagValue.DISALLOW_DELETION)
2402+
]
22892403
assert request.session.flash.calls == [
22902404
pretend.call(
22912405
"Could not delete release - "
@@ -2301,6 +2415,42 @@ def test_delete_project_release_bad_confirm(self):
23012415
)
23022416
]
23032417

2418+
def test_delete_project_release_file_disallow_deletion(self):
2419+
release = pretend.stub(version="1.2.3", project=pretend.stub(name="foobar"))
2420+
request = pretend.stub(
2421+
method="POST",
2422+
flags=pretend.stub(enabled=pretend.call_recorder(lambda *a: True)),
2423+
route_path=pretend.call_recorder(lambda *a, **kw: "/the-redirect"),
2424+
session=pretend.stub(flash=pretend.call_recorder(lambda *a, **kw: None)),
2425+
)
2426+
view = views.ManageProjectRelease(release, request)
2427+
2428+
result = view.delete_project_release_file()
2429+
2430+
assert isinstance(result, HTTPSeeOther)
2431+
assert result.headers["Location"] == "/the-redirect"
2432+
2433+
assert request.flags.enabled.calls == [
2434+
pretend.call(AdminFlagValue.DISALLOW_DELETION)
2435+
]
2436+
2437+
assert request.session.flash.calls == [
2438+
pretend.call(
2439+
(
2440+
"Project deletion temporarily disabled. "
2441+
"See https://pypi.org/help#admin-intervention for details."
2442+
),
2443+
queue="error",
2444+
)
2445+
]
2446+
assert request.route_path.calls == [
2447+
pretend.call(
2448+
"manage.project.release",
2449+
project_name=release.project.name,
2450+
version=release.version,
2451+
)
2452+
]
2453+
23042454
def test_delete_project_release_file(self, db_request):
23052455
user = UserFactory.create()
23062456

@@ -2359,6 +2509,7 @@ def test_delete_project_release_file_no_confirm(self):
23592509
POST={"confirm_project_name": ""},
23602510
method="POST",
23612511
db=pretend.stub(delete=pretend.call_recorder(lambda a: None)),
2512+
flags=pretend.stub(enabled=pretend.call_recorder(lambda *a: False)),
23622513
route_path=pretend.call_recorder(lambda *a, **kw: "/the-redirect"),
23632514
session=pretend.stub(flash=pretend.call_recorder(lambda *a, **kw: None)),
23642515
)
@@ -2370,6 +2521,9 @@ def test_delete_project_release_file_no_confirm(self):
23702521
assert result.headers["Location"] == "/the-redirect"
23712522

23722523
assert request.db.delete.calls == []
2524+
assert request.flags.enabled.calls == [
2525+
pretend.call(AdminFlagValue.DISALLOW_DELETION)
2526+
]
23732527
assert request.session.flash.calls == [
23742528
pretend.call("Confirm the request", queue="error")
23752529
]
@@ -2396,6 +2550,7 @@ def no_result_found():
23962550
filter=lambda *a: pretend.stub(one=no_result_found)
23972551
),
23982552
)
2553+
db_request.flags = pretend.stub(enabled=pretend.call_recorder(lambda *a: False))
23992554
db_request.route_path = pretend.call_recorder(lambda *a, **kw: "/the-redirect")
24002555
db_request.session = pretend.stub(
24012556
flash=pretend.call_recorder(lambda *a, **kw: None)
@@ -2409,6 +2564,9 @@ def no_result_found():
24092564
assert result.headers["Location"] == "/the-redirect"
24102565

24112566
assert db_request.db.delete.calls == []
2567+
assert db_request.flags.enabled.calls == [
2568+
pretend.call(AdminFlagValue.DISALLOW_DELETION)
2569+
]
24122570
assert db_request.session.flash.calls == [
24132571
pretend.call("Could not find file", queue="error")
24142572
]

warehouse/admin/flags.py

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717

1818
class AdminFlagValue:
19+
DISALLOW_DELETION = "disallow-deletion"
1920
DISALLOW_NEW_PROJECT_REGISTRATION = "disallow-new-project-registration"
2021
DISALLOW_NEW_USER_REGISTRATION = "disallow-new-user-registration"
2122
READ_ONLY = "read-only"

0 commit comments

Comments
 (0)