diff --git a/activitypub.py b/activitypub.py index 81e31492..32747015 100644 --- a/activitypub.py +++ b/activitypub.py @@ -438,6 +438,10 @@ class ActivityPub(User, Protocol): obj = resp.json() except requests.JSONDecodeError: _error("Couldn't decode as JSON") + if not isinstance(obj, dict): + logger.warning(f'Got non-object: {obj}') + return resp, None + cls._hydrate(obj) return resp, obj diff --git a/common.py b/common.py index fb8e45ce..01670cb2 100644 --- a/common.py +++ b/common.py @@ -212,6 +212,10 @@ def content_type(resp): """Returns a :class:`requests.Response`'s Content-Type, without charset suffix.""" type = resp.headers.get('Content-Type') if type: + # TODO: don't remove profile + # right now, when we remove it, and don't use it to compare against eg + # as2.CONTENT_TYPE_LD, we end up accepting non-AS2 JSON-LD, eg: + # Content-Type: application/ld+json; charset=UTF-8 return type.split(';')[0] diff --git a/tests/test_activitypub.py b/tests/test_activitypub.py index 5acdc3f9..aa4eb151 100644 --- a/tests/test_activitypub.py +++ b/tests/test_activitypub.py @@ -2864,24 +2864,34 @@ class ActivityPubUtilsTest(TestCase): mock_post.assert_called_once() self.assertEqual(302, resp.status_code) - @patch('requests.get') + @patch('requests.get', return_value=AS2) def test_fetch_direct(self, mock_get): - mock_get.return_value = AS2 obj = Object(id='http://orig') - ActivityPub.fetch(obj) + self.assertTrue(ActivityPub.fetch(obj)) self.assertEqual(AS2_OBJ, obj.as2) mock_get.assert_has_calls(( self.as2_req('http://orig'), )) + @patch('requests.get') + def test_fetch_direct_list(self, mock_get): + mock_get.return_value = self.as2_resp([AS2_OBJ]) + obj = Object(id='http://orig') + self.assertFalse(ActivityPub.fetch(obj)) + self.assertIsNone(obj.as2) + + mock_get.assert_has_calls(( + self.as2_req('http://orig'), + )) + @patch('requests.get') def test_fetch_direct_ld_content_type(self, mock_get): mock_get.return_value = requests_response(AS2_OBJ, headers={ 'Content-Type': 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', }) obj = Object(id='http://orig') - ActivityPub.fetch(obj) + self.assertTrue(ActivityPub.fetch(obj)) self.assertEqual(AS2_OBJ, obj.as2) mock_get.assert_has_calls(( @@ -2892,7 +2902,7 @@ class ActivityPubUtilsTest(TestCase): def test_fetch_via_html(self, mock_get): mock_get.side_effect = [HTML_WITH_AS2, AS2] obj = Object(id='http://orig') - ActivityPub.fetch(obj) + self.assertTrue(ActivityPub.fetch(obj)) self.assertEqual(AS2_OBJ, obj.as2) mock_get.assert_has_calls(( @@ -2956,7 +2966,7 @@ class ActivityPubUtilsTest(TestCase): mock_get.return_value = self.as2_resp(event_article) obj = Object(id='http://orig') - ActivityPub.fetch(obj) + self.assertTrue(ActivityPub.fetch(obj)) self.assertEqual(event_article, obj.as2) @patch('requests.get') @@ -2969,7 +2979,7 @@ class ActivityPubUtilsTest(TestCase): mock_get.side_effect = [self.as2_resp(actor), self.as2_resp(featured)] obj = Object(id='http://orig') - ActivityPub.fetch(obj) + self.assertTrue(ActivityPub.fetch(obj)) self.assertEqual({**actor, 'featured': {'foo': 'bar'}}, obj.as2) mock_get.assert_has_calls(( @@ -2986,7 +2996,7 @@ class ActivityPubUtilsTest(TestCase): mock_get.return_value = self.as2_resp(actor) obj = Object(id='http://orig') - ActivityPub.fetch(obj) + self.assertTrue(ActivityPub.fetch(obj)) self.assertEqual(actor, obj.as2) mock_get.assert_called_once()