Skip to content

Commit 9b22697

Browse files
Stop webapp replacement when deletion fails.
1 parent 860dd6c commit 9b22697

2 files changed

Lines changed: 27 additions & 3 deletions

File tree

‎pythonanywhere_core/webapp.py‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,17 @@ def create(self, python_version: str, virtualenv_path: Path | None, project_path
8585
:param python_version: python version to use
8686
:param virtualenv_path: path to the virtualenv, or None to skip setting it
8787
:param project_path: path to the project
88-
:param nuke: if True, delete any existing webapp for this domain
88+
:param nuke: if True, delete any existing webapp for this domain;
89+
continue only on HTTP 204 (deleted) or 404 (absent)
8990
9091
:raises PythonAnywhereApiException: if API call fails
9192
"""
9293
if nuke:
93-
call_api(self.domain_url, "delete")
94+
response = call_api(self.domain_url, "delete")
95+
if response.status_code not in (204, 404):
96+
raise PythonAnywhereApiException(
97+
f"DELETE before replacing webapp via API failed, got {response}:{response.text}"
98+
)
9499
response = call_api(
95100
self.webapps_url,
96101
"post",

‎tests/test_webapp.py‎

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,18 +262,37 @@ def test_raises_if_patch_does_not_20x(api_responses, api_token, base_url, domain
262262
## DELETE (for nuke functionality in CREATE)
263263

264264
def test_does_delete_first_for_nuke_call(api_responses, api_token, base_url, domain_url, webapp):
265-
api_responses.add(responses.DELETE, domain_url, status=200)
265+
api_responses.add(responses.DELETE, domain_url, status=204)
266266
api_responses.add(responses.POST, base_url, status=201, body=json.dumps({"status": "OK"}))
267267
api_responses.add(responses.PATCH, domain_url, status=200)
268268

269269
webapp.create("3.10", "/virtualenv/path", "/project/path", nuke=True)
270270

271+
assert [call.request.method for call in api_responses.calls] == ["DELETE", "POST", "PATCH"]
271272
delete = api_responses.calls[0]
272273
assert delete.request.method == "DELETE"
273274
assert delete.request.url == domain_url
274275
assert delete.request.headers["Authorization"] == f"Token {api_token}"
275276

276277

278+
@pytest.mark.parametrize("status_code", [200, 201, 202, 403, 409, 500])
279+
def test_nuke_stops_before_creation_when_delete_fails(
280+
api_responses, api_token, base_url, domain_url, webapp, status_code
281+
):
282+
# Register later steps too, so an erroneous continuation is observable.
283+
api_responses.assert_all_requests_are_fired = False
284+
api_responses.add(responses.DELETE, domain_url, status=status_code, body="Delete failed")
285+
api_responses.add(responses.POST, base_url, status=201, json={"status": "OK"})
286+
api_responses.add(responses.PATCH, domain_url, status=200)
287+
288+
with pytest.raises(PythonAnywhereApiException) as exc:
289+
webapp.create("3.10", "/virtualenv/path", "/project/path", nuke=True)
290+
291+
assert str(status_code) in str(exc.value)
292+
assert "Delete failed" in str(exc.value)
293+
assert [call.request.method for call in api_responses.calls] == ["DELETE"]
294+
295+
277296
def test_ignores_404_from_delete_call_when_nuking(api_responses, api_token, base_url, domain_url, webapp):
278297
api_responses.add(responses.DELETE, domain_url, status=404)
279298
api_responses.add(responses.POST, base_url, status=201, body=json.dumps({"status": "OK"}))

0 commit comments

Comments
 (0)