""" tests incoming activities"""
import json
from unittest.mock import patch

from django.test import TestCase

from bookwyrm import models, views


# pylint: disable=too-many-public-methods
class InboxRelationships(TestCase):
    """inbox tests"""

    def setUp(self):
        """basic user and book data"""
        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@example.com",
                "mouse@mouse.com",
                "mouseword",
                local=True,
                localname="mouse",
            )
        self.local_user.remote_id = "https://example.com/user/mouse"
        self.local_user.save(broadcast=False, update_fields=["remote_id"])
        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",
            )

        models.SiteSettings.objects.create()

    def test_follow(self):
        """remote user wants to follow local user"""
        activity = {
            "@context": "https://www.w3.org/ns/activitystreams",
            "id": "https://example.com/users/rat/follows/123",
            "type": "Follow",
            "actor": "https://example.com/users/rat",
            "object": "https://example.com/user/mouse",
        }

        self.assertFalse(models.UserFollowRequest.objects.exists())
        with patch(
            "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
        ) as mock:
            views.inbox.activity_task(activity)
            self.assertEqual(mock.call_count, 1)
            response_activity = json.loads(mock.call_args[1]["args"][1])
            self.assertEqual(response_activity["type"], "Accept")

        # notification created
        notification = models.Notification.objects.get()
        self.assertEqual(notification.user, self.local_user)
        self.assertEqual(notification.notification_type, "FOLLOW")

        # the request should have been deleted
        self.assertFalse(models.UserFollowRequest.objects.exists())

        # the follow relationship should exist
        follow = models.UserFollows.objects.get(user_object=self.local_user)
        self.assertEqual(follow.user_subject, self.remote_user)

    def test_follow_duplicate(self):
        """remote user wants to follow local user twice"""
        activity = {
            "@context": "https://www.w3.org/ns/activitystreams",
            "id": "https://example.com/users/rat/follows/123",
            "type": "Follow",
            "actor": "https://example.com/users/rat",
            "object": "https://example.com/user/mouse",
        }

        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
            views.inbox.activity_task(activity)

        # the follow relationship should exist
        follow = models.UserFollows.objects.get(user_object=self.local_user)
        self.assertEqual(follow.user_subject, self.remote_user)

        with patch(
            "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
        ) as mock:
            views.inbox.activity_task(activity)
            self.assertEqual(mock.call_count, 1)
            response_activity = json.loads(mock.call_args[1]["args"][1])
            self.assertEqual(response_activity["type"], "Accept")

        # the follow relationship should STILL exist
        follow = models.UserFollows.objects.get(user_object=self.local_user)
        self.assertEqual(follow.user_subject, self.remote_user)

    def test_follow_manually_approved(self):
        """needs approval before following"""
        activity = {
            "@context": "https://www.w3.org/ns/activitystreams",
            "id": "https://example.com/users/rat/follows/123",
            "type": "Follow",
            "actor": "https://example.com/users/rat",
            "object": "https://example.com/user/mouse",
        }

        self.local_user.manually_approves_followers = True
        self.local_user.save(
            broadcast=False, update_fields=["manually_approves_followers"]
        )

        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
            views.inbox.activity_task(activity)

        # notification created
        notification = models.Notification.objects.get()
        self.assertEqual(notification.user, self.local_user)
        self.assertEqual(notification.notification_type, "FOLLOW_REQUEST")

        # the request should exist
        request = models.UserFollowRequest.objects.get()
        self.assertEqual(request.user_subject, self.remote_user)
        self.assertEqual(request.user_object, self.local_user)

        # the follow relationship should not exist
        follow = models.UserFollows.objects.all()
        self.assertEqual(list(follow), [])

    def test_undo_follow_request(self):
        """the requester cancels a follow request"""
        self.local_user.manually_approves_followers = True
        self.local_user.save(
            broadcast=False, update_fields=["manually_approves_followers"]
        )
        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
            request = models.UserFollowRequest.objects.create(
                user_subject=self.remote_user, user_object=self.local_user
            )
        self.assertTrue(self.local_user.follower_requests.exists())

        activity = {
            "type": "Undo",
            "id": "bleh",
            "to": ["https://www.w3.org/ns/activitystreams#Public"],
            "cc": ["https://example.com/user/mouse/followers"],
            "actor": self.remote_user.remote_id,
            "@context": "https://www.w3.org/ns/activitystreams",
            "object": {
                "@context": "https://www.w3.org/ns/activitystreams",
                "id": request.remote_id,
                "type": "Follow",
                "actor": "https://example.com/users/rat",
                "object": "https://example.com/user/mouse",
            },
        }

        views.inbox.activity_task(activity)

        self.assertFalse(self.local_user.follower_requests.exists())

    def test_unfollow(self):
        """remove a relationship"""
        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
            rel = models.UserFollows.objects.create(
                user_subject=self.remote_user, user_object=self.local_user
            )
        activity = {
            "type": "Undo",
            "id": "bleh",
            "to": ["https://www.w3.org/ns/activitystreams#Public"],
            "cc": ["https://example.com/user/mouse/followers"],
            "actor": self.remote_user.remote_id,
            "@context": "https://www.w3.org/ns/activitystreams",
            "object": {
                "id": rel.remote_id,
                "type": "Follow",
                "actor": "https://example.com/users/rat",
                "object": "https://example.com/user/mouse",
            },
        }
        self.assertEqual(self.remote_user, self.local_user.followers.first())

        views.inbox.activity_task(activity)
        self.assertIsNone(self.local_user.followers.first())

    @patch("bookwyrm.activitystreams.add_user_statuses_task.delay")
    @patch("bookwyrm.lists_stream.add_user_lists_task.delay")
    def test_follow_accept(self, *_):
        """a remote user approved a follow request from local"""
        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
            rel = models.UserFollowRequest.objects.create(
                user_subject=self.local_user, user_object=self.remote_user
            )
        activity = {
            "@context": "https://www.w3.org/ns/activitystreams",
            "id": "https://example.com/users/rat/follows/123#accepts",
            "type": "Accept",
            "actor": "https://example.com/users/rat",
            "object": {
                "id": rel.remote_id,
                "type": "Follow",
                "actor": "https://example.com/user/mouse",
                "object": "https://example.com/users/rat",
            },
        }

        self.assertEqual(models.UserFollowRequest.objects.count(), 1)

        views.inbox.activity_task(activity)

        # request should be deleted
        self.assertEqual(models.UserFollowRequest.objects.count(), 0)

        # relationship should be created
        follows = self.remote_user.followers
        self.assertEqual(follows.count(), 1)
        self.assertEqual(follows.first(), self.local_user)

    def test_follow_reject(self):
        """turn down a follow request"""
        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
            rel = models.UserFollowRequest.objects.create(
                user_subject=self.local_user, user_object=self.remote_user
            )
        activity = {
            "@context": "https://www.w3.org/ns/activitystreams",
            "id": "https://example.com/users/rat/follows/123#accepts",
            "type": "Reject",
            "actor": "https://example.com/users/rat",
            "object": {
                "id": rel.remote_id,
                "type": "Follow",
                "actor": "https://example.com/user/mouse",
                "object": "https://example.com/users/rat",
            },
        }

        self.assertEqual(models.UserFollowRequest.objects.count(), 1)

        views.inbox.activity_task(activity)

        # request should be deleted
        self.assertFalse(models.UserFollowRequest.objects.exists())
        self.assertFalse(self.remote_user.followers.exists())

