Skip to content

Commit

Permalink
Return client error status code for invalid form submissions (#849)
Browse files Browse the repository at this point in the history
* Returns client error status code for invalid form submissions

* fix flaky test
  • Loading branch information
sissbruecker authored Sep 23, 2024
1 parent d400602 commit c3a2305
Show file tree
Hide file tree
Showing 7 changed files with 43 additions and 10 deletions.
13 changes: 13 additions & 0 deletions bookmarks/tests/test_bookmark_edit_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ def create_form_data(self, overrides=None):
}
return {**form_data, **overrides}

def test_should_render_successfully(self):
bookmark = self.setup_bookmark()
response = self.client.get(reverse("bookmarks:edit", args=[bookmark.id]))
self.assertEqual(response.status_code, 200)

def test_should_edit_bookmark(self):
bookmark = self.setup_bookmark()
form_data = self.create_form_data({"id": bookmark.id})
Expand All @@ -46,6 +51,14 @@ def test_should_edit_bookmark(self):
self.assertEqual(tags[0].name, "editedtag1")
self.assertEqual(tags[1].name, "editedtag2")

def test_should_return_422_with_invalid_form(self):
bookmark = self.setup_bookmark()
form_data = self.create_form_data({"id": bookmark.id, "url": ""})
response = self.client.post(
reverse("bookmarks:edit", args=[bookmark.id]), form_data
)
self.assertEqual(response.status_code, 422)

def test_should_edit_unread_state(self):
bookmark = self.setup_bookmark()

Expand Down
5 changes: 5 additions & 0 deletions bookmarks/tests/test_bookmark_new_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ def test_should_create_new_bookmark(self):
self.assertEqual(tags[0].name, "tag1")
self.assertEqual(tags[1].name, "tag2")

def test_should_return_422_with_invalid_form(self):
form_data = self.create_form_data({"url": ""})
response = self.client.post(reverse("bookmarks:new"), form_data)
self.assertEqual(response.status_code, 422)

def test_should_create_new_unread_bookmark(self):
form_data = self.create_form_data({"unread": True})

Expand Down
2 changes: 2 additions & 0 deletions bookmarks/tests/test_password_change_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ def test_should_return_error_for_invalid_old_password(self):

response = self.client.post(reverse("change_password"), form_data)

self.assertEqual(response.status_code, 422)
self.assertIn("old_password", response.context_data["form"].errors)

def test_should_return_error_for_mismatching_new_password(self):
Expand All @@ -54,4 +55,5 @@ def test_should_return_error_for_mismatching_new_password(self):

response = self.client.post(reverse("change_password"), form_data)

self.assertEqual(response.status_code, 422)
self.assertIn("new_password2", response.context_data["form"].errors)
9 changes: 7 additions & 2 deletions bookmarks/tests/test_settings_general_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def create_profile_form_data(self, overrides=None):
if not overrides:
overrides = {}
form_data = {
"update_profile": "",
"theme": UserProfile.THEME_AUTO,
"bookmark_date_display": UserProfile.BOOKMARK_DATE_DISPLAY_RELATIVE,
"bookmark_description_display": UserProfile.BOOKMARK_DESCRIPTION_DISPLAY_INLINE,
Expand Down Expand Up @@ -195,6 +196,12 @@ def test_update_profile(self):

self.assertSuccessMessage(html, "Profile updated")

def test_update_profile_with_invalid_form_returns_422(self):
form_data = self.create_profile_form_data({"items_per_page": "-1"})
response = self.client.post(reverse("bookmarks:settings.update"), form_data)

self.assertEqual(response.status_code, 422)

def test_update_profile_should_not_be_called_without_respective_form_action(self):
form_data = {
"theme": UserProfile.THEME_DARK,
Expand All @@ -217,7 +224,6 @@ def test_enable_favicons_should_schedule_icon_update(self):
# Enabling favicons schedules update
form_data = self.create_profile_form_data(
{
"update_profile": "",
"enable_favicons": True,
}
)
Expand Down Expand Up @@ -331,7 +337,6 @@ def test_enable_preview_image_should_schedule_preview_update(self):
# Enabling favicons schedules update
form_data = self.create_profile_form_data(
{
"update_profile": "",
"enable_preview_images": True,
}
)
Expand Down
8 changes: 5 additions & 3 deletions bookmarks/tests/test_singlefile_service.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import secrets
import gzip
import os
import subprocess
Expand All @@ -9,9 +10,10 @@


class SingleFileServiceTestCase(TestCase):
html_content = "<html><body><h1>Hello, World!</h1></body></html>"
html_filepath = "temp.html.gz"
temp_html_filepath = "temp.html.gz.tmp"
def setUp(self):
self.html_content = "<html><body><h1>Hello, World!</h1></body></html>"
self.html_filepath = secrets.token_hex(8) + ".html.gz"
self.temp_html_filepath = self.html_filepath + ".tmp"

def tearDown(self):
if os.path.exists(self.html_filepath):
Expand Down
7 changes: 3 additions & 4 deletions bookmarks/views/bookmarks.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,6 @@ def convert_tag_string(tag_string: str):

@login_required
def new(request):
status = 200
initial_url = request.GET.get("url")
initial_title = request.GET.get("title")
initial_description = request.GET.get("description")
Expand All @@ -169,8 +168,6 @@ def new(request):
return HttpResponseRedirect(reverse("bookmarks:close"))
else:
return HttpResponseRedirect(reverse("bookmarks:index"))
else:
status = 422
else:
form = BookmarkForm()
if initial_url:
Expand All @@ -186,6 +183,7 @@ def new(request):
if initial_mark_unread:
form.initial["unread"] = "true"

status = 422 if request.method == "POST" and not form.is_valid() else 200
context = {
"form": form,
"auto_close": initial_auto_close,
Expand Down Expand Up @@ -216,9 +214,10 @@ def edit(request, bookmark_id: int):

form.initial["tag_string"] = build_tag_string(bookmark.tag_names, " ")

status = 422 if request.method == "POST" and not form.is_valid() else 200
context = {"form": form, "bookmark_id": bookmark_id, "return_url": return_url}

return render(request, "bookmarks/edit.html", context)
return render(request, "bookmarks/edit.html", context, status=status)


def remove(request, bookmark_id: int):
Expand Down
9 changes: 8 additions & 1 deletion siteroot/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ def form_invalid(self, form):
return response


class LinkdingPasswordChangeView(auth_views.PasswordChangeView):
def form_invalid(self, form):
response = super().form_invalid(form)
response.status_code = 422
return response


urlpatterns = [
path("admin/", linkding_admin_site.urls),
path(
Expand All @@ -50,7 +57,7 @@ def form_invalid(self, form):
path("logout/", auth_views.LogoutView.as_view(), name="logout"),
path(
"change-password/",
auth_views.PasswordChangeView.as_view(),
LinkdingPasswordChangeView.as_view(),
name="change_password",
),
path(
Expand Down

0 comments on commit c3a2305

Please sign in to comment.