Repository: bookwyrm-social/bookwyrm

Issue:
Can't follow user from wyrms.de
**Describe the bug**
A user (from wyrms.de) followed me, I followed back. My *followers*-page says I'm following her. My *following*-page doesn't list her. I can see a post from her in the federated feed but not in my home feed. When I go to her profile page, it says I'm following her but I'm not in her followers-list. Her profile page on wyrms.de shows that I follow her.

I unfollowed and followed again. wyrms.de received my first follow activity but the second one doesn't show up in the logs.

**Screenshots**
This is how it looks from wyrms.de:
![e491241f-414d-416b-9e7b-ec8b80fc056a](https://user-images.githubusercontent.com/3681516/113873020-b74b3980-97b4-11eb-81d7-f76be957273b.png)

Her profile page with followers-list, from bookwyrm.social:
![screenshot_2021-04-07T15:24:04](https://user-images.githubusercontent.com/3681516/113873683-4f492300-97b5-11eb-9371-3fe6bb96253a.png)


Discussion:
I see a failed task in the logs on b.s - it looks like part of the transaction was lost, which caused the incoming Accept from wyrms.de to fail. This looks like a transient failure to me, did unfollowing and refollowing resolve it? I think the bigger issue is that activities need to be more resilient to one of the servers temporarily not responding
I unfollowed and refollowed just now, it didn't work.
I was able to replicate this locally! I'll take a close look tomorrow.
I have the same issue with another account on wyrms.de. Is it just me or does anyone else have the same problem?
Not sure if it's the same issue: Bookwyrm un-followed a user from wyrms.de without me doing anything. Re-following doesn't work, like with the other accounts in this thread.
That sounds related! I'm taking a look at this today
Trying again, I haven't been able to replicate the issues with following users, but I am seeing that, according to Flower, your user appears to be editing _Braiding Sweetgrass_ every thirty seconds nonstop for a days. It seems like something has gone haywire with that broadcast action, and it could be that that is preventing other activities from working correctly
#979 won't fix whatever issue is causing this in the first place, but it should make it so that it works when you try to re-follow a user

Resolving patch:
diff --git a/bookwyrm/models/fields.py b/bookwyrm/models/fields.py
--- a/bookwyrm/models/fields.py
+++ b/bookwyrm/models/fields.py
@@ -275,9 +275,12 @@ def field_to_activity(self, value):
         return [i.remote_id for i in value.all()]
 
     def field_from_activity(self, value):
-        items = []
         if value is None or value is MISSING:
-            return []
+            return None
+        if not isinstance(value, list):
+            # If this is a link, we currently aren't doing anything with it
+            return None
+        items = []
         for remote_id in value:
             try:
                 validate_remote_id(remote_id)


Test patch:
diff --git a/bookwyrm/tests/data/ap_user_rat.json b/bookwyrm/tests/data/ap_user_rat.json
new file mode 100644
--- /dev/null
+++ b/bookwyrm/tests/data/ap_user_rat.json
@@ -0,0 +1,39 @@
+{
+    "@context": [
+        "https://www.w3.org/ns/activitystreams",
+        "https://w3id.org/security/v1",
+        {
+            "manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
+            "schema": "http://schema.org#",
+            "PropertyValue": "schema:PropertyValue",
+            "value": "schema:value"
+        }
+    ],
+    "id": "https://example.com/users/rat",
+    "type": "Person",
+    "preferredUsername": "rat",
+    "name": "RAT???",
+    "inbox": "https://example.com/users/rat/inbox",
+    "outbox": "https://example.com/users/rat/outbox",
+    "followers": "https://example.com/users/rat/followers",
+    "following": "https://example.com/users/rat/following",
+    "summary": "",
+    "publicKey": {
+        "id": "https://example.com/users/rat/#main-key",
+        "owner": "https://example.com/users/rat",
+        "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6QisDrjOQvkRo/MqNmSYPwqtt\nCxg/8rCW+9jKbFUKvqjTeKVotEE85122v/DCvobCCdfQuYIFdVMk+dB1xJ0iPGPg\nyU79QHY22NdV9mFKA2qtXVVxb5cxpA4PlwOHM6PM/k8B+H09OUrop2aPUAYwy+vg\n+MXyz8bAXrIS1kq6fQIDAQAB\n-----END PUBLIC KEY-----"
+    },
+    "endpoints": {
+        "sharedInbox": "https://example.com/inbox"
+    },
+    "bookwyrmUser": true,
+    "manuallyApprovesFollowers": false,
+    "discoverable": true,
+    "devices": "https://friend.camp/users/tripofmice/collections/devices",
+    "tag": [],
+    "icon": {
+        "type": "Image",
+        "mediaType": "image/png",
+        "url": "https://example.com/images/avatars/AL-2-crop-50.png"
+    }
+}
diff --git a/bookwyrm/tests/views/inbox/test_inbox_update.py b/bookwyrm/tests/views/inbox/test_inbox_update.py
--- a/bookwyrm/tests/views/inbox/test_inbox_update.py
+++ b/bookwyrm/tests/views/inbox/test_inbox_update.py
@@ -80,14 +80,22 @@ def test_update_list(self):
 
     def test_update_user(self):
         """ update an existing user """
-        # we only do this with remote users
-        self.local_user.local = False
-        self.local_user.save()
+        models.UserFollows.objects.create(
+            user_subject=self.local_user,
+            user_object=self.remote_user,
+        )
+        models.UserFollows.objects.create(
+            user_subject=self.remote_user,
+            user_object=self.local_user,
+        )
+        self.assertTrue(self.remote_user in self.local_user.followers.all())
+        self.assertTrue(self.local_user in self.remote_user.followers.all())
 
-        datafile = pathlib.Path(__file__).parent.joinpath("../../data/ap_user.json")
+        datafile = pathlib.Path(__file__).parent.joinpath("../../data/ap_user_rat.json")
         userdata = json.loads(datafile.read_bytes())
         del userdata["icon"]
-        self.assertIsNone(self.local_user.name)
+        self.assertIsNone(self.remote_user.name)
+        self.assertFalse(self.remote_user.discoverable)
         views.inbox.activity_task(
             {
                 "type": "Update",
@@ -98,12 +106,15 @@ def test_update_user(self):
                 "object": userdata,
             }
         )
-        user = models.User.objects.get(id=self.local_user.id)
-        self.assertEqual(user.name, "MOUSE?? MOUSE!!")
-        self.assertEqual(user.username, "mouse@example.com")
-        self.assertEqual(user.localname, "mouse")
+        user = models.User.objects.get(id=self.remote_user.id)
+        self.assertEqual(user.name, "RAT???")
+        self.assertEqual(user.username, "rat@example.com")
         self.assertTrue(user.discoverable)
 
+        # make sure relationships aren't disrupted
+        self.assertTrue(self.remote_user in self.local_user.followers.all())
+        self.assertTrue(self.local_user in self.remote_user.followers.all())
+
     def test_update_edition(self):
         """ update an existing edition """
         datafile = pathlib.Path(__file__).parent.joinpath("../../data/bw_edition.json")