a5478fe0abf3f137839a02dac6f4fcaba80e4be7
<mask token>


class InboxRelationships(TestCase):
    <mask token>

    def setUp(self):
        """basic user and book data"""
        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@example.com', 'mouse@mouse.com', 'mouseword', local=
                True, localname='mouse')
        self.local_user.remote_id = 'https://example.com/user/mouse'
        self.local_user.save(broadcast=False, update_fields=['remote_id'])
        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')
        models.SiteSettings.objects.create()

    def test_follow(self):
        """remote user wants to follow local user"""
        activity = {'@context': 'https://www.w3.org/ns/activitystreams',
            'id': 'https://example.com/users/rat/follows/123', 'type':
            'Follow', 'actor': 'https://example.com/users/rat', 'object':
            'https://example.com/user/mouse'}
        self.assertFalse(models.UserFollowRequest.objects.exists())
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'
            ) as mock:
            views.inbox.activity_task(activity)
            self.assertEqual(mock.call_count, 1)
            response_activity = json.loads(mock.call_args[1]['args'][1])
            self.assertEqual(response_activity['type'], 'Accept')
        notification = models.Notification.objects.get()
        self.assertEqual(notification.user, self.local_user)
        self.assertEqual(notification.notification_type, 'FOLLOW')
        self.assertFalse(models.UserFollowRequest.objects.exists())
        follow = models.UserFollows.objects.get(user_object=self.local_user)
        self.assertEqual(follow.user_subject, self.remote_user)
    <mask token>

    def test_follow_manually_approved(self):
        """needs approval before following"""
        activity = {'@context': 'https://www.w3.org/ns/activitystreams',
            'id': 'https://example.com/users/rat/follows/123', 'type':
            'Follow', 'actor': 'https://example.com/users/rat', 'object':
            'https://example.com/user/mouse'}
        self.local_user.manually_approves_followers = True
        self.local_user.save(broadcast=False, update_fields=[
            'manually_approves_followers'])
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'):
            views.inbox.activity_task(activity)
        notification = models.Notification.objects.get()
        self.assertEqual(notification.user, self.local_user)
        self.assertEqual(notification.notification_type, 'FOLLOW_REQUEST')
        request = models.UserFollowRequest.objects.get()
        self.assertEqual(request.user_subject, self.remote_user)
        self.assertEqual(request.user_object, self.local_user)
        follow = models.UserFollows.objects.all()
        self.assertEqual(list(follow), [])

    def test_undo_follow_request(self):
        """the requester cancels a follow request"""
        self.local_user.manually_approves_followers = True
        self.local_user.save(broadcast=False, update_fields=[
            'manually_approves_followers'])
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'):
            request = models.UserFollowRequest.objects.create(user_subject=
                self.remote_user, user_object=self.local_user)
        self.assertTrue(self.local_user.follower_requests.exists())
        activity = {'type': 'Undo', 'id': 'bleh', 'to': [
            'https://www.w3.org/ns/activitystreams#Public'], 'cc': [
            'https://example.com/user/mouse/followers'], 'actor': self.
            remote_user.remote_id, '@context':
            'https://www.w3.org/ns/activitystreams', 'object': {'@context':
            'https://www.w3.org/ns/activitystreams', 'id': request.
            remote_id, 'type': 'Follow', 'actor':
            'https://example.com/users/rat', 'object':
            'https://example.com/user/mouse'}}
        views.inbox.activity_task(activity)
        self.assertFalse(self.local_user.follower_requests.exists())

    def test_unfollow(self):
        """remove a relationship"""
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'):
            rel = models.UserFollows.objects.create(user_subject=self.
                remote_user, user_object=self.local_user)
        activity = {'type': 'Undo', 'id': 'bleh', 'to': [
            'https://www.w3.org/ns/activitystreams#Public'], 'cc': [
            'https://example.com/user/mouse/followers'], 'actor': self.
            remote_user.remote_id, '@context':
            'https://www.w3.org/ns/activitystreams', 'object': {'id': rel.
            remote_id, 'type': 'Follow', 'actor':
            'https://example.com/users/rat', 'object':
            'https://example.com/user/mouse'}}
        self.assertEqual(self.remote_user, self.local_user.followers.first())
        views.inbox.activity_task(activity)
        self.assertIsNone(self.local_user.followers.first())

    @patch('bookwyrm.activitystreams.add_user_statuses_task.delay')
    @patch('bookwyrm.lists_stream.add_user_lists_task.delay')
    def test_follow_accept(self, *_):
        """a remote user approved a follow request from local"""
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'):
            rel = models.UserFollowRequest.objects.create(user_subject=self
                .local_user, user_object=self.remote_user)
        activity = {'@context': 'https://www.w3.org/ns/activitystreams',
            'id': 'https://example.com/users/rat/follows/123#accepts',
            'type': 'Accept', 'actor': 'https://example.com/users/rat',
            'object': {'id': rel.remote_id, 'type': 'Follow', 'actor':
            'https://example.com/user/mouse', 'object':
            'https://example.com/users/rat'}}
        self.assertEqual(models.UserFollowRequest.objects.count(), 1)
        views.inbox.activity_task(activity)
        self.assertEqual(models.UserFollowRequest.objects.count(), 0)
        follows = self.remote_user.followers
        self.assertEqual(follows.count(), 1)
        self.assertEqual(follows.first(), self.local_user)

    def test_follow_reject(self):
        """turn down a follow request"""
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'):
            rel = models.UserFollowRequest.objects.create(user_subject=self
                .local_user, user_object=self.remote_user)
        activity = {'@context': 'https://www.w3.org/ns/activitystreams',
            'id': 'https://example.com/users/rat/follows/123#accepts',
            'type': 'Reject', 'actor': 'https://example.com/users/rat',
            'object': {'id': rel.remote_id, 'type': 'Follow', 'actor':
            'https://example.com/user/mouse', 'object':
            'https://example.com/users/rat'}}
        self.assertEqual(models.UserFollowRequest.objects.count(), 1)
        views.inbox.activity_task(activity)
        self.assertFalse(models.UserFollowRequest.objects.exists())
        self.assertFalse(self.remote_user.followers.exists())

