diff --git a/bookwyrm/tests/activitystreams/__init__.py b/bookwyrm/tests/activitystreams/__init__.py new file mode 100644 index 000000000..b6e690fd5 --- /dev/null +++ b/bookwyrm/tests/activitystreams/__init__.py @@ -0,0 +1 @@ +from . import * diff --git a/bookwyrm/tests/activitystreams/test_abstractstream.py b/bookwyrm/tests/activitystreams/test_abstractstream.py new file mode 100644 index 000000000..f96742865 --- /dev/null +++ b/bookwyrm/tests/activitystreams/test_abstractstream.py @@ -0,0 +1,145 @@ +""" testing activitystreams """ +from unittest.mock import patch +from django.test import TestCase +from bookwyrm import activitystreams, models + + +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.activitystreams.add_status_task.delay") +@patch("bookwyrm.activitystreams.add_book_statuses_task.delay") +@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay") +@patch("bookwyrm.activitystreams.populate_stream_task.delay") +class Activitystreams(TestCase): + """using redis to build activity streams""" + + def setUp(self): + """use a test csv""" + with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( + "bookwyrm.activitystreams.populate_stream_task.delay" + ): + self.local_user = models.User.objects.create_user( + "mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse" + ) + self.another_user = models.User.objects.create_user( + "nutria", + "nutria@nutria.nutria", + "password", + local=True, + localname="nutria", + ) + with patch("bookwyrm.models.user.set_remote_server.delay"): + self.remote_user = models.User.objects.create_user( + "rat", + "rat@rat.com", + "ratword", + local=False, + remote_id="https://example.com/users/rat", + inbox="https://example.com/users/rat/inbox", + outbox="https://example.com/users/rat/outbox", + ) + work = models.Work.objects.create(title="test work") + self.book = models.Edition.objects.create(title="test book", parent_work=work) + + class TestStream(activitystreams.ActivityStream): + """test stream, don't have to do anything here""" + + key = "test" + + self.test_stream = TestStream() + + def test_activitystream_class_ids(self, *_): + """the abstract base class for stream objects""" + self.assertEqual( + self.test_stream.stream_id(self.local_user), + "{}-test".format(self.local_user.id), + ) + self.assertEqual( + self.test_stream.unread_id(self.local_user), + "{}-test-unread".format(self.local_user.id), + ) + + def test_abstractstream_get_audience(self, *_): + """get a list of users that should see a status""" + status = models.Status.objects.create( + user=self.remote_user, content="hi", privacy="public" + ) + users = self.test_stream.get_audience(status) + # remote users don't have feeds + self.assertFalse(self.remote_user in users) + self.assertTrue(self.local_user in users) + self.assertTrue(self.another_user in users) + + def test_abstractstream_get_audience_direct(self, *_): + """get a list of users that should see a status""" + status = models.Status.objects.create( + user=self.remote_user, + content="hi", + privacy="direct", + ) + status.mention_users.add(self.local_user) + users = self.test_stream.get_audience(status) + self.assertEqual(users, []) + + status = models.Comment.objects.create( + user=self.remote_user, + content="hi", + privacy="direct", + book=self.book, + ) + status.mention_users.add(self.local_user) + users = self.test_stream.get_audience(status) + self.assertTrue(self.local_user in users) + self.assertFalse(self.another_user in users) + self.assertFalse(self.remote_user in users) + + def test_abstractstream_get_audience_followers_remote_user(self, *_): + """get a list of users that should see a status""" + status = models.Status.objects.create( + user=self.remote_user, + content="hi", + privacy="followers", + ) + users = self.test_stream.get_audience(status) + self.assertFalse(users.exists()) + + def test_abstractstream_get_audience_followers_self(self, *_): + """get a list of users that should see a status""" + status = models.Comment.objects.create( + user=self.local_user, + content="hi", + privacy="direct", + book=self.book, + ) + users = self.test_stream.get_audience(status) + self.assertTrue(self.local_user in users) + self.assertFalse(self.another_user in users) + self.assertFalse(self.remote_user in users) + + def test_abstractstream_get_audience_followers_with_mention(self, *_): + """get a list of users that should see a status""" + status = models.Comment.objects.create( + user=self.remote_user, + content="hi", + privacy="direct", + book=self.book, + ) + status.mention_users.add(self.local_user) + + users = self.test_stream.get_audience(status) + self.assertTrue(self.local_user in users) + self.assertFalse(self.another_user in users) + self.assertFalse(self.remote_user in users) + + def test_abstractstream_get_audience_followers_with_relationship(self, *_): + """get a list of users that should see a status""" + self.remote_user.followers.add(self.local_user) + status = models.Comment.objects.create( + user=self.remote_user, + content="hi", + privacy="direct", + book=self.book, + ) + users = self.test_stream.get_audience(status) + self.assertFalse(self.local_user in users) + self.assertFalse(self.another_user in users) + self.assertFalse(self.remote_user in users) diff --git a/bookwyrm/tests/activitystreams/test_booksstream.py b/bookwyrm/tests/activitystreams/test_booksstream.py new file mode 100644 index 000000000..d6d94b738 --- /dev/null +++ b/bookwyrm/tests/activitystreams/test_booksstream.py @@ -0,0 +1,54 @@ +""" testing activitystreams """ +from unittest.mock import patch +from django.test import TestCase +from bookwyrm import activitystreams, models + + +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.activitystreams.add_status_task.delay") +@patch("bookwyrm.activitystreams.add_book_statuses_task.delay") +@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay") +@patch("bookwyrm.activitystreams.populate_stream_task.delay") +class Activitystreams(TestCase): + """using redis to build activity streams""" + + def setUp(self): + """use a test csv""" + with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( + "bookwyrm.activitystreams.populate_stream_task.delay" + ): + self.local_user = models.User.objects.create_user( + "mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse" + ) + with patch("bookwyrm.models.user.set_remote_server.delay"): + self.remote_user = models.User.objects.create_user( + "rat", + "rat@rat.com", + "ratword", + local=False, + remote_id="https://example.com/users/rat", + inbox="https://example.com/users/rat/inbox", + outbox="https://example.com/users/rat/outbox", + ) + work = models.Work.objects.create(title="test work") + self.book = models.Edition.objects.create(title="test book", parent_work=work) + + def test_get_statuses_for_user_books(self, *_): + """create a stream for a user""" + alt_book = models.Edition.objects.create( + title="hi", parent_work=self.book.parent_work + ) + status = models.Status.objects.create( + user=self.local_user, content="hi", privacy="public" + ) + status = models.Comment.objects.create( + user=self.remote_user, content="hi", privacy="public", book=alt_book + ) + models.ShelfBook.objects.create( + user=self.local_user, + shelf=self.local_user.shelf_set.first(), + book=self.book, + ) + # yes book, yes audience + result = activitystreams.BooksStream().get_statuses_for_user(self.local_user) + self.assertEqual(list(result), [status]) diff --git a/bookwyrm/tests/activitystreams/test_homestream.py b/bookwyrm/tests/activitystreams/test_homestream.py new file mode 100644 index 000000000..42a73f3cf --- /dev/null +++ b/bookwyrm/tests/activitystreams/test_homestream.py @@ -0,0 +1,67 @@ +""" testing activitystreams """ +from unittest.mock import patch +from django.test import TestCase +from bookwyrm import activitystreams, models + + +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.activitystreams.add_status_task.delay") +@patch("bookwyrm.activitystreams.add_book_statuses_task.delay") +@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay") +@patch("bookwyrm.activitystreams.populate_stream_task.delay") +class Activitystreams(TestCase): + """using redis to build activity streams""" + + def setUp(self): + """use a test csv""" + with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( + "bookwyrm.activitystreams.populate_stream_task.delay" + ): + self.local_user = models.User.objects.create_user( + "mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse" + ) + self.another_user = models.User.objects.create_user( + "nutria", + "nutria@nutria.nutria", + "password", + local=True, + localname="nutria", + ) + with patch("bookwyrm.models.user.set_remote_server.delay"): + self.remote_user = models.User.objects.create_user( + "rat", + "rat@rat.com", + "ratword", + local=False, + remote_id="https://example.com/users/rat", + inbox="https://example.com/users/rat/inbox", + outbox="https://example.com/users/rat/outbox", + ) + + def test_homestream_get_audience(self, *_): + """get a list of users that should see a status""" + status = models.Status.objects.create( + user=self.remote_user, content="hi", privacy="public" + ) + users = activitystreams.HomeStream().get_audience(status) + self.assertFalse(users.exists()) + + def test_homestream_get_audience_with_mentions(self, *_): + """get a list of users that should see a status""" + status = models.Status.objects.create( + user=self.remote_user, content="hi", privacy="public" + ) + status.mention_users.add(self.local_user) + users = activitystreams.HomeStream().get_audience(status) + self.assertFalse(self.local_user in users) + self.assertFalse(self.another_user in users) + + def test_homestream_get_audience_with_relationship(self, *_): + """get a list of users that should see a status""" + self.remote_user.followers.add(self.local_user) + status = models.Status.objects.create( + user=self.remote_user, content="hi", privacy="public" + ) + users = activitystreams.HomeStream().get_audience(status) + self.assertTrue(self.local_user in users) + self.assertFalse(self.another_user in users) diff --git a/bookwyrm/tests/activitystreams/test_localstream.py b/bookwyrm/tests/activitystreams/test_localstream.py new file mode 100644 index 000000000..e6b05557f --- /dev/null +++ b/bookwyrm/tests/activitystreams/test_localstream.py @@ -0,0 +1,139 @@ +""" testing activitystreams """ +from unittest.mock import patch +from django.test import TestCase +from bookwyrm import activitystreams, models + + +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +@patch("bookwyrm.activitystreams.add_status_task.delay") +@patch("bookwyrm.activitystreams.add_book_statuses_task.delay") +@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay") +@patch("bookwyrm.activitystreams.populate_stream_task.delay") +class Activitystreams(TestCase): + """using redis to build activity streams""" + + def setUp(self): + """use a test csv""" + with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( + "bookwyrm.activitystreams.populate_stream_task.delay" + ): + self.local_user = models.User.objects.create_user( + "mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse" + ) + self.another_user = models.User.objects.create_user( + "nutria", + "nutria@nutria.nutria", + "password", + local=True, + localname="nutria", + ) + with patch("bookwyrm.models.user.set_remote_server.delay"): + self.remote_user = models.User.objects.create_user( + "rat", + "rat@rat.com", + "ratword", + local=False, + remote_id="https://example.com/users/rat", + inbox="https://example.com/users/rat/inbox", + outbox="https://example.com/users/rat/outbox", + ) + work = models.Work.objects.create(title="test work") + self.book = models.Edition.objects.create(title="test book", parent_work=work) + + def test_localstream_get_audience_remote_status(self, *_): + """get a list of users that should see a status""" + status = models.Status.objects.create( + user=self.remote_user, content="hi", privacy="public" + ) + users = activitystreams.LocalStream().get_audience(status) + self.assertEqual(users, []) + + def test_localstream_get_audience_local_status(self, *_): + """get a list of users that should see a status""" + status = models.Status.objects.create( + user=self.local_user, content="hi", privacy="public" + ) + users = activitystreams.LocalStream().get_audience(status) + self.assertTrue(self.local_user in users) + self.assertTrue(self.another_user in users) + + def test_localstream_get_audience_unlisted(self, *_): + """get a list of users that should see a status""" + status = models.Status.objects.create( + user=self.local_user, content="hi", privacy="unlisted" + ) + users = activitystreams.LocalStream().get_audience(status) + self.assertEqual(users, []) + + def test_localstream_get_audience_books_no_book(self, *_): + """get a list of users that should see a status""" + status = models.Status.objects.create( + user=self.local_user, content="hi", privacy="public" + ) + audience = activitystreams.BooksStream().get_audience(status) + # no books, no audience + self.assertEqual(audience, []) + + def test_localstream_get_audience_books_mention_books(self, *_): + """get a list of users that should see a status""" + status = models.Status.objects.create( + user=self.local_user, content="hi", privacy="public" + ) + status.mention_books.add(self.book) + status.save(broadcast=False) + models.ShelfBook.objects.create( + user=self.local_user, + shelf=self.local_user.shelf_set.first(), + book=self.book, + ) + # yes book, yes audience + audience = activitystreams.BooksStream().get_audience(status) + self.assertTrue(self.local_user in audience) + + def test_localstream_get_audience_books_book_field(self, *_): + """get a list of users that should see a status""" + status = models.Comment.objects.create( + user=self.local_user, content="hi", privacy="public", book=self.book + ) + models.ShelfBook.objects.create( + user=self.local_user, + shelf=self.local_user.shelf_set.first(), + book=self.book, + ) + # yes book, yes audience + audience = activitystreams.BooksStream().get_audience(status) + self.assertTrue(self.local_user in audience) + + def test_localstream_get_audience_books_alternate_edition(self, *_): + """get a list of users that should see a status""" + alt_book = models.Edition.objects.create( + title="hi", parent_work=self.book.parent_work + ) + status = models.Comment.objects.create( + user=self.remote_user, content="hi", privacy="public", book=alt_book + ) + models.ShelfBook.objects.create( + user=self.local_user, + shelf=self.local_user.shelf_set.first(), + book=self.book, + ) + # yes book, yes audience + audience = activitystreams.BooksStream().get_audience(status) + self.assertTrue(self.local_user in audience) + + def test_localstream_get_audience_books_non_public(self, *_): + """get a list of users that should see a status""" + alt_book = models.Edition.objects.create( + title="hi", parent_work=self.book.parent_work + ) + status = models.Comment.objects.create( + user=self.remote_user, content="hi", privacy="unlisted", book=alt_book + ) + models.ShelfBook.objects.create( + user=self.local_user, + shelf=self.local_user.shelf_set.first(), + book=self.book, + ) + # yes book, yes audience + audience = activitystreams.BooksStream().get_audience(status) + self.assertEqual(audience, []) diff --git a/bookwyrm/tests/activitystreams/test_signals.py b/bookwyrm/tests/activitystreams/test_signals.py new file mode 100644 index 000000000..1c94cc9f5 --- /dev/null +++ b/bookwyrm/tests/activitystreams/test_signals.py @@ -0,0 +1,68 @@ +""" testing activitystreams """ +from unittest.mock import patch +from django.test import TestCase +from bookwyrm import activitystreams, models + + +@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") +class ActivitystreamsSignals(TestCase): + """using redis to build activity streams""" + + def setUp(self): + """use a test csv""" + with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( + "bookwyrm.activitystreams.populate_stream_task.delay" + ): + self.local_user = models.User.objects.create_user( + "mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse" + ) + with patch("bookwyrm.models.user.set_remote_server.delay"): + self.remote_user = models.User.objects.create_user( + "rat", + "rat@rat.com", + "ratword", + local=False, + remote_id="https://example.com/users/rat", + inbox="https://example.com/users/rat/inbox", + outbox="https://example.com/users/rat/outbox", + ) + work = models.Work.objects.create(title="test work") + self.book = models.Edition.objects.create(title="test book", parent_work=work) + + def test_add_status_on_create_ignore(self, _): + """a new statuses has entered""" + activitystreams.add_status_on_create(models.User, self.local_user, False) + + def test_add_status_on_create_deleted(self, _): + """a new statuses has entered""" + with patch("bookwyrm.activitystreams.remove_status_task.delay"): + status = models.Status.objects.create( + user=self.remote_user, content="hi", privacy="public", deleted=True + ) + with patch("bookwyrm.activitystreams.remove_status_task.delay") as mock: + activitystreams.add_status_on_create(models.Status, status, False) + self.assertEqual(mock.call_count, 1) + args = mock.call_args[0] + self.assertEqual(args[0], status.id) + + def test_add_status_on_create_created(self, _): + """a new statuses has entered""" + status = models.Status.objects.create( + user=self.remote_user, content="hi", privacy="public" + ) + with patch("bookwyrm.activitystreams.add_status_task.delay") as mock: + activitystreams.add_status_on_create_command(models.Status, status, False) + self.assertEqual(mock.call_count, 1) + args = mock.call_args[0] + self.assertEqual(args[0], status.id) + + def test_populate_streams_on_account_create(self, _): + """create streams for a user""" + with patch("bookwyrm.activitystreams.populate_stream_task.delay") as mock: + activitystreams.populate_streams_on_account_create( + models.User, self.local_user, True + ) + self.assertEqual(mock.call_count, 3) + args = mock.call_args[0] + self.assertEqual(args[0], "books") + self.assertEqual(args[1], self.local_user.id) diff --git a/bookwyrm/tests/activitystreams/test_tasks.py b/bookwyrm/tests/activitystreams/test_tasks.py new file mode 100644 index 000000000..f4c85e1bf --- /dev/null +++ b/bookwyrm/tests/activitystreams/test_tasks.py @@ -0,0 +1,193 @@ +""" testing activitystreams """ +from unittest.mock import patch +from django.test import TestCase +from bookwyrm import activitystreams, models + + +class Activitystreams(TestCase): + """using redis to build activity streams""" + + def setUp(self): + """use a test csv""" + with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( + "bookwyrm.activitystreams.populate_stream_task.delay" + ): + self.local_user = models.User.objects.create_user( + "mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse" + ) + self.another_user = models.User.objects.create_user( + "nutria", + "nutria@nutria.nutria", + "password", + local=True, + localname="nutria", + ) + work = models.Work.objects.create(title="test work") + self.book = models.Edition.objects.create(title="test book", parent_work=work) + with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"): + self.status = models.Status.objects.create( + content="hi", user=self.local_user + ) + + def test_add_book_statuses_task(self): + """statuses related to a book""" + with patch("bookwyrm.activitystreams.BooksStream.add_book_statuses") as mock: + activitystreams.add_book_statuses_task(self.local_user.id, self.book.id) + self.assertTrue(mock.called) + args = mock.call_args[0] + self.assertEqual(args[0], self.local_user) + self.assertEqual(args[1], self.book) + + def test_remove_book_statuses_task(self): + """remove stauses related to a book""" + with patch("bookwyrm.activitystreams.BooksStream.remove_book_statuses") as mock: + activitystreams.remove_book_statuses_task(self.local_user.id, self.book.id) + self.assertTrue(mock.called) + args = mock.call_args[0] + self.assertEqual(args[0], self.local_user) + self.assertEqual(args[1], self.book) + + def test_populate_stream_task(self): + """populate a given stream""" + with patch("bookwyrm.activitystreams.BooksStream.populate_streams") as mock: + activitystreams.populate_stream_task("books", self.local_user.id) + self.assertTrue(mock.called) + args = mock.call_args[0] + self.assertEqual(args[0], self.local_user) + + with patch("bookwyrm.activitystreams.HomeStream.populate_streams") as mock: + activitystreams.populate_stream_task("home", self.local_user.id) + self.assertTrue(mock.called) + args = mock.call_args[0] + self.assertEqual(args[0], self.local_user) + + def test_remove_status_task(self): + """remove a status from all streams""" + with patch( + "bookwyrm.activitystreams.ActivityStream.remove_object_from_related_stores" + ) as mock: + activitystreams.remove_status_task(self.status.id) + self.assertEqual(mock.call_count, 3) + args = mock.call_args[0] + self.assertEqual(args[0], self.status) + + def test_add_status_task(self): + """add a status to all streams""" + with patch("bookwyrm.activitystreams.ActivityStream.add_status") as mock: + activitystreams.add_status_task(self.status.id) + self.assertEqual(mock.call_count, 3) + args = mock.call_args[0] + self.assertEqual(args[0], self.status) + + def test_remove_user_statuses_task(self): + """remove all statuses by a user from another users' feeds""" + with patch( + "bookwyrm.activitystreams.ActivityStream.remove_user_statuses" + ) as mock: + activitystreams.remove_user_statuses_task( + self.local_user.id, self.another_user.id + ) + self.assertEqual(mock.call_count, 3) + args = mock.call_args[0] + self.assertEqual(args[0], self.local_user) + self.assertEqual(args[1], self.another_user) + + with patch("bookwyrm.activitystreams.HomeStream.remove_user_statuses") as mock: + activitystreams.remove_user_statuses_task( + self.local_user.id, self.another_user.id, stream_list=["home"] + ) + self.assertEqual(mock.call_count, 1) + args = mock.call_args[0] + self.assertEqual(args[0], self.local_user) + self.assertEqual(args[1], self.another_user) + + def test_add_user_statuses_task(self): + """add a user's statuses to another users feeds""" + with patch("bookwyrm.activitystreams.ActivityStream.add_user_statuses") as mock: + activitystreams.add_user_statuses_task( + self.local_user.id, self.another_user.id + ) + self.assertEqual(mock.call_count, 3) + args = mock.call_args[0] + self.assertEqual(args[0], self.local_user) + self.assertEqual(args[1], self.another_user) + + with patch("bookwyrm.activitystreams.HomeStream.add_user_statuses") as mock: + activitystreams.add_user_statuses_task( + self.local_user.id, self.another_user.id, stream_list=["home"] + ) + self.assertEqual(mock.call_count, 1) + args = mock.call_args[0] + self.assertEqual(args[0], self.local_user) + self.assertEqual(args[1], self.another_user) + + @patch("bookwyrm.activitystreams.LocalStream.remove_object_from_related_stores") + @patch("bookwyrm.activitystreams.BooksStream.remove_object_from_related_stores") + @patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") + def test_boost_to_another_timeline(self, *_): + """add a boost and deduplicate the boosted status on the timeline""" + status = models.Status.objects.create(user=self.local_user, content="hi") + with patch("bookwyrm.activitystreams.handle_boost_task.delay"): + boost = models.Boost.objects.create( + boosted_status=status, + user=self.another_user, + ) + with patch( + "bookwyrm.activitystreams.HomeStream.remove_object_from_related_stores" + ) as mock: + activitystreams.handle_boost_task(boost.id) + + self.assertTrue(mock.called) + call_args = mock.call_args + self.assertEqual(call_args[0][0], status) + self.assertEqual( + call_args[1]["stores"], ["{:d}-home".format(self.another_user.id)] + ) + + @patch("bookwyrm.activitystreams.LocalStream.remove_object_from_related_stores") + @patch("bookwyrm.activitystreams.BooksStream.remove_object_from_related_stores") + @patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") + def test_boost_to_following_timeline(self, *_): + """add a boost and deduplicate the boosted status on the timeline""" + self.local_user.following.add(self.another_user) + status = models.Status.objects.create(user=self.local_user, content="hi") + with patch("bookwyrm.activitystreams.handle_boost_task.delay"): + boost = models.Boost.objects.create( + boosted_status=status, + user=self.another_user, + ) + with patch( + "bookwyrm.activitystreams.HomeStream.remove_object_from_related_stores" + ) as mock: + activitystreams.handle_boost_task(boost.id) + self.assertTrue(mock.called) + call_args = mock.call_args + self.assertEqual(call_args[0][0], status) + self.assertTrue( + "{:d}-home".format(self.another_user.id) in call_args[1]["stores"] + ) + self.assertTrue( + "{:d}-home".format(self.local_user.id) in call_args[1]["stores"] + ) + + @patch("bookwyrm.activitystreams.LocalStream.remove_object_from_related_stores") + @patch("bookwyrm.activitystreams.BooksStream.remove_object_from_related_stores") + @patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") + def test_boost_to_same_timeline(self, *_): + """add a boost and deduplicate the boosted status on the timeline""" + status = models.Status.objects.create(user=self.local_user, content="hi") + with patch("bookwyrm.activitystreams.handle_boost_task.delay"): + boost = models.Boost.objects.create( + boosted_status=status, + user=self.local_user, + ) + with patch( + "bookwyrm.activitystreams.HomeStream.remove_object_from_related_stores" + ) as mock: + activitystreams.handle_boost_task(boost.id) + self.assertTrue(mock.called) + call_args = mock.call_args + self.assertEqual(call_args[0][0], status) + self.assertEqual( + call_args[1]["stores"], ["{:d}-home".format(self.local_user.id)] + ) diff --git a/bookwyrm/tests/test_activitystreams.py b/bookwyrm/tests/test_activitystreams.py deleted file mode 100644 index add5875f7..000000000 --- a/bookwyrm/tests/test_activitystreams.py +++ /dev/null @@ -1,360 +0,0 @@ -""" testing activitystreams """ -from unittest.mock import patch -from django.test import TestCase -from bookwyrm import activitystreams, models - - -@patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay") -@patch("bookwyrm.activitystreams.add_status_task.delay") -@patch("bookwyrm.activitystreams.add_book_statuses_task.delay") -@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay") -@patch("bookwyrm.activitystreams.populate_stream_task.delay") -# pylint: disable=too-many-public-methods -class Activitystreams(TestCase): - """using redis to build activity streams""" - - def setUp(self): - """use a test csv""" - with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch( - "bookwyrm.activitystreams.populate_stream_task.delay" - ): - self.local_user = models.User.objects.create_user( - "mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse" - ) - self.another_user = models.User.objects.create_user( - "nutria", - "nutria@nutria.nutria", - "password", - local=True, - localname="nutria", - ) - with patch("bookwyrm.models.user.set_remote_server.delay"): - self.remote_user = models.User.objects.create_user( - "rat", - "rat@rat.com", - "ratword", - local=False, - remote_id="https://example.com/users/rat", - inbox="https://example.com/users/rat/inbox", - outbox="https://example.com/users/rat/outbox", - ) - work = models.Work.objects.create(title="test work") - self.book = models.Edition.objects.create(title="test book", parent_work=work) - - class TestStream(activitystreams.ActivityStream): - """test stream, don't have to do anything here""" - - key = "test" - - self.test_stream = TestStream() - - def test_activitystream_class_ids(self, *_): - """the abstract base class for stream objects""" - self.assertEqual( - self.test_stream.stream_id(self.local_user), - "{}-test".format(self.local_user.id), - ) - self.assertEqual( - self.test_stream.unread_id(self.local_user), - "{}-test-unread".format(self.local_user.id), - ) - - def test_abstractstream_get_audience(self, *_): - """get a list of users that should see a status""" - status = models.Status.objects.create( - user=self.remote_user, content="hi", privacy="public" - ) - users = self.test_stream.get_audience(status) - # remote users don't have feeds - self.assertFalse(self.remote_user in users) - self.assertTrue(self.local_user in users) - self.assertTrue(self.another_user in users) - - def test_abstractstream_get_audience_direct(self, *_): - """get a list of users that should see a status""" - status = models.Status.objects.create( - user=self.remote_user, - content="hi", - privacy="direct", - ) - status.mention_users.add(self.local_user) - users = self.test_stream.get_audience(status) - self.assertEqual(users, []) - - status = models.Comment.objects.create( - user=self.remote_user, - content="hi", - privacy="direct", - book=self.book, - ) - status.mention_users.add(self.local_user) - users = self.test_stream.get_audience(status) - self.assertTrue(self.local_user in users) - self.assertFalse(self.another_user in users) - self.assertFalse(self.remote_user in users) - - def test_abstractstream_get_audience_followers_remote_user(self, *_): - """get a list of users that should see a status""" - status = models.Status.objects.create( - user=self.remote_user, - content="hi", - privacy="followers", - ) - users = self.test_stream.get_audience(status) - self.assertFalse(users.exists()) - - def test_abstractstream_get_audience_followers_self(self, *_): - """get a list of users that should see a status""" - status = models.Comment.objects.create( - user=self.local_user, - content="hi", - privacy="direct", - book=self.book, - ) - users = self.test_stream.get_audience(status) - self.assertTrue(self.local_user in users) - self.assertFalse(self.another_user in users) - self.assertFalse(self.remote_user in users) - - def test_abstractstream_get_audience_followers_with_mention(self, *_): - """get a list of users that should see a status""" - status = models.Comment.objects.create( - user=self.remote_user, - content="hi", - privacy="direct", - book=self.book, - ) - status.mention_users.add(self.local_user) - - users = self.test_stream.get_audience(status) - self.assertTrue(self.local_user in users) - self.assertFalse(self.another_user in users) - self.assertFalse(self.remote_user in users) - - def test_abstractstream_get_audience_followers_with_relationship(self, *_): - """get a list of users that should see a status""" - self.remote_user.followers.add(self.local_user) - status = models.Comment.objects.create( - user=self.remote_user, - content="hi", - privacy="direct", - book=self.book, - ) - users = self.test_stream.get_audience(status) - self.assertFalse(self.local_user in users) - self.assertFalse(self.another_user in users) - self.assertFalse(self.remote_user in users) - - def test_homestream_get_audience(self, *_): - """get a list of users that should see a status""" - status = models.Status.objects.create( - user=self.remote_user, content="hi", privacy="public" - ) - users = activitystreams.HomeStream().get_audience(status) - self.assertFalse(users.exists()) - - def test_homestream_get_audience_with_mentions(self, *_): - """get a list of users that should see a status""" - status = models.Status.objects.create( - user=self.remote_user, content="hi", privacy="public" - ) - status.mention_users.add(self.local_user) - users = activitystreams.HomeStream().get_audience(status) - self.assertFalse(self.local_user in users) - self.assertFalse(self.another_user in users) - - def test_homestream_get_audience_with_relationship(self, *_): - """get a list of users that should see a status""" - self.remote_user.followers.add(self.local_user) - status = models.Status.objects.create( - user=self.remote_user, content="hi", privacy="public" - ) - users = activitystreams.HomeStream().get_audience(status) - self.assertTrue(self.local_user in users) - self.assertFalse(self.another_user in users) - - def test_localstream_get_audience_remote_status(self, *_): - """get a list of users that should see a status""" - status = models.Status.objects.create( - user=self.remote_user, content="hi", privacy="public" - ) - users = activitystreams.LocalStream().get_audience(status) - self.assertEqual(users, []) - - def test_localstream_get_audience_local_status(self, *_): - """get a list of users that should see a status""" - status = models.Status.objects.create( - user=self.local_user, content="hi", privacy="public" - ) - users = activitystreams.LocalStream().get_audience(status) - self.assertTrue(self.local_user in users) - self.assertTrue(self.another_user in users) - - def test_localstream_get_audience_unlisted(self, *_): - """get a list of users that should see a status""" - status = models.Status.objects.create( - user=self.local_user, content="hi", privacy="unlisted" - ) - users = activitystreams.LocalStream().get_audience(status) - self.assertEqual(users, []) - - def test_localstream_get_audience_books_no_book(self, *_): - """get a list of users that should see a status""" - status = models.Status.objects.create( - user=self.local_user, content="hi", privacy="public" - ) - audience = activitystreams.BooksStream().get_audience(status) - # no books, no audience - self.assertEqual(audience, []) - - def test_localstream_get_audience_books_mention_books(self, *_): - """get a list of users that should see a status""" - status = models.Status.objects.create( - user=self.local_user, content="hi", privacy="public" - ) - status.mention_books.add(self.book) - status.save(broadcast=False) - models.ShelfBook.objects.create( - user=self.local_user, - shelf=self.local_user.shelf_set.first(), - book=self.book, - ) - # yes book, yes audience - audience = activitystreams.BooksStream().get_audience(status) - self.assertTrue(self.local_user in audience) - - def test_localstream_get_audience_books_book_field(self, *_): - """get a list of users that should see a status""" - status = models.Comment.objects.create( - user=self.local_user, content="hi", privacy="public", book=self.book - ) - models.ShelfBook.objects.create( - user=self.local_user, - shelf=self.local_user.shelf_set.first(), - book=self.book, - ) - # yes book, yes audience - audience = activitystreams.BooksStream().get_audience(status) - self.assertTrue(self.local_user in audience) - - def test_localstream_get_audience_books_alternate_edition(self, *_): - """get a list of users that should see a status""" - alt_book = models.Edition.objects.create( - title="hi", parent_work=self.book.parent_work - ) - status = models.Comment.objects.create( - user=self.remote_user, content="hi", privacy="public", book=alt_book - ) - models.ShelfBook.objects.create( - user=self.local_user, - shelf=self.local_user.shelf_set.first(), - book=self.book, - ) - # yes book, yes audience - audience = activitystreams.BooksStream().get_audience(status) - self.assertTrue(self.local_user in audience) - - def test_localstream_get_audience_books_non_public(self, *_): - """get a list of users that should see a status""" - alt_book = models.Edition.objects.create( - title="hi", parent_work=self.book.parent_work - ) - status = models.Comment.objects.create( - user=self.remote_user, content="hi", privacy="unlisted", book=alt_book - ) - models.ShelfBook.objects.create( - user=self.local_user, - shelf=self.local_user.shelf_set.first(), - book=self.book, - ) - # yes book, yes audience - audience = activitystreams.BooksStream().get_audience(status) - self.assertEqual(audience, []) - - def test_get_statuses_for_user_books(self, *_): - """create a stream for a user""" - alt_book = models.Edition.objects.create( - title="hi", parent_work=self.book.parent_work - ) - status = models.Status.objects.create( - user=self.local_user, content="hi", privacy="public" - ) - status = models.Comment.objects.create( - user=self.remote_user, content="hi", privacy="public", book=alt_book - ) - models.ShelfBook.objects.create( - user=self.local_user, - shelf=self.local_user.shelf_set.first(), - book=self.book, - ) - # yes book, yes audience - result = activitystreams.BooksStream().get_statuses_for_user(self.local_user) - self.assertEqual(list(result), [status]) - - @patch("bookwyrm.activitystreams.LocalStream.remove_object_from_related_stores") - @patch("bookwyrm.activitystreams.BooksStream.remove_object_from_related_stores") - def test_boost_to_another_timeline(self, *_): - """add a boost and deduplicate the boosted status on the timeline""" - status = models.Status.objects.create(user=self.local_user, content="hi") - with patch("bookwyrm.activitystreams.handle_boost_task.delay"): - boost = models.Boost.objects.create( - boosted_status=status, - user=self.another_user, - ) - with patch( - "bookwyrm.activitystreams.HomeStream.remove_object_from_related_stores" - ) as mock: - activitystreams.handle_boost_task(boost.id) - - self.assertTrue(mock.called) - call_args = mock.call_args - self.assertEqual(call_args[0][0], status) - self.assertEqual( - call_args[1]["stores"], ["{:d}-home".format(self.another_user.id)] - ) - - @patch("bookwyrm.activitystreams.LocalStream.remove_object_from_related_stores") - @patch("bookwyrm.activitystreams.BooksStream.remove_object_from_related_stores") - def test_boost_to_following_timeline(self, *_): - """add a boost and deduplicate the boosted status on the timeline""" - self.local_user.following.add(self.another_user) - status = models.Status.objects.create(user=self.local_user, content="hi") - with patch("bookwyrm.activitystreams.handle_boost_task.delay"): - boost = models.Boost.objects.create( - boosted_status=status, - user=self.another_user, - ) - with patch( - "bookwyrm.activitystreams.HomeStream.remove_object_from_related_stores" - ) as mock: - activitystreams.handle_boost_task(boost.id) - self.assertTrue(mock.called) - call_args = mock.call_args - self.assertEqual(call_args[0][0], status) - self.assertTrue( - "{:d}-home".format(self.another_user.id) in call_args[1]["stores"] - ) - self.assertTrue( - "{:d}-home".format(self.local_user.id) in call_args[1]["stores"] - ) - - @patch("bookwyrm.activitystreams.LocalStream.remove_object_from_related_stores") - @patch("bookwyrm.activitystreams.BooksStream.remove_object_from_related_stores") - def test_boost_to_same_timeline(self, *_): - """add a boost and deduplicate the boosted status on the timeline""" - status = models.Status.objects.create(user=self.local_user, content="hi") - with patch("bookwyrm.activitystreams.handle_boost_task.delay"): - boost = models.Boost.objects.create( - boosted_status=status, - user=self.local_user, - ) - with patch( - "bookwyrm.activitystreams.HomeStream.remove_object_from_related_stores" - ) as mock: - activitystreams.handle_boost_task(boost.id) - self.assertTrue(mock.called) - call_args = mock.call_args - self.assertEqual(call_args[0][0], status) - self.assertEqual( - call_args[1]["stores"], ["{:d}-home".format(self.local_user.id)] - )