From afd3b27d78bfdecf54a95f8f5a17a84a546a6900 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 6 Jul 2022 09:47:33 -0700 Subject: [PATCH 1/9] Adds coverage command to bw-dev --- bw-dev | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bw-dev b/bw-dev index 4dd543c0a..7ecef5c34 100755 --- a/bw-dev +++ b/bw-dev @@ -103,6 +103,9 @@ case "$CMD" in pytest) runweb pytest --no-cov-on-fail "$@" ;; + pytest_coverage_report) + runweb pytest -n 3 --cov-report term-missing "$@" + ;; collectstatic) runweb python manage.py collectstatic --no-input ;; From c48fbd8753ba3926a8f40187cd838055c5258e77 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 6 Jul 2022 09:47:48 -0700 Subject: [PATCH 2/9] Separates reporting and report admins tests --- bookwyrm/tests/views/admin/test_reports.py | 46 --------- bookwyrm/tests/views/test_report.py | 114 +++++++++++++++++++++ 2 files changed, 114 insertions(+), 46 deletions(-) create mode 100644 bookwyrm/tests/views/test_report.py diff --git a/bookwyrm/tests/views/admin/test_reports.py b/bookwyrm/tests/views/admin/test_reports.py index d0875f2c2..48a81239b 100644 --- a/bookwyrm/tests/views/admin/test_reports.py +++ b/bookwyrm/tests/views/admin/test_reports.py @@ -89,52 +89,6 @@ class ReportViews(TestCase): self.assertEqual(comment.note, "hi") self.assertEqual(comment.report, report) - def test_report_modal_view(self): - """a user reports another user""" - request = self.factory.get("") - request.user = self.local_user - result = views.Report.as_view()(request, self.local_user.id) - - validate_html(result.render()) - - def test_make_report(self): - """a user reports another user""" - form = forms.ReportForm() - form.data["reporter"] = self.local_user.id - form.data["user"] = self.rat.id - request = self.factory.post("", form.data) - request.user = self.local_user - - views.Report.as_view()(request) - - report = models.Report.objects.get() - self.assertEqual(report.reporter, self.local_user) - self.assertEqual(report.user, self.rat) - - def test_report_link(self): - """a user reports a link as spam""" - book = models.Edition.objects.create(title="hi") - link = models.FileLink.objects.create( - book=book, added_by=self.local_user, url="https://skdjfs.sdf" - ) - domain = link.domain - domain.status = "approved" - domain.save() - - form = forms.ReportForm() - form.data["reporter"] = self.local_user.id - form.data["user"] = self.rat.id - form.data["links"] = link.id - request = self.factory.post("", form.data) - request.user = self.local_user - - views.Report.as_view()(request) - - report = models.Report.objects.get() - domain.refresh_from_db() - self.assertEqual(report.links.first().id, link.id) - self.assertEqual(domain.status, "pending") - def test_resolve_report(self): """toggle report resolution status""" report = models.Report.objects.create(reporter=self.local_user, user=self.rat) diff --git a/bookwyrm/tests/views/test_report.py b/bookwyrm/tests/views/test_report.py new file mode 100644 index 000000000..3600e41e7 --- /dev/null +++ b/bookwyrm/tests/views/test_report.py @@ -0,0 +1,114 @@ +""" test for app action functionality """ +from unittest.mock import patch + +from django.test import TestCase +from django.test.client import RequestFactory + +from bookwyrm import forms, models, views +from bookwyrm.tests.validate_html import validate_html + + +class ReportViews(TestCase): + """every response to a get request, html or json""" + + def setUp(self): + """we need basic test data and mocks""" + self.factory = RequestFactory() + with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( + "bookwyrm.activitystreams.populate_stream_task.delay" + ), patch("bookwyrm.lists_stream.populate_lists_task.delay"): + self.local_user = models.User.objects.create_user( + "mouse@local.com", + "mouse@mouse.mouse", + "password", + local=True, + localname="mouse", + ) + self.rat = models.User.objects.create_user( + "rat@local.com", + "rat@mouse.mouse", + "password", + local=True, + localname="rat", + ) + with patch( + "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async" + ), patch("bookwyrm.activitystreams.add_status_task.delay"): + self.status = models.Status.objects.create( + user=self.local_user, + content="Test status", + ) + models.SiteSettings.objects.create() + + def test_report_modal_view(self): + """a user reports another user""" + request = self.factory.get("") + request.user = self.local_user + result = views.Report.as_view()(request, self.local_user.id) + + validate_html(result.render()) + + def test_report_modal_view_with_status(self): + """a user reports another user""" + request = self.factory.get("") + request.user = self.local_user + result = views.Report.as_view()( + request, + user_id=self.local_user.id, + status_id=self.status.id + ) + + validate_html(result.render()) + + def test_report_modal_view_with_link_domain(self): + """a user reports another user""" + link = models.Link.objects.create( + url="http://example.com/hi", + added_by=self.local_user, + ) + request = self.factory.get("") + request.user = self.local_user + result = views.Report.as_view()( + request, + link_id=link.id + ) + + validate_html(result.render()) + + def test_make_report(self): + """a user reports another user""" + form = forms.ReportForm() + form.data["reporter"] = self.local_user.id + form.data["user"] = self.rat.id + request = self.factory.post("", form.data) + request.user = self.local_user + + views.Report.as_view()(request) + + report = models.Report.objects.get() + self.assertEqual(report.reporter, self.local_user) + self.assertEqual(report.user, self.rat) + + def test_report_link(self): + """a user reports a link as spam""" + book = models.Edition.objects.create(title="hi") + link = models.FileLink.objects.create( + book=book, added_by=self.local_user, url="https://skdjfs.sdf" + ) + domain = link.domain + domain.status = "approved" + domain.save() + + form = forms.ReportForm() + form.data["reporter"] = self.local_user.id + form.data["user"] = self.rat.id + form.data["links"] = link.id + request = self.factory.post("", form.data) + request.user = self.local_user + + views.Report.as_view()(request) + + report = models.Report.objects.get() + domain.refresh_from_db() + self.assertEqual(report.links.first().id, link.id) + self.assertEqual(domain.status, "pending") From f4dd14acadbd3524c673d592eef0efbed0cc8fc1 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 6 Jul 2022 10:05:22 -0700 Subject: [PATCH 3/9] Adds more webfinger tests for helper functions --- bookwyrm/tests/views/test_helpers.py | 60 ++++++++++++++++++++++++++-- bookwyrm/tests/views/test_report.py | 9 +---- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/bookwyrm/tests/views/test_helpers.py b/bookwyrm/tests/views/test_helpers.py index a092a4c9a..ce1f6a735 100644 --- a/bookwyrm/tests/views/test_helpers.py +++ b/bookwyrm/tests/views/test_helpers.py @@ -112,7 +112,17 @@ class ViewsHelpers(TestCase): request = self.factory.get("", {"q": "Test Book"}, HTTP_USER_AGENT=USER_AGENT) self.assertTrue(views.helpers.is_bookwyrm_request(request)) - def test_existing_user(self, *_): + def test_handle_remote_webfinger_invalid(self, *_): + """Various ways you can send a bad query""" + # if there's no query, there's no result + result = views.helpers.handle_remote_webfinger(None) + self.assertIsNone(result) + + # malformed user + result = views.helpers.handle_remote_webfinger("noatsymbol") + self.assertIsNone(result) + + def test_handle_remote_webfinger_existing_user(self, *_): """simple database lookup by username""" result = views.helpers.handle_remote_webfinger("@mouse@local.com") self.assertEqual(result, self.local_user) @@ -124,7 +134,19 @@ class ViewsHelpers(TestCase): self.assertEqual(result, self.local_user) @responses.activate - def test_load_user(self, *_): + def test_handle_remote_webfinger_load_user_invalid_result(self, *_): + """find a remote user using webfinger, but fail""" + username = "mouse@example.com" + responses.add( + responses.GET, + f"https://example.com/.well-known/webfinger?resource=acct:{username}", + status=500, + ) + result = views.helpers.handle_remote_webfinger("@mouse@example.com") + self.assertIsNone(result) + + @responses.activate + def test_handle_remote_webfinger_load_user(self, *_): """find a remote user using webfinger""" username = "mouse@example.com" wellknown = { @@ -154,7 +176,7 @@ class ViewsHelpers(TestCase): self.assertIsInstance(result, models.User) self.assertEqual(result.username, "mouse@example.com") - def test_user_on_blocked_server(self, *_): + def test_handler_remote_webfinger_user_on_blocked_server(self, *_): """find a remote user using webfinger""" models.FederatedServer.objects.create( server_name="example.com", status="blocked" @@ -163,6 +185,38 @@ class ViewsHelpers(TestCase): result = views.helpers.handle_remote_webfinger("@mouse@example.com") self.assertIsNone(result) + @responses.activate + def test_subscribe_remote_webfinger(self, *_): + """remote subscribe templates""" + query = "mouse@example.com" + response = { + "subject": f"acct:{query}", + "links": [ + { + "rel": "self", + "type": "application/activity+json", + "href": "https://example.com/user/mouse", + "template": "hi", + }, + { + "rel": "http://ostatus.org/schema/1.0/subscribe", + "type": "application/activity+json", + "href": "https://example.com/user/mouse", + "template": "hello", + }, + ], + } + responses.add( + responses.GET, + f"https://example.com/.well-known/webfinger?resource=acct:{query}", + json=response, + status=200, + ) + template = views.helpers.subscribe_remote_webfinger(query) + self.assertEqual(template, "hello") + template = views.helpers.subscribe_remote_webfinger(f"@{query}") + self.assertEqual(template, "hello") + def test_handle_reading_status_to_read(self, *_): """posts shelve activities""" shelf = self.local_user.shelf_set.get(identifier="to-read") diff --git a/bookwyrm/tests/views/test_report.py b/bookwyrm/tests/views/test_report.py index 3600e41e7..85dca8aea 100644 --- a/bookwyrm/tests/views/test_report.py +++ b/bookwyrm/tests/views/test_report.py @@ -53,9 +53,7 @@ class ReportViews(TestCase): request = self.factory.get("") request.user = self.local_user result = views.Report.as_view()( - request, - user_id=self.local_user.id, - status_id=self.status.id + request, user_id=self.local_user.id, status_id=self.status.id ) validate_html(result.render()) @@ -68,10 +66,7 @@ class ReportViews(TestCase): ) request = self.factory.get("") request.user = self.local_user - result = views.Report.as_view()( - request, - link_id=link.id - ) + result = views.Report.as_view()(request, link_id=link.id) validate_html(result.render()) From f5638c1e44839d0d593d98338157eeefd42f0e96 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 6 Jul 2022 10:05:55 -0700 Subject: [PATCH 4/9] Removed unused helper function --- bookwyrm/views/helpers.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/bookwyrm/views/helpers.py b/bookwyrm/views/helpers.py index 7d8eced7c..7be42a87b 100644 --- a/bookwyrm/views/helpers.py +++ b/bookwyrm/views/helpers.py @@ -148,13 +148,6 @@ def handle_reading_status(user, shelf, book, privacy): status.save() -def is_blocked(viewer, user): - """is this viewer blocked by the user?""" - if viewer.is_authenticated and viewer in user.blocks.all(): - return True - return False - - def load_date_in_user_tz_as_utc(date_str: str, user: models.User) -> datetime: """ensures that data is stored consistently in the UTC timezone""" if not date_str: From 87434fbb9df733cc9552402cfedc242c4c950cdd Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 6 Jul 2022 10:38:22 -0700 Subject: [PATCH 5/9] Adds a couple more edit book views tests --- bookwyrm/tests/views/books/test_edit_book.py | 78 ++++++++++++++++---- 1 file changed, 65 insertions(+), 13 deletions(-) diff --git a/bookwyrm/tests/views/books/test_edit_book.py b/bookwyrm/tests/views/books/test_edit_book.py index c7869807b..1ac8336d8 100644 --- a/bookwyrm/tests/views/books/test_edit_book.py +++ b/bookwyrm/tests/views/books/test_edit_book.py @@ -48,7 +48,7 @@ class EditBookViews(TestCase): models.SiteSettings.objects.create() - def test_edit_book_page(self): + def test_edit_book_get(self): """there are so many views, this just makes sure it LOADS""" view = views.EditBook.as_view() request = self.factory.get("") @@ -59,18 +59,7 @@ class EditBookViews(TestCase): validate_html(result.render()) self.assertEqual(result.status_code, 200) - def test_edit_book_create_page(self): - """there are so many views, this just makes sure it LOADS""" - view = views.CreateBook.as_view() - request = self.factory.get("") - request.user = self.local_user - request.user.is_superuser = True - result = view(request) - self.assertIsInstance(result, TemplateResponse) - validate_html(result.render()) - self.assertEqual(result.status_code, 200) - - def test_edit_book(self): + def test_edit_book_post(self): """lets a user edit a book""" view = views.EditBook.as_view() self.local_user.groups.add(self.group) @@ -86,6 +75,23 @@ class EditBookViews(TestCase): self.book.refresh_from_db() self.assertEqual(self.book.title, "New Title") + def test_edit_book_post_invalid(self): + """book form is invalid""" + view = views.EditBook.as_view() + self.local_user.groups.add(self.group) + form = forms.EditionForm(instance=self.book) + form.data["title"] = "" + form.data["last_edited_by"] = self.local_user.id + request = self.factory.post("", form.data) + request.user = self.local_user + + result = view(request, self.book.id) + validate_html(result.render()) + self.assertEqual(result.status_code, 200) + # Title is unchanged + self.book.refresh_from_db() + self.assertEqual(self.book.title, "Example Edition") + def test_edit_book_add_author(self): """lets a user edit a book with new authors""" view = views.EditBook.as_view() @@ -234,3 +240,49 @@ class EditBookViews(TestCase): self.assertEqual(len(result["author_matches"]), 2) self.assertEqual(result["author_matches"][0]["name"], "Sappho") self.assertEqual(result["author_matches"][1]["name"], "Some Guy") + + def test_create_book_get(self): + """there are so many views, this just makes sure it LOADS""" + view = views.CreateBook.as_view() + request = self.factory.get("") + request.user = self.local_user + request.user.is_superuser = True + result = view(request) + self.assertIsInstance(result, TemplateResponse) + validate_html(result.render()) + self.assertEqual(result.status_code, 200) + + def test_create_book_post_existing_work(self): + """Adding an edition to an existing work""" + author = models.Author.objects.create(name="Sappho") + view = views.CreateBook.as_view() + form = forms.EditionForm() + form.data["title"] = "A Title" + form.data["parent_work"] = self.work.id + form.data["authors"] = [author.id] + form.data["last_edited_by"] = self.local_user.id + request = self.factory.post("", form.data) + request.user = self.local_user + request.user.is_superuser = True + + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"): + result = view(request) + self.assertEqual(result.status_code, 302) + + new_edition = models.Edition.objects.get(title="A Title") + self.assertEqual(new_edition.parent_work, self.work) + self.assertEqual(new_edition.authors.first(), author) + + def test_create_book_post_invalid(self): + """book form is invalid""" + view = views.CreateBook.as_view() + self.local_user.groups.add(self.group) + form = forms.EditionForm(instance=self.book) + form.data["title"] = "" + form.data["last_edited_by"] = self.local_user.id + request = self.factory.post("", form.data) + request.user = self.local_user + + result = view(request) + validate_html(result.render()) + self.assertEqual(result.status_code, 200) From 574c1db7325e38cd693046bcef4ef47e2d0d2753 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 6 Jul 2022 10:50:50 -0700 Subject: [PATCH 6/9] Adds more templatetag tests --- bookwyrm/templatetags/utilities.py | 2 +- bookwyrm/tests/templatetags/test_utilities.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/bookwyrm/templatetags/utilities.py b/bookwyrm/templatetags/utilities.py index 00fa4035c..834d39a14 100644 --- a/bookwyrm/templatetags/utilities.py +++ b/bookwyrm/templatetags/utilities.py @@ -53,7 +53,7 @@ def comparison_bool(str1, str2, reverse=False): @register.filter(is_safe=True) def truncatepath(value, arg): - """Truncate a path by removing all directories except the first and truncating .""" + """Truncate a path by removing all directories except the first and truncating""" path = os.path.normpath(value.name) path_list = path.split(os.sep) try: diff --git a/bookwyrm/tests/templatetags/test_utilities.py b/bookwyrm/tests/templatetags/test_utilities.py index 0136ca8cd..7738a51d9 100644 --- a/bookwyrm/tests/templatetags/test_utilities.py +++ b/bookwyrm/tests/templatetags/test_utilities.py @@ -1,4 +1,5 @@ """ style fixes and lookups for templates """ +from collections import namedtuple import re from unittest.mock import patch @@ -61,3 +62,18 @@ class UtilitiesTags(TestCase): self.assertEqual(utilities.get_title(self.book), "Test Book") book = models.Edition.objects.create(title="Oh", subtitle="oh my") self.assertEqual(utilities.get_title(book), "Oh: oh my") + + def test_comparison_bool(self, *_): + """just a simple comparison""" + self.assertTrue(utilities.comparison_bool("a", "a")) + self.assertFalse(utilities.comparison_bool("a", "b")) + + self.assertFalse(utilities.comparison_bool("a", "a", reverse=True)) + self.assertTrue(utilities.comparison_bool("a", "b", reverse=True)) + + def test_truncatepath(self, *_): + """truncate a path""" + ValueMock = namedtuple("Value", ("name")) + value = ValueMock("home/one/two/three/four") + self.assertEqual(utilities.truncatepath(value, 2), "home/…ur") + self.assertEqual(utilities.truncatepath(value, "a"), "four") From 7a9d320afd21fdb62693c2a11938341b1a298ab4 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 6 Jul 2022 10:52:50 -0700 Subject: [PATCH 7/9] Expands automod view test --- bookwyrm/tests/views/admin/test_automod.py | 5 +++++ bookwyrm/views/admin/automod.py | 3 +-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/bookwyrm/tests/views/admin/test_automod.py b/bookwyrm/tests/views/admin/test_automod.py index 1ed36caf5..98e342ba7 100644 --- a/bookwyrm/tests/views/admin/test_automod.py +++ b/bookwyrm/tests/views/admin/test_automod.py @@ -53,6 +53,11 @@ class AutomodViews(TestCase): request.user.is_superuser = True result = view(request) + self.assertIsInstance(result, TemplateResponse) validate_html(result.render()) self.assertEqual(result.status_code, 200) + + rule = models.AutoMod.objects.get() + self.assertTrue(rule.flag_users) + self.assertFalse(rule.flag_statuses) diff --git a/bookwyrm/views/admin/automod.py b/bookwyrm/views/admin/automod.py index f8c3e8e67..65eae71a9 100644 --- a/bookwyrm/views/admin/automod.py +++ b/bookwyrm/views/admin/automod.py @@ -33,8 +33,7 @@ class AutoMod(View): def post(self, request): """add rule""" form = forms.AutoModRuleForm(request.POST) - success = form.is_valid() - if success: + if form.is_valid(): form.save() form = forms.AutoModRuleForm() From 9c21d1c06fb692d7a23389ed9e3137f5a1123a19 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 6 Jul 2022 11:15:05 -0700 Subject: [PATCH 8/9] Adds more automod tests --- bookwyrm/tests/views/admin/test_automod.py | 56 +++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/bookwyrm/tests/views/admin/test_automod.py b/bookwyrm/tests/views/admin/test_automod.py index 98e342ba7..443ec2ee5 100644 --- a/bookwyrm/tests/views/admin/test_automod.py +++ b/bookwyrm/tests/views/admin/test_automod.py @@ -4,6 +4,7 @@ from unittest.mock import patch from django.template.response import TemplateResponse from django.test import TestCase from django.test.client import RequestFactory +from django_celery_beat.models import PeriodicTask, IntervalSchedule from bookwyrm import forms, models, views from bookwyrm.tests.validate_html import validate_html @@ -28,6 +29,43 @@ class AutomodViews(TestCase): models.SiteSettings.objects.create() def test_automod_rules_get(self): + """there are so many views, this just makes sure it LOADS""" + schedule = IntervalSchedule.objects.create(every=1, period="days") + PeriodicTask.objects.create( + interval=schedule, + name="automod-task", + task="bookwyrm.models.antispam.automod_task", + ) + models.AutoMod.objects.create(created_by=self.local_user, string_match="hello") + view = views.AutoMod.as_view() + request = self.factory.get("") + request.user = self.local_user + request.user.is_superuser = True + + result = view(request) + self.assertIsInstance(result, TemplateResponse) + validate_html(result.render()) + self.assertEqual(result.status_code, 200) + + def test_automod_rules_get_empty_with_schedule(self): + """there are so many views, this just makes sure it LOADS""" + schedule = IntervalSchedule.objects.create(every=1, period="days") + PeriodicTask.objects.create( + interval=schedule, + name="automod-task", + task="bookwyrm.models.antispam.automod_task", + ) + view = views.AutoMod.as_view() + request = self.factory.get("") + request.user = self.local_user + request.user.is_superuser = True + + result = view(request) + self.assertIsInstance(result, TemplateResponse) + validate_html(result.render()) + self.assertEqual(result.status_code, 200) + + def test_automod_rules_get_empty_without_schedule(self): """there are so many views, this just makes sure it LOADS""" view = views.AutoMod.as_view() request = self.factory.get("") @@ -45,7 +83,7 @@ class AutomodViews(TestCase): form.data["string_match"] = "hello" form.data["flag_users"] = True form.data["flag_statuses"] = False - form.data["created_by"] = self.local_user + form.data["created_by"] = self.local_user.id view = views.AutoMod.as_view() request = self.factory.post("", form.data) @@ -61,3 +99,19 @@ class AutomodViews(TestCase): rule = models.AutoMod.objects.get() self.assertTrue(rule.flag_users) self.assertFalse(rule.flag_statuses) + + def test_schedule_automod_task(self): + """Schedule the task""" + self.assertFalse(IntervalSchedule.objects.exists()) + + form = forms.IntervalScheduleForm() + form.data["every"] = 1 + form.data["period"] = "days" + request = self.factory.post("", form.data) + request.user = self.local_user + request.user.is_superuser = True + + response = views.schedule_automod_task(request) + self.assertEqual(response.status_code, 302) + + self.assertTrue(IntervalSchedule.objects.exists()) From b2a8a0409238b6e109aaa8b4d46f06b7c0b55772 Mon Sep 17 00:00:00 2001 From: Mouse Reeve Date: Wed, 6 Jul 2022 11:24:04 -0700 Subject: [PATCH 9/9] Removes unnecessary imports --- bookwyrm/tests/views/admin/test_reports.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bookwyrm/tests/views/admin/test_reports.py b/bookwyrm/tests/views/admin/test_reports.py index 48a81239b..059cfdd8a 100644 --- a/bookwyrm/tests/views/admin/test_reports.py +++ b/bookwyrm/tests/views/admin/test_reports.py @@ -6,7 +6,7 @@ from django.template.response import TemplateResponse from django.test import TestCase from django.test.client import RequestFactory -from bookwyrm import forms, models, views +from bookwyrm import models, views from bookwyrm.tests.validate_html import validate_html