<mask token>


class InboxRelationships(TestCase):
    <mask token>

    def setUp(self):
        """basic user and book data"""
        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@example.com', 'mouse@mouse.com', 'mouseword', local=
                True, localname='mouse')
        self.local_user.remote_id = 'https://example.com/user/mouse'
        self.local_user.save(broadcast=False, update_fields=['remote_id'])
        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')
        models.SiteSettings.objects.create()

    def test_follow(self):
        """remote user wants to follow local user"""
        activity = {'@context': 'https://www.w3.org/ns/activitystreams',
            'id': 'https://example.com/users/rat/follows/123', 'type':
            'Follow', 'actor': 'https://example.com/users/rat', 'object':
            'https://example.com/user/mouse'}
        self.assertFalse(models.UserFollowRequest.objects.exists())
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'
            ) as mock:
            views.inbox.activity_task(activity)
            self.assertEqual(mock.call_count, 1)
            response_activity = json.loads(mock.call_args[1]['args'][1])
            self.assertEqual(response_activity['type'], 'Accept')
        notification = models.Notification.objects.get()
        self.assertEqual(notification.user, self.local_user)
        self.assertEqual(notification.notification_type, 'FOLLOW')
        self.assertFalse(models.UserFollowRequest.objects.exists())
        follow = models.UserFollows.objects.get(user_object=self.local_user)
        self.assertEqual(follow.user_subject, self.remote_user)

    def test_follow_duplicate(self):
        """remote user wants to follow local user twice"""
        activity = {'@context': 'https://www.w3.org/ns/activitystreams',
            'id': 'https://example.com/users/rat/follows/123', 'type':
            'Follow', 'actor': 'https://example.com/users/rat', 'object':
            'https://example.com/user/mouse'}
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'):
            views.inbox.activity_task(activity)
        follow = models.UserFollows.objects.get(user_object=self.local_user)
        self.assertEqual(follow.user_subject, self.remote_user)
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'
            ) as mock:
            views.inbox.activity_task(activity)
            self.assertEqual(mock.call_count, 1)
            response_activity = json.loads(mock.call_args[1]['args'][1])
            self.assertEqual(response_activity['type'], 'Accept')
        follow = models.UserFollows.objects.get(user_object=self.local_user)
        self.assertEqual(follow.user_subject, self.remote_user)

    def test_follow_manually_approved(self):
        """needs approval before following"""
        activity = {'@context': 'https://www.w3.org/ns/activitystreams',
            'id': 'https://example.com/users/rat/follows/123', 'type':
            'Follow', 'actor': 'https://example.com/users/rat', 'object':
            'https://example.com/user/mouse'}
        self.local_user.manually_approves_followers = True
        self.local_user.save(broadcast=False, update_fields=[
            'manually_approves_followers'])
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'):
            views.inbox.activity_task(activity)
        notification = models.Notification.objects.get()
        self.assertEqual(notification.user, self.local_user)
        self.assertEqual(notification.notification_type, 'FOLLOW_REQUEST')
        request = models.UserFollowRequest.objects.get()
        self.assertEqual(request.user_subject, self.remote_user)
        self.assertEqual(request.user_object, self.local_user)
        follow = models.UserFollows.objects.all()
        self.assertEqual(list(follow), [])

    def test_undo_follow_request(self):
        """the requester cancels a follow request"""
        self.local_user.manually_approves_followers = True
        self.local_user.save(broadcast=False, update_fields=[
            'manually_approves_followers'])
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'):
            request = models.UserFollowRequest.objects.create(user_subject=
                self.remote_user, user_object=self.local_user)
        self.assertTrue(self.local_user.follower_requests.exists())
        activity = {'type': 'Undo', 'id': 'bleh', 'to': [
            'https://www.w3.org/ns/activitystreams#Public'], 'cc': [
            'https://example.com/user/mouse/followers'], 'actor': self.
            remote_user.remote_id, '@context':
            'https://www.w3.org/ns/activitystreams', 'object': {'@context':
            'https://www.w3.org/ns/activitystreams', 'id': request.
            remote_id, 'type': 'Follow', 'actor':
            'https://example.com/users/rat', 'object':
            'https://example.com/user/mouse'}}
        views.inbox.activity_task(activity)
        self.assertFalse(self.local_user.follower_requests.exists())

    def test_unfollow(self):
        """remove a relationship"""
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'):
            rel = models.UserFollows.objects.create(user_subject=self.
                remote_user, user_object=self.local_user)
        activity = {'type': 'Undo', 'id': 'bleh', 'to': [
            'https://www.w3.org/ns/activitystreams#Public'], 'cc': [
            'https://example.com/user/mouse/followers'], 'actor': self.
            remote_user.remote_id, '@context':
            'https://www.w3.org/ns/activitystreams', 'object': {'id': rel.
            remote_id, 'type': 'Follow', 'actor':
            'https://example.com/users/rat', 'object':
            'https://example.com/user/mouse'}}
        self.assertEqual(self.remote_user, self.local_user.followers.first())
        views.inbox.activity_task(activity)
        self.assertIsNone(self.local_user.followers.first())

    @patch('bookwyrm.activitystreams.add_user_statuses_task.delay')
    @patch('bookwyrm.lists_stream.add_user_lists_task.delay')
    def test_follow_accept(self, *_):
        """a remote user approved a follow request from local"""
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'):
            rel = models.UserFollowRequest.objects.create(user_subject=self
                .local_user, user_object=self.remote_user)
        activity = {'@context': 'https://www.w3.org/ns/activitystreams',
            'id': 'https://example.com/users/rat/follows/123#accepts',
            'type': 'Accept', 'actor': 'https://example.com/users/rat',
            'object': {'id': rel.remote_id, 'type': 'Follow', 'actor':
            'https://example.com/user/mouse', 'object':
            'https://example.com/users/rat'}}
        self.assertEqual(models.UserFollowRequest.objects.count(), 1)
        views.inbox.activity_task(activity)
        self.assertEqual(models.UserFollowRequest.objects.count(), 0)
        follows = self.remote_user.followers
        self.assertEqual(follows.count(), 1)
        self.assertEqual(follows.first(), self.local_user)

    def test_follow_reject(self):
        """turn down a follow request"""
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'):
            rel = models.UserFollowRequest.objects.create(user_subject=self
                .local_user, user_object=self.remote_user)
        activity = {'@context': 'https://www.w3.org/ns/activitystreams',
            'id': 'https://example.com/users/rat/follows/123#accepts',
            'type': 'Reject', 'actor': 'https://example.com/users/rat',
            'object': {'id': rel.remote_id, 'type': 'Follow', 'actor':
            'https://example.com/user/mouse', 'object':
            'https://example.com/users/rat'}}
        self.assertEqual(models.UserFollowRequest.objects.count(), 1)
        views.inbox.activity_task(activity)
        self.assertFalse(models.UserFollowRequest.objects.exists())
        self.assertFalse(self.remote_user.followers.exists())

<mask token>


class InboxRelationships(TestCase):
    """inbox tests"""

    def setUp(self):
        """basic user and book data"""
        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@example.com', 'mouse@mouse.com', 'mouseword', local=
                True, localname='mouse')
        self.local_user.remote_id = 'https://example.com/user/mouse'
        self.local_user.save(broadcast=False, update_fields=['remote_id'])
        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')
        models.SiteSettings.objects.create()

    def test_follow(self):
        """remote user wants to follow local user"""
        activity = {'@context': 'https://www.w3.org/ns/activitystreams',
            'id': 'https://example.com/users/rat/follows/123', 'type':
            'Follow', 'actor': 'https://example.com/users/rat', 'object':
            'https://example.com/user/mouse'}
        self.assertFalse(models.UserFollowRequest.objects.exists())
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'
            ) as mock:
            views.inbox.activity_task(activity)
            self.assertEqual(mock.call_count, 1)
            response_activity = json.loads(mock.call_args[1]['args'][1])
            self.assertEqual(response_activity['type'], 'Accept')
        notification = models.Notification.objects.get()
        self.assertEqual(notification.user, self.local_user)
        self.assertEqual(notification.notification_type, 'FOLLOW')
        self.assertFalse(models.UserFollowRequest.objects.exists())
        follow = models.UserFollows.objects.get(user_object=self.local_user)
        self.assertEqual(follow.user_subject, self.remote_user)

    def test_follow_duplicate(self):
        """remote user wants to follow local user twice"""
        activity = {'@context': 'https://www.w3.org/ns/activitystreams',
            'id': 'https://example.com/users/rat/follows/123', 'type':
            'Follow', 'actor': 'https://example.com/users/rat', 'object':
            'https://example.com/user/mouse'}
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'):
            views.inbox.activity_task(activity)
        follow = models.UserFollows.objects.get(user_object=self.local_user)
        self.assertEqual(follow.user_subject, self.remote_user)
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'
            ) as mock:
            views.inbox.activity_task(activity)
            self.assertEqual(mock.call_count, 1)
            response_activity = json.loads(mock.call_args[1]['args'][1])
            self.assertEqual(response_activity['type'], 'Accept')
        follow = models.UserFollows.objects.get(user_object=self.local_user)
        self.assertEqual(follow.user_subject, self.remote_user)

    def test_follow_manually_approved(self):
        """needs approval before following"""
        activity = {'@context': 'https://www.w3.org/ns/activitystreams',
            'id': 'https://example.com/users/rat/follows/123', 'type':
            'Follow', 'actor': 'https://example.com/users/rat', 'object':
            'https://example.com/user/mouse'}
        self.local_user.manually_approves_followers = True
        self.local_user.save(broadcast=False, update_fields=[
            'manually_approves_followers'])
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'):
            views.inbox.activity_task(activity)
        notification = models.Notification.objects.get()
        self.assertEqual(notification.user, self.local_user)
        self.assertEqual(notification.notification_type, 'FOLLOW_REQUEST')
        request = models.UserFollowRequest.objects.get()
        self.assertEqual(request.user_subject, self.remote_user)
        self.assertEqual(request.user_object, self.local_user)
        follow = models.UserFollows.objects.all()
        self.assertEqual(list(follow), [])

    def test_undo_follow_request(self):
        """the requester cancels a follow request"""
        self.local_user.manually_approves_followers = True
        self.local_user.save(broadcast=False, update_fields=[
            'manually_approves_followers'])
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'):
            request = models.UserFollowRequest.objects.create(user_subject=
                self.remote_user, user_object=self.local_user)
        self.assertTrue(self.local_user.follower_requests.exists())
        activity = {'type': 'Undo', 'id': 'bleh', 'to': [
            'https://www.w3.org/ns/activitystreams#Public'], 'cc': [
            'https://example.com/user/mouse/followers'], 'actor': self.
            remote_user.remote_id, '@context':
            'https://www.w3.org/ns/activitystreams', 'object': {'@context':
            'https://www.w3.org/ns/activitystreams', 'id': request.
            remote_id, 'type': 'Follow', 'actor':
            'https://example.com/users/rat', 'object':
            'https://example.com/user/mouse'}}
        views.inbox.activity_task(activity)
        self.assertFalse(self.local_user.follower_requests.exists())

    def test_unfollow(self):
        """remove a relationship"""
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'):
            rel = models.UserFollows.objects.create(user_subject=self.
                remote_user, user_object=self.local_user)
        activity = {'type': 'Undo', 'id': 'bleh', 'to': [
            'https://www.w3.org/ns/activitystreams#Public'], 'cc': [
            'https://example.com/user/mouse/followers'], 'actor': self.
            remote_user.remote_id, '@context':
            'https://www.w3.org/ns/activitystreams', 'object': {'id': rel.
            remote_id, 'type': 'Follow', 'actor':
            'https://example.com/users/rat', 'object':
            'https://example.com/user/mouse'}}
        self.assertEqual(self.remote_user, self.local_user.followers.first())
        views.inbox.activity_task(activity)
        self.assertIsNone(self.local_user.followers.first())

    @patch('bookwyrm.activitystreams.add_user_statuses_task.delay')
    @patch('bookwyrm.lists_stream.add_user_lists_task.delay')
    def test_follow_accept(self, *_):
        """a remote user approved a follow request from local"""
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'):
            rel = models.UserFollowRequest.objects.create(user_subject=self
                .local_user, user_object=self.remote_user)
        activity = {'@context': 'https://www.w3.org/ns/activitystreams',
            'id': 'https://example.com/users/rat/follows/123#accepts',
            'type': 'Accept', 'actor': 'https://example.com/users/rat',
            'object': {'id': rel.remote_id, 'type': 'Follow', 'actor':
            'https://example.com/user/mouse', 'object':
            'https://example.com/users/rat'}}
        self.assertEqual(models.UserFollowRequest.objects.count(), 1)
        views.inbox.activity_task(activity)
        self.assertEqual(models.UserFollowRequest.objects.count(), 0)
        follows = self.remote_user.followers
        self.assertEqual(follows.count(), 1)
        self.assertEqual(follows.first(), self.local_user)

    def test_follow_reject(self):
        """turn down a follow request"""
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'):
            rel = models.UserFollowRequest.objects.create(user_subject=self
                .local_user, user_object=self.remote_user)
        activity = {'@context': 'https://www.w3.org/ns/activitystreams',
            'id': 'https://example.com/users/rat/follows/123#accepts',
            'type': 'Reject', 'actor': 'https://example.com/users/rat',
            'object': {'id': rel.remote_id, 'type': 'Follow', 'actor':
            'https://example.com/user/mouse', 'object':
            'https://example.com/users/rat'}}
        self.assertEqual(models.UserFollowRequest.objects.count(), 1)
        views.inbox.activity_task(activity)
        self.assertFalse(models.UserFollowRequest.objects.exists())
        self.assertFalse(self.remote_user.followers.exists())

<mask token>
import json
from unittest.mock import patch
from django.test import TestCase
from bookwyrm import models, views


class InboxRelationships(TestCase):
    """inbox tests"""

    def setUp(self):
        """basic user and book data"""
        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@example.com', 'mouse@mouse.com', 'mouseword', local=
                True, localname='mouse')
        self.local_user.remote_id = 'https://example.com/user/mouse'
        self.local_user.save(broadcast=False, update_fields=['remote_id'])
        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')
        models.SiteSettings.objects.create()

    def test_follow(self):
        """remote user wants to follow local user"""
        activity = {'@context': 'https://www.w3.org/ns/activitystreams',
            'id': 'https://example.com/users/rat/follows/123', 'type':
            'Follow', 'actor': 'https://example.com/users/rat', 'object':
            'https://example.com/user/mouse'}
        self.assertFalse(models.UserFollowRequest.objects.exists())
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'
            ) as mock:
            views.inbox.activity_task(activity)
            self.assertEqual(mock.call_count, 1)
            response_activity = json.loads(mock.call_args[1]['args'][1])
            self.assertEqual(response_activity['type'], 'Accept')
        notification = models.Notification.objects.get()
        self.assertEqual(notification.user, self.local_user)
        self.assertEqual(notification.notification_type, 'FOLLOW')
        self.assertFalse(models.UserFollowRequest.objects.exists())
        follow = models.UserFollows.objects.get(user_object=self.local_user)
        self.assertEqual(follow.user_subject, self.remote_user)

    def test_follow_duplicate(self):
        """remote user wants to follow local user twice"""
        activity = {'@context': 'https://www.w3.org/ns/activitystreams',
            'id': 'https://example.com/users/rat/follows/123', 'type':
            'Follow', 'actor': 'https://example.com/users/rat', 'object':
            'https://example.com/user/mouse'}
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'):
            views.inbox.activity_task(activity)
        follow = models.UserFollows.objects.get(user_object=self.local_user)
        self.assertEqual(follow.user_subject, self.remote_user)
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'
            ) as mock:
            views.inbox.activity_task(activity)
            self.assertEqual(mock.call_count, 1)
            response_activity = json.loads(mock.call_args[1]['args'][1])
            self.assertEqual(response_activity['type'], 'Accept')
        follow = models.UserFollows.objects.get(user_object=self.local_user)
        self.assertEqual(follow.user_subject, self.remote_user)

    def test_follow_manually_approved(self):
        """needs approval before following"""
        activity = {'@context': 'https://www.w3.org/ns/activitystreams',
            'id': 'https://example.com/users/rat/follows/123', 'type':
            'Follow', 'actor': 'https://example.com/users/rat', 'object':
            'https://example.com/user/mouse'}
        self.local_user.manually_approves_followers = True
        self.local_user.save(broadcast=False, update_fields=[
            'manually_approves_followers'])
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'):
            views.inbox.activity_task(activity)
        notification = models.Notification.objects.get()
        self.assertEqual(notification.user, self.local_user)
        self.assertEqual(notification.notification_type, 'FOLLOW_REQUEST')
        request = models.UserFollowRequest.objects.get()
        self.assertEqual(request.user_subject, self.remote_user)
        self.assertEqual(request.user_object, self.local_user)
        follow = models.UserFollows.objects.all()
        self.assertEqual(list(follow), [])

    def test_undo_follow_request(self):
        """the requester cancels a follow request"""
        self.local_user.manually_approves_followers = True
        self.local_user.save(broadcast=False, update_fields=[
            'manually_approves_followers'])
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'):
            request = models.UserFollowRequest.objects.create(user_subject=
                self.remote_user, user_object=self.local_user)
        self.assertTrue(self.local_user.follower_requests.exists())
        activity = {'type': 'Undo', 'id': 'bleh', 'to': [
            'https://www.w3.org/ns/activitystreams#Public'], 'cc': [
            'https://example.com/user/mouse/followers'], 'actor': self.
            remote_user.remote_id, '@context':
            'https://www.w3.org/ns/activitystreams', 'object': {'@context':
            'https://www.w3.org/ns/activitystreams', 'id': request.
            remote_id, 'type': 'Follow', 'actor':
            'https://example.com/users/rat', 'object':
            'https://example.com/user/mouse'}}
        views.inbox.activity_task(activity)
        self.assertFalse(self.local_user.follower_requests.exists())

    def test_unfollow(self):
        """remove a relationship"""
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'):
            rel = models.UserFollows.objects.create(user_subject=self.
                remote_user, user_object=self.local_user)
        activity = {'type': 'Undo', 'id': 'bleh', 'to': [
            'https://www.w3.org/ns/activitystreams#Public'], 'cc': [
            'https://example.com/user/mouse/followers'], 'actor': self.
            remote_user.remote_id, '@context':
            'https://www.w3.org/ns/activitystreams', 'object': {'id': rel.
            remote_id, 'type': 'Follow', 'actor':
            'https://example.com/users/rat', 'object':
            'https://example.com/user/mouse'}}
        self.assertEqual(self.remote_user, self.local_user.followers.first())
        views.inbox.activity_task(activity)
        self.assertIsNone(self.local_user.followers.first())

    @patch('bookwyrm.activitystreams.add_user_statuses_task.delay')
    @patch('bookwyrm.lists_stream.add_user_lists_task.delay')
    def test_follow_accept(self, *_):
        """a remote user approved a follow request from local"""
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'):
            rel = models.UserFollowRequest.objects.create(user_subject=self
                .local_user, user_object=self.remote_user)
        activity = {'@context': 'https://www.w3.org/ns/activitystreams',
            'id': 'https://example.com/users/rat/follows/123#accepts',
            'type': 'Accept', 'actor': 'https://example.com/users/rat',
            'object': {'id': rel.remote_id, 'type': 'Follow', 'actor':
            'https://example.com/user/mouse', 'object':
            'https://example.com/users/rat'}}
        self.assertEqual(models.UserFollowRequest.objects.count(), 1)
        views.inbox.activity_task(activity)
        self.assertEqual(models.UserFollowRequest.objects.count(), 0)
        follows = self.remote_user.followers
        self.assertEqual(follows.count(), 1)
        self.assertEqual(follows.first(), self.local_user)

    def test_follow_reject(self):
        """turn down a follow request"""
        with patch(
            'bookwyrm.models.activitypub_mixin.broadcast_task.apply_async'):
            rel = models.UserFollowRequest.objects.create(user_subject=self
                .local_user, user_object=self.remote_user)
        activity = {'@context': 'https://www.w3.org/ns/activitystreams',
            'id': 'https://example.com/users/rat/follows/123#accepts',
            'type': 'Reject', 'actor': 'https://example.com/users/rat',
            'object': {'id': rel.remote_id, 'type': 'Follow', 'actor':
            'https://example.com/user/mouse', 'object':
            'https://example.com/users/rat'}}
        self.assertEqual(models.UserFollowRequest.objects.count(), 1)
        views.inbox.activity_task(activity)
        self.assertFalse(models.UserFollowRequest.objects.exists())
        self.assertFalse(self.remote_user.followers.exists())

""" tests incoming activities"""
import json
from unittest.mock import patch

from django.test import TestCase

from bookwyrm import models, views


# pylint: disable=too-many-public-methods
class InboxRelationships(TestCase):
    """inbox tests"""

    def setUp(self):
        """basic user and book data"""
        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@example.com",
                "mouse@mouse.com",
                "mouseword",
                local=True,
                localname="mouse",
            )
        self.local_user.remote_id = "https://example.com/user/mouse"
        self.local_user.save(broadcast=False, update_fields=["remote_id"])
        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",
            )

        models.SiteSettings.objects.create()

    def test_follow(self):
        """remote user wants to follow local user"""
        activity = {
            "@context": "https://www.w3.org/ns/activitystreams",
            "id": "https://example.com/users/rat/follows/123",
            "type": "Follow",
            "actor": "https://example.com/users/rat",
            "object": "https://example.com/user/mouse",
        }

        self.assertFalse(models.UserFollowRequest.objects.exists())
        with patch(
            "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
        ) as mock:
            views.inbox.activity_task(activity)
            self.assertEqual(mock.call_count, 1)
            response_activity = json.loads(mock.call_args[1]["args"][1])
            self.assertEqual(response_activity["type"], "Accept")

        # notification created
        notification = models.Notification.objects.get()
        self.assertEqual(notification.user, self.local_user)
        self.assertEqual(notification.notification_type, "FOLLOW")

        # the request should have been deleted
        self.assertFalse(models.UserFollowRequest.objects.exists())

        # the follow relationship should exist
        follow = models.UserFollows.objects.get(user_object=self.local_user)
        self.assertEqual(follow.user_subject, self.remote_user)

    def test_follow_duplicate(self):
        """remote user wants to follow local user twice"""
        activity = {
            "@context": "https://www.w3.org/ns/activitystreams",
            "id": "https://example.com/users/rat/follows/123",
            "type": "Follow",
            "actor": "https://example.com/users/rat",
            "object": "https://example.com/user/mouse",
        }

        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
            views.inbox.activity_task(activity)

        # the follow relationship should exist
        follow = models.UserFollows.objects.get(user_object=self.local_user)
        self.assertEqual(follow.user_subject, self.remote_user)

        with patch(
            "bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
        ) as mock:
            views.inbox.activity_task(activity)
            self.assertEqual(mock.call_count, 1)
            response_activity = json.loads(mock.call_args[1]["args"][1])
            self.assertEqual(response_activity["type"], "Accept")

        # the follow relationship should STILL exist
        follow = models.UserFollows.objects.get(user_object=self.local_user)
        self.assertEqual(follow.user_subject, self.remote_user)

    def test_follow_manually_approved(self):
        """needs approval before following"""
        activity = {
            "@context": "https://www.w3.org/ns/activitystreams",
            "id": "https://example.com/users/rat/follows/123",
            "type": "Follow",
            "actor": "https://example.com/users/rat",
            "object": "https://example.com/user/mouse",
        }

        self.local_user.manually_approves_followers = True
        self.local_user.save(
            broadcast=False, update_fields=["manually_approves_followers"]
        )

        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
            views.inbox.activity_task(activity)

        # notification created
        notification = models.Notification.objects.get()
        self.assertEqual(notification.user, self.local_user)
        self.assertEqual(notification.notification_type, "FOLLOW_REQUEST")

        # the request should exist
        request = models.UserFollowRequest.objects.get()
        self.assertEqual(request.user_subject, self.remote_user)
        self.assertEqual(request.user_object, self.local_user)

        # the follow relationship should not exist
        follow = models.UserFollows.objects.all()
        self.assertEqual(list(follow), [])

    def test_undo_follow_request(self):
        """the requester cancels a follow request"""
        self.local_user.manually_approves_followers = True
        self.local_user.save(
            broadcast=False, update_fields=["manually_approves_followers"]
        )
        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
            request = models.UserFollowRequest.objects.create(
                user_subject=self.remote_user, user_object=self.local_user
            )
        self.assertTrue(self.local_user.follower_requests.exists())

        activity = {
            "type": "Undo",
            "id": "bleh",
            "to": ["https://www.w3.org/ns/activitystreams#Public"],
            "cc": ["https://example.com/user/mouse/followers"],
            "actor": self.remote_user.remote_id,
            "@context": "https://www.w3.org/ns/activitystreams",
            "object": {
                "@context": "https://www.w3.org/ns/activitystreams",
                "id": request.remote_id,
                "type": "Follow",
                "actor": "https://example.com/users/rat",
                "object": "https://example.com/user/mouse",
            },
        }

        views.inbox.activity_task(activity)

        self.assertFalse(self.local_user.follower_requests.exists())

    def test_unfollow(self):
        """remove a relationship"""
        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
            rel = models.UserFollows.objects.create(
                user_subject=self.remote_user, user_object=self.local_user
            )
        activity = {
            "type": "Undo",
            "id": "bleh",
            "to": ["https://www.w3.org/ns/activitystreams#Public"],
            "cc": ["https://example.com/user/mouse/followers"],
            "actor": self.remote_user.remote_id,
            "@context": "https://www.w3.org/ns/activitystreams",
            "object": {
                "id": rel.remote_id,
                "type": "Follow",
                "actor": "https://example.com/users/rat",
                "object": "https://example.com/user/mouse",
            },
        }
        self.assertEqual(self.remote_user, self.local_user.followers.first())

        views.inbox.activity_task(activity)
        self.assertIsNone(self.local_user.followers.first())

    @patch("bookwyrm.activitystreams.add_user_statuses_task.delay")
    @patch("bookwyrm.lists_stream.add_user_lists_task.delay")
    def test_follow_accept(self, *_):
        """a remote user approved a follow request from local"""
        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
            rel = models.UserFollowRequest.objects.create(
                user_subject=self.local_user, user_object=self.remote_user
            )
        activity = {
            "@context": "https://www.w3.org/ns/activitystreams",
            "id": "https://example.com/users/rat/follows/123#accepts",
            "type": "Accept",
            "actor": "https://example.com/users/rat",
            "object": {
                "id": rel.remote_id,
                "type": "Follow",
                "actor": "https://example.com/user/mouse",
                "object": "https://example.com/users/rat",
            },
        }

        self.assertEqual(models.UserFollowRequest.objects.count(), 1)

        views.inbox.activity_task(activity)

        # request should be deleted
        self.assertEqual(models.UserFollowRequest.objects.count(), 0)

        # relationship should be created
        follows = self.remote_user.followers
        self.assertEqual(follows.count(), 1)
        self.assertEqual(follows.first(), self.local_user)

    def test_follow_reject(self):
        """turn down a follow request"""
        with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
            rel = models.UserFollowRequest.objects.create(
                user_subject=self.local_user, user_object=self.remote_user
            )
        activity = {
            "@context": "https://www.w3.org/ns/activitystreams",
            "id": "https://example.com/users/rat/follows/123#accepts",
            "type": "Reject",
            "actor": "https://example.com/users/rat",
            "object": {
                "id": rel.remote_id,
                "type": "Follow",
                "actor": "https://example.com/user/mouse",
                "object": "https://example.com/users/rat",
            },
        }

        self.assertEqual(models.UserFollowRequest.objects.count(), 1)

        views.inbox.activity_task(activity)

        # request should be deleted
        self.assertFalse(models.UserFollowRequest.objects.exists())
        self.assertFalse(self.remote_user.followers.exists())
