From ce8be54acb02085d3fb520c81a711cdf2337650f Mon Sep 17 00:00:00 2001 From: rafalp Date: Thu, 25 Jul 2024 23:48:44 +0200 Subject: [PATCH] Add tests for moderation flows on threads lists --- .../tests/test_threads_lists_moderation.py | 418 ++++++++++++++++-- misago/threads/views/list.py | 13 +- 2 files changed, 383 insertions(+), 48 deletions(-) diff --git a/misago/threads/tests/test_threads_lists_moderation.py b/misago/threads/tests/test_threads_lists_moderation.py index d3267ef272..f9283d3a4a 100644 --- a/misago/threads/tests/test_threads_lists_moderation.py +++ b/misago/threads/tests/test_threads_lists_moderation.py @@ -359,6 +359,132 @@ def test_category_threads_executes_multi_stage_moderation_action_in_htmx( assert thread.category == child_category +@override_dynamic_settings(index_view="categories") +def test_site_threads_list_moderation_returns_error_for_user( + default_category, user_client +): + thread = post_thread(default_category, "Moderate Thread") + + response = user_client.post( + reverse("misago:threads"), + {"moderation": "close", "threads": [thread.id]}, + ) + assert_contains(response, "Invalid moderation action") + + thread.refresh_from_db() + assert not thread.is_closed + + +def test_category_threads_list_moderation_returns_error_for_user( + default_category, user_client +): + thread = post_thread(default_category, "Moderate Thread") + + response = user_client.post( + default_category.get_absolute_url(), + {"moderation": "close", "threads": [thread.id]}, + ) + assert_contains(response, "Invalid moderation action") + + thread.refresh_from_db() + assert not thread.is_closed + + +@override_dynamic_settings(index_view="categories") +def test_site_threads_list_moderation_returns_error_for_user_in_htmx( + default_category, user_client +): + thread = post_thread(default_category, "Moderate Thread") + + response = user_client.post( + reverse("misago:threads"), + {"moderation": "close", "threads": [thread.id]}, + headers={"hx-request": "true"}, + ) + assert_contains(response, "Invalid moderation action") + + thread.refresh_from_db() + assert not thread.is_closed + + +def test_category_threads_list_moderation_returns_error_for_user_in_htmx( + default_category, user_client +): + thread = post_thread(default_category, "Moderate Thread") + + response = user_client.post( + default_category.get_absolute_url(), + {"moderation": "close", "threads": [thread.id]}, + headers={"hx-request": "true"}, + ) + assert_contains(response, "Invalid moderation action") + + thread.refresh_from_db() + assert not thread.is_closed + + +@override_dynamic_settings(index_view="categories") +def test_site_threads_list_moderation_returns_error_for_guest(default_category, client): + thread = post_thread(default_category, "Moderate Thread") + + response = client.post( + reverse("misago:threads"), + {"moderation": "close", "threads": [thread.id]}, + ) + assert_contains(response, "Invalid moderation action") + + thread.refresh_from_db() + assert not thread.is_closed + + +def test_category_threads_list_moderation_returns_error_for_guest( + default_category, client +): + thread = post_thread(default_category, "Moderate Thread") + + response = client.post( + default_category.get_absolute_url(), + {"moderation": "close", "threads": [thread.id]}, + ) + assert_contains(response, "Invalid moderation action") + + thread.refresh_from_db() + assert not thread.is_closed + + +@override_dynamic_settings(index_view="categories") +def test_site_threads_list_moderation_returns_error_for_guest_in_htmx( + default_category, client +): + thread = post_thread(default_category, "Moderate Thread") + + response = client.post( + reverse("misago:threads"), + {"moderation": "close", "threads": [thread.id]}, + headers={"hx-request": "true"}, + ) + assert_contains(response, "Invalid moderation action") + + thread.refresh_from_db() + assert not thread.is_closed + + +def test_category_threads_list_moderation_returns_error_for_guest_in_htmx( + default_category, client +): + thread = post_thread(default_category, "Moderate Thread") + + response = client.post( + default_category.get_absolute_url(), + {"moderation": "close", "threads": [thread.id]}, + headers={"hx-request": "true"}, + ) + assert_contains(response, "Invalid moderation action") + + thread.refresh_from_db() + assert not thread.is_closed + + @override_dynamic_settings(index_view="categories") def test_site_threads_list_returns_error_for_invalid_moderation_action( default_category, moderator_client @@ -412,126 +538,326 @@ def test_category_threads_returns_error_for_invalid_moderation_action_in_htmx( @override_dynamic_settings(index_view="categories") -def test_site_threads_list_moderation_returns_error_for_user( - default_category, user_client +def test_site_threads_list_returns_error_for_empty_moderation_action( + default_category, moderator_client ): thread = post_thread(default_category, "Moderate Thread") - response = user_client.post( + response = moderator_client.post( reverse("misago:threads"), - {"moderation": "close", "threads": [thread.id]}, + {"moderation": "", "threads": [thread.id]}, ) assert_contains(response, "Invalid moderation action") - thread.refresh_from_db() - assert not thread.is_closed - -def test_category_threads_list_moderation_returns_error_for_user( - default_category, user_client +def test_category_threads_returns_error_for_empty_moderation_action( + default_category, moderator_client ): thread = post_thread(default_category, "Moderate Thread") - response = user_client.post( + response = moderator_client.post( default_category.get_absolute_url(), - {"moderation": "close", "threads": [thread.id]}, + {"moderation": "", "threads": [thread.id]}, ) assert_contains(response, "Invalid moderation action") - thread.refresh_from_db() - assert not thread.is_closed - @override_dynamic_settings(index_view="categories") -def test_site_threads_list_moderation_returns_error_for_user_in_htmx( - default_category, user_client +def test_site_threads_list_returns_error_for_empty_moderation_action_in_htmx( + default_category, moderator_client ): thread = post_thread(default_category, "Moderate Thread") - response = user_client.post( + response = moderator_client.post( reverse("misago:threads"), - {"moderation": "close", "threads": [thread.id]}, + {"moderation": "", "threads": [thread.id]}, headers={"hx-request": "true"}, ) assert_contains(response, "Invalid moderation action") - thread.refresh_from_db() - assert not thread.is_closed - -def test_category_threads_list_moderation_returns_error_for_user_in_htmx( - default_category, user_client +def test_category_threads_returns_error_for_empty_moderation_action_in_htmx( + default_category, moderator_client ): thread = post_thread(default_category, "Moderate Thread") - response = user_client.post( + response = moderator_client.post( default_category.get_absolute_url(), - {"moderation": "close", "threads": [thread.id]}, + {"moderation": "", "threads": [thread.id]}, headers={"hx-request": "true"}, ) assert_contains(response, "Invalid moderation action") - thread.refresh_from_db() - assert not thread.is_closed + +@override_dynamic_settings(index_view="categories") +def test_site_threads_list_returns_error_for_empty_threads_selection( + default_category, moderator_client +): + response = moderator_client.post( + reverse("misago:threads"), + {"moderation": "close", "threads": []}, + ) + assert_contains(response, "No valid threads selected") + + +def test_category_threads_returns_error_for_empty_threads_selection( + default_category, moderator_client +): + response = moderator_client.post( + default_category.get_absolute_url(), + {"moderation": "close", "threads": []}, + ) + assert_contains(response, "No valid threads selected") @override_dynamic_settings(index_view="categories") -def test_site_threads_list_moderation_returns_error_for_guest(default_category, client): - thread = post_thread(default_category, "Moderate Thread") +def test_site_threads_list_returns_error_for_empty_threads_selection_in_htmx( + default_category, moderator_client +): + response = moderator_client.post( + reverse("misago:threads"), + {"moderation": "close", "threads": []}, + headers={"hx-request": "true"}, + ) + assert_contains(response, "No valid threads selected") - response = client.post( + +def test_category_threads_returns_error_for_empty_threads_selection_in_htmx( + default_category, moderator_client +): + response = moderator_client.post( + default_category.get_absolute_url(), + {"moderation": "close", "threads": []}, + headers={"hx-request": "true"}, + ) + assert_contains(response, "No valid threads selected") + + +@override_dynamic_settings(index_view="categories") +def test_site_threads_list_returns_error_for_invalid_threads_selection( + default_category, moderator_client +): + response = moderator_client.post( + reverse("misago:threads"), + {"moderation": "close", "threads": "invalid"}, + ) + assert_contains(response, "No valid threads selected") + + +def test_category_threads_returns_error_for_invalid_threads_selection( + default_category, moderator_client +): + response = moderator_client.post( + default_category.get_absolute_url(), + {"moderation": "close", "threads": "invalid"}, + ) + assert_contains(response, "No valid threads selected") + + +@override_dynamic_settings(index_view="categories") +def test_site_threads_list_returns_error_for_invalid_threads_selection_in_htmx( + default_category, moderator_client +): + response = moderator_client.post( + reverse("misago:threads"), + {"moderation": "close", "threads": "invalid"}, + headers={"hx-request": "true"}, + ) + assert_contains(response, "No valid threads selected") + + +def test_category_threads_returns_error_for_invalid_threads_selection_in_htmx( + default_category, moderator_client +): + response = moderator_client.post( + default_category.get_absolute_url(), + {"moderation": "close", "threads": "invalid"}, + headers={"hx-request": "true"}, + ) + assert_contains(response, "No valid threads selected") + + +@override_dynamic_settings(index_view="categories") +def test_site_threads_list_returns_error_for_invalid_threads_ids_in_selection( + default_category, moderator_client +): + response = moderator_client.post( + reverse("misago:threads"), + {"moderation": "close", "threads": ["invalid"]}, + ) + assert_contains(response, "No valid threads selected") + + +def test_category_threads_returns_error_for_invalid_threads_ids_in_selection( + default_category, moderator_client +): + response = moderator_client.post( + default_category.get_absolute_url(), + {"moderation": "close", "threads": ["invalid"]}, + ) + assert_contains(response, "No valid threads selected") + + +@override_dynamic_settings(index_view="categories") +def test_site_threads_list_returns_error_for_invalid_threads_ids_in_selection_in_htmx( + default_category, moderator_client +): + response = moderator_client.post( + reverse("misago:threads"), + {"moderation": "close", "threads": ["invalid"]}, + headers={"hx-request": "true"}, + ) + assert_contains(response, "No valid threads selected") + + +def test_category_threads_returns_error_for_invalid_threads_ids_in_selection_in_htmx( + default_category, moderator_client +): + response = moderator_client.post( + default_category.get_absolute_url(), + {"moderation": "close", "threads": ["invalid"]}, + headers={"hx-request": "true"}, + ) + assert_contains(response, "No valid threads selected") + + +@override_dynamic_settings(index_view="categories") +def test_site_threads_list_returns_error_for_not_existing_threads_ids_in_selection( + default_category, moderator_client +): + response = moderator_client.post( + reverse("misago:threads"), + {"moderation": "close", "threads": [42]}, + ) + assert_contains(response, "No valid threads selected") + + +def test_category_threads_returns_error_for_not_existing_threads_ids_in_selection( + default_category, moderator_client +): + response = moderator_client.post( + default_category.get_absolute_url(), + {"moderation": "close", "threads": [42]}, + ) + assert_contains(response, "No valid threads selected") + + +@override_dynamic_settings(index_view="categories") +def test_site_threads_list_returns_error_for_not_existing_threads_ids_in_selection_in_htmx( + default_category, moderator_client +): + response = moderator_client.post( + reverse("misago:threads"), + {"moderation": "close", "threads": [42]}, + headers={"hx-request": "true"}, + ) + assert_contains(response, "No valid threads selected") + + +def test_category_threads_returns_error_for_not_existing_threads_ids_in_selection_in_htmx( + default_category, moderator_client +): + response = moderator_client.post( + default_category.get_absolute_url(), + {"moderation": "close", "threads": [42]}, + headers={"hx-request": "true"}, + ) + assert_contains(response, "No valid threads selected") + + +@override_dynamic_settings(index_view="categories") +def test_site_threads_list_returns_error_for_thread_in_selection_user_cant_moderate( + default_category, child_category, user, user_client +): + Moderator.objects.create( + categories=[default_category.id], + user=user, + is_global=False, + ) + thread = post_thread(child_category, "Moderated Thread") + + response = user_client.post( reverse("misago:threads"), {"moderation": "close", "threads": [thread.id]}, ) - assert_contains(response, "Invalid moderation action") + assert_contains( + response, + "Can't moderate the "Moderated Thread" thread", + ) thread.refresh_from_db() assert not thread.is_closed -def test_category_threads_list_moderation_returns_error_for_guest( - default_category, client +@override_dynamic_settings(index_view="categories") +def test_category_threads_list_returns_error_for_thread_in_selection_user_cant_moderate( + default_category, child_category, user, user_client ): - thread = post_thread(default_category, "Moderate Thread") + Moderator.objects.create( + categories=[default_category.id], + user=user, + is_global=False, + ) + thread = post_thread(child_category, "Moderated Thread") - response = client.post( + response = user_client.post( default_category.get_absolute_url(), {"moderation": "close", "threads": [thread.id]}, ) - assert_contains(response, "Invalid moderation action") + assert_contains( + response, + "Can't moderate the "Moderated Thread" thread", + ) thread.refresh_from_db() assert not thread.is_closed @override_dynamic_settings(index_view="categories") -def test_site_threads_list_moderation_returns_error_for_guest_in_htmx( - default_category, client +def test_site_threads_list_returns_error_for_thread_in_selection_user_cant_moderate_in_htmx( + default_category, child_category, user, user_client ): - thread = post_thread(default_category, "Moderate Thread") + Moderator.objects.create( + categories=[default_category.id], + user=user, + is_global=False, + ) + thread = post_thread(child_category, "Moderated Thread") - response = client.post( + response = user_client.post( reverse("misago:threads"), {"moderation": "close", "threads": [thread.id]}, headers={"hx-request": "true"}, ) - assert_contains(response, "Invalid moderation action") + assert_contains( + response, + "Can't moderate the "Moderated Thread" thread", + ) thread.refresh_from_db() assert not thread.is_closed -def test_category_threads_list_moderation_returns_error_for_guest_in_htmx( - default_category, client +@override_dynamic_settings(index_view="categories") +def test_category_threads_list_returns_error_for_thread_in_selection_user_cant_moderate_in_htmx( + default_category, child_category, user, user_client ): - thread = post_thread(default_category, "Moderate Thread") + Moderator.objects.create( + categories=[default_category.id], + user=user, + is_global=False, + ) + thread = post_thread(child_category, "Moderated Thread") - response = client.post( + response = user_client.post( default_category.get_absolute_url(), {"moderation": "close", "threads": [thread.id]}, headers={"hx-request": "true"}, ) - assert_contains(response, "Invalid moderation action") + assert_contains( + response, + "Can't moderate the "Moderated Thread" thread", + ) thread.refresh_from_db() assert not thread.is_closed diff --git a/misago/threads/views/list.py b/misago/threads/views/list.py index 9da5f85c65..38f2cdb52f 100644 --- a/misago/threads/views/list.py +++ b/misago/threads/views/list.py @@ -225,12 +225,21 @@ def get_selected_threads(self, request: HttpRequest, threads: dict) -> list[Thre selection: list[Thread] = [] for thread_data in threads["items"]: thread = thread_data["thread"] - if thread.id in threads_ids and thread_data["moderation"]: + if thread.id in threads_ids: + if not thread_data["moderation"]: + raise ValidationError( + pgettext( + "threads moderation error", + 'Can\'t moderate the "%(thread)s" thread', + ) + % {"thread": thread.title}, + ) + selection.append(thread) if not selection: raise ValidationError( - pgettext("threads moderation error", "No threads selected"), + pgettext("threads moderation error", "No valid threads selected"), ) return selection