Merge remote-tracking branch 'remotes/origin/develop' into 2168-media-preview-proxy
[akkoma] / test / web / activity_pub / transmogrifier_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
6 use Oban.Testing, repo: Pleroma.Repo
7 use Pleroma.DataCase
8
9 alias Pleroma.Activity
10 alias Pleroma.Object
11 alias Pleroma.Object.Fetcher
12 alias Pleroma.Tests.ObanHelpers
13 alias Pleroma.User
14 alias Pleroma.Web.ActivityPub.Transmogrifier
15 alias Pleroma.Web.AdminAPI.AccountView
16 alias Pleroma.Web.CommonAPI
17
18 import Mock
19 import Pleroma.Factory
20 import ExUnit.CaptureLog
21
22 setup_all do
23 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
24 :ok
25 end
26
27 setup do: clear_config([:instance, :max_remote_account_fields])
28
29 describe "handle_incoming" do
30 test "it works for incoming notices with tag not being an array (kroeg)" do
31 data = File.read!("test/fixtures/kroeg-array-less-emoji.json") |> Poison.decode!()
32
33 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
34 object = Object.normalize(data["object"])
35
36 assert object.data["emoji"] == %{
37 "icon_e_smile" => "https://puckipedia.com/forum/images/smilies/icon_e_smile.png"
38 }
39
40 data = File.read!("test/fixtures/kroeg-array-less-hashtag.json") |> Poison.decode!()
41
42 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
43 object = Object.normalize(data["object"])
44
45 assert "test" in object.data["tag"]
46 end
47
48 test "it works for incoming notices with url not being a string (prismo)" do
49 data = File.read!("test/fixtures/prismo-url-map.json") |> Poison.decode!()
50
51 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
52 object = Object.normalize(data["object"])
53
54 assert object.data["url"] == "https://prismo.news/posts/83"
55 end
56
57 test "it cleans up incoming notices which are not really DMs" do
58 user = insert(:user)
59 other_user = insert(:user)
60
61 to = [user.ap_id, other_user.ap_id]
62
63 data =
64 File.read!("test/fixtures/mastodon-post-activity.json")
65 |> Poison.decode!()
66 |> Map.put("to", to)
67 |> Map.put("cc", [])
68
69 object =
70 data["object"]
71 |> Map.put("to", to)
72 |> Map.put("cc", [])
73
74 data = Map.put(data, "object", object)
75
76 {:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data)
77
78 assert data["to"] == []
79 assert data["cc"] == to
80
81 object_data = Object.normalize(activity).data
82
83 assert object_data["to"] == []
84 assert object_data["cc"] == to
85 end
86
87 test "it ignores an incoming notice if we already have it" do
88 activity = insert(:note_activity)
89
90 data =
91 File.read!("test/fixtures/mastodon-post-activity.json")
92 |> Poison.decode!()
93 |> Map.put("object", Object.normalize(activity).data)
94
95 {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
96
97 assert activity == returned_activity
98 end
99
100 @tag capture_log: true
101 test "it fetches reply-to activities if we don't have them" do
102 data =
103 File.read!("test/fixtures/mastodon-post-activity.json")
104 |> Poison.decode!()
105
106 object =
107 data["object"]
108 |> Map.put("inReplyTo", "https://shitposter.club/notice/2827873")
109
110 data = Map.put(data, "object", object)
111 {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
112 returned_object = Object.normalize(returned_activity, false)
113
114 assert activity =
115 Activity.get_create_by_object_ap_id(
116 "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
117 )
118
119 assert returned_object.data["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
120 end
121
122 test "it does not fetch reply-to activities beyond max replies depth limit" do
123 data =
124 File.read!("test/fixtures/mastodon-post-activity.json")
125 |> Poison.decode!()
126
127 object =
128 data["object"]
129 |> Map.put("inReplyTo", "https://shitposter.club/notice/2827873")
130
131 data = Map.put(data, "object", object)
132
133 with_mock Pleroma.Web.Federator,
134 allowed_thread_distance?: fn _ -> false end do
135 {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
136
137 returned_object = Object.normalize(returned_activity, false)
138
139 refute Activity.get_create_by_object_ap_id(
140 "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
141 )
142
143 assert returned_object.data["inReplyToAtomUri"] ==
144 "https://shitposter.club/notice/2827873"
145 end
146 end
147
148 test "it does not crash if the object in inReplyTo can't be fetched" do
149 data =
150 File.read!("test/fixtures/mastodon-post-activity.json")
151 |> Poison.decode!()
152
153 object =
154 data["object"]
155 |> Map.put("inReplyTo", "https://404.site/whatever")
156
157 data =
158 data
159 |> Map.put("object", object)
160
161 assert capture_log(fn ->
162 {:ok, _returned_activity} = Transmogrifier.handle_incoming(data)
163 end) =~ "[warn] Couldn't fetch \"https://404.site/whatever\", error: nil"
164 end
165
166 test "it does not work for deactivated users" do
167 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
168
169 insert(:user, ap_id: data["actor"], deactivated: true)
170
171 assert {:error, _} = Transmogrifier.handle_incoming(data)
172 end
173
174 test "it works for incoming notices" do
175 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
176
177 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
178
179 assert data["id"] ==
180 "http://mastodon.example.org/users/admin/statuses/99512778738411822/activity"
181
182 assert data["context"] ==
183 "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation"
184
185 assert data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
186
187 assert data["cc"] == [
188 "http://mastodon.example.org/users/admin/followers",
189 "http://localtesting.pleroma.lol/users/lain"
190 ]
191
192 assert data["actor"] == "http://mastodon.example.org/users/admin"
193
194 object_data = Object.normalize(data["object"]).data
195
196 assert object_data["id"] ==
197 "http://mastodon.example.org/users/admin/statuses/99512778738411822"
198
199 assert object_data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
200
201 assert object_data["cc"] == [
202 "http://mastodon.example.org/users/admin/followers",
203 "http://localtesting.pleroma.lol/users/lain"
204 ]
205
206 assert object_data["actor"] == "http://mastodon.example.org/users/admin"
207 assert object_data["attributedTo"] == "http://mastodon.example.org/users/admin"
208
209 assert object_data["context"] ==
210 "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation"
211
212 assert object_data["sensitive"] == true
213
214 user = User.get_cached_by_ap_id(object_data["actor"])
215
216 assert user.note_count == 1
217 end
218
219 test "it works for incoming notices with hashtags" do
220 data = File.read!("test/fixtures/mastodon-post-activity-hashtag.json") |> Poison.decode!()
221
222 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
223 object = Object.normalize(data["object"])
224
225 assert Enum.at(object.data["tag"], 2) == "moo"
226 end
227
228 test "it works for incoming listens" do
229 data = %{
230 "@context" => "https://www.w3.org/ns/activitystreams",
231 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
232 "cc" => [],
233 "type" => "Listen",
234 "id" => "http://mastodon.example.org/users/admin/listens/1234/activity",
235 "actor" => "http://mastodon.example.org/users/admin",
236 "object" => %{
237 "type" => "Audio",
238 "id" => "http://mastodon.example.org/users/admin/listens/1234",
239 "attributedTo" => "http://mastodon.example.org/users/admin",
240 "title" => "lain radio episode 1",
241 "artist" => "lain",
242 "album" => "lain radio",
243 "length" => 180_000
244 }
245 }
246
247 {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data)
248
249 object = Object.normalize(activity)
250
251 assert object.data["title"] == "lain radio episode 1"
252 assert object.data["artist"] == "lain"
253 assert object.data["album"] == "lain radio"
254 assert object.data["length"] == 180_000
255 end
256
257 test "it works for incoming notices with contentMap" do
258 data =
259 File.read!("test/fixtures/mastodon-post-activity-contentmap.json") |> Poison.decode!()
260
261 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
262 object = Object.normalize(data["object"])
263
264 assert object.data["content"] ==
265 "<p><span class=\"h-card\"><a href=\"http://localtesting.pleroma.lol/users/lain\" class=\"u-url mention\">@<span>lain</span></a></span></p>"
266 end
267
268 test "it works for incoming notices with to/cc not being an array (kroeg)" do
269 data = File.read!("test/fixtures/kroeg-post-activity.json") |> Poison.decode!()
270
271 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
272 object = Object.normalize(data["object"])
273
274 assert object.data["content"] ==
275 "<p>henlo from my Psion netBook</p><p>message sent from my Psion netBook</p>"
276 end
277
278 test "it ensures that as:Public activities make it to their followers collection" do
279 user = insert(:user)
280
281 data =
282 File.read!("test/fixtures/mastodon-post-activity.json")
283 |> Poison.decode!()
284 |> Map.put("actor", user.ap_id)
285 |> Map.put("to", ["https://www.w3.org/ns/activitystreams#Public"])
286 |> Map.put("cc", [])
287
288 object =
289 data["object"]
290 |> Map.put("attributedTo", user.ap_id)
291 |> Map.put("to", ["https://www.w3.org/ns/activitystreams#Public"])
292 |> Map.put("cc", [])
293 |> Map.put("id", user.ap_id <> "/activities/12345678")
294
295 data = Map.put(data, "object", object)
296
297 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
298
299 assert data["cc"] == [User.ap_followers(user)]
300 end
301
302 test "it ensures that address fields become lists" do
303 user = insert(:user)
304
305 data =
306 File.read!("test/fixtures/mastodon-post-activity.json")
307 |> Poison.decode!()
308 |> Map.put("actor", user.ap_id)
309 |> Map.put("to", nil)
310 |> Map.put("cc", nil)
311
312 object =
313 data["object"]
314 |> Map.put("attributedTo", user.ap_id)
315 |> Map.put("to", nil)
316 |> Map.put("cc", nil)
317 |> Map.put("id", user.ap_id <> "/activities/12345678")
318
319 data = Map.put(data, "object", object)
320
321 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
322
323 assert !is_nil(data["to"])
324 assert !is_nil(data["cc"])
325 end
326
327 test "it strips internal likes" do
328 data =
329 File.read!("test/fixtures/mastodon-post-activity.json")
330 |> Poison.decode!()
331
332 likes = %{
333 "first" =>
334 "http://mastodon.example.org/objects/dbdbc507-52c8-490d-9b7c-1e1d52e5c132/likes?page=1",
335 "id" => "http://mastodon.example.org/objects/dbdbc507-52c8-490d-9b7c-1e1d52e5c132/likes",
336 "totalItems" => 3,
337 "type" => "OrderedCollection"
338 }
339
340 object = Map.put(data["object"], "likes", likes)
341 data = Map.put(data, "object", object)
342
343 {:ok, %Activity{object: object}} = Transmogrifier.handle_incoming(data)
344
345 refute Map.has_key?(object.data, "likes")
346 end
347
348 test "it strips internal reactions" do
349 user = insert(:user)
350 {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"})
351 {:ok, _} = CommonAPI.react_with_emoji(activity.id, user, "📢")
352
353 %{object: object} = Activity.get_by_id_with_object(activity.id)
354 assert Map.has_key?(object.data, "reactions")
355 assert Map.has_key?(object.data, "reaction_count")
356
357 object_data = Transmogrifier.strip_internal_fields(object.data)
358 refute Map.has_key?(object_data, "reactions")
359 refute Map.has_key?(object_data, "reaction_count")
360 end
361
362 test "it works for incomming unfollows with an existing follow" do
363 user = insert(:user)
364
365 follow_data =
366 File.read!("test/fixtures/mastodon-follow-activity.json")
367 |> Poison.decode!()
368 |> Map.put("object", user.ap_id)
369
370 {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(follow_data)
371
372 data =
373 File.read!("test/fixtures/mastodon-unfollow-activity.json")
374 |> Poison.decode!()
375 |> Map.put("object", follow_data)
376
377 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
378
379 assert data["type"] == "Undo"
380 assert data["object"]["type"] == "Follow"
381 assert data["object"]["object"] == user.ap_id
382 assert data["actor"] == "http://mastodon.example.org/users/admin"
383
384 refute User.following?(User.get_cached_by_ap_id(data["actor"]), user)
385 end
386
387 test "it works for incoming follows to locked account" do
388 pending_follower = insert(:user, ap_id: "http://mastodon.example.org/users/admin")
389 user = insert(:user, locked: true)
390
391 data =
392 File.read!("test/fixtures/mastodon-follow-activity.json")
393 |> Poison.decode!()
394 |> Map.put("object", user.ap_id)
395
396 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
397
398 assert data["type"] == "Follow"
399 assert data["object"] == user.ap_id
400 assert data["state"] == "pending"
401 assert data["actor"] == "http://mastodon.example.org/users/admin"
402
403 assert [^pending_follower] = User.get_follow_requests(user)
404 end
405
406 test "it works for incoming accepts which were pre-accepted" do
407 follower = insert(:user)
408 followed = insert(:user)
409
410 {:ok, follower} = User.follow(follower, followed)
411 assert User.following?(follower, followed) == true
412
413 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed)
414
415 accept_data =
416 File.read!("test/fixtures/mastodon-accept-activity.json")
417 |> Poison.decode!()
418 |> Map.put("actor", followed.ap_id)
419
420 object =
421 accept_data["object"]
422 |> Map.put("actor", follower.ap_id)
423 |> Map.put("id", follow_activity.data["id"])
424
425 accept_data = Map.put(accept_data, "object", object)
426
427 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
428 refute activity.local
429
430 assert activity.data["object"] == follow_activity.data["id"]
431
432 assert activity.data["id"] == accept_data["id"]
433
434 follower = User.get_cached_by_id(follower.id)
435
436 assert User.following?(follower, followed) == true
437 end
438
439 test "it works for incoming accepts which were orphaned" do
440 follower = insert(:user)
441 followed = insert(:user, locked: true)
442
443 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed)
444
445 accept_data =
446 File.read!("test/fixtures/mastodon-accept-activity.json")
447 |> Poison.decode!()
448 |> Map.put("actor", followed.ap_id)
449
450 accept_data =
451 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
452
453 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
454 assert activity.data["object"] == follow_activity.data["id"]
455
456 follower = User.get_cached_by_id(follower.id)
457
458 assert User.following?(follower, followed) == true
459 end
460
461 test "it works for incoming accepts which are referenced by IRI only" do
462 follower = insert(:user)
463 followed = insert(:user, locked: true)
464
465 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed)
466
467 accept_data =
468 File.read!("test/fixtures/mastodon-accept-activity.json")
469 |> Poison.decode!()
470 |> Map.put("actor", followed.ap_id)
471 |> Map.put("object", follow_activity.data["id"])
472
473 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
474 assert activity.data["object"] == follow_activity.data["id"]
475
476 follower = User.get_cached_by_id(follower.id)
477
478 assert User.following?(follower, followed) == true
479
480 follower = User.get_by_id(follower.id)
481 assert follower.following_count == 1
482
483 followed = User.get_by_id(followed.id)
484 assert followed.follower_count == 1
485 end
486
487 test "it fails for incoming accepts which cannot be correlated" do
488 follower = insert(:user)
489 followed = insert(:user, locked: true)
490
491 accept_data =
492 File.read!("test/fixtures/mastodon-accept-activity.json")
493 |> Poison.decode!()
494 |> Map.put("actor", followed.ap_id)
495
496 accept_data =
497 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
498
499 :error = Transmogrifier.handle_incoming(accept_data)
500
501 follower = User.get_cached_by_id(follower.id)
502
503 refute User.following?(follower, followed) == true
504 end
505
506 test "it fails for incoming rejects which cannot be correlated" do
507 follower = insert(:user)
508 followed = insert(:user, locked: true)
509
510 accept_data =
511 File.read!("test/fixtures/mastodon-reject-activity.json")
512 |> Poison.decode!()
513 |> Map.put("actor", followed.ap_id)
514
515 accept_data =
516 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
517
518 :error = Transmogrifier.handle_incoming(accept_data)
519
520 follower = User.get_cached_by_id(follower.id)
521
522 refute User.following?(follower, followed) == true
523 end
524
525 test "it works for incoming rejects which are orphaned" do
526 follower = insert(:user)
527 followed = insert(:user, locked: true)
528
529 {:ok, follower} = User.follow(follower, followed)
530 {:ok, _, _, _follow_activity} = CommonAPI.follow(follower, followed)
531
532 assert User.following?(follower, followed) == true
533
534 reject_data =
535 File.read!("test/fixtures/mastodon-reject-activity.json")
536 |> Poison.decode!()
537 |> Map.put("actor", followed.ap_id)
538
539 reject_data =
540 Map.put(reject_data, "object", Map.put(reject_data["object"], "actor", follower.ap_id))
541
542 {:ok, activity} = Transmogrifier.handle_incoming(reject_data)
543 refute activity.local
544 assert activity.data["id"] == reject_data["id"]
545
546 follower = User.get_cached_by_id(follower.id)
547
548 assert User.following?(follower, followed) == false
549 end
550
551 test "it works for incoming rejects which are referenced by IRI only" do
552 follower = insert(:user)
553 followed = insert(:user, locked: true)
554
555 {:ok, follower} = User.follow(follower, followed)
556 {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed)
557
558 assert User.following?(follower, followed) == true
559
560 reject_data =
561 File.read!("test/fixtures/mastodon-reject-activity.json")
562 |> Poison.decode!()
563 |> Map.put("actor", followed.ap_id)
564 |> Map.put("object", follow_activity.data["id"])
565
566 {:ok, %Activity{data: _}} = Transmogrifier.handle_incoming(reject_data)
567
568 follower = User.get_cached_by_id(follower.id)
569
570 assert User.following?(follower, followed) == false
571 end
572
573 test "it rejects activities without a valid ID" do
574 user = insert(:user)
575
576 data =
577 File.read!("test/fixtures/mastodon-follow-activity.json")
578 |> Poison.decode!()
579 |> Map.put("object", user.ap_id)
580 |> Map.put("id", "")
581
582 :error = Transmogrifier.handle_incoming(data)
583 end
584
585 test "skip converting the content when it is nil" do
586 object_id = "https://peertube.social/videos/watch/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe"
587
588 {:ok, object} = Fetcher.fetch_and_contain_remote_object_from_id(object_id)
589
590 result =
591 Pleroma.Web.ActivityPub.Transmogrifier.fix_object(Map.merge(object, %{"content" => nil}))
592
593 assert result["content"] == nil
594 end
595
596 test "it converts content of object to html" do
597 object_id = "https://peertube.social/videos/watch/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe"
598
599 {:ok, %{"content" => content_markdown}} =
600 Fetcher.fetch_and_contain_remote_object_from_id(object_id)
601
602 {:ok, %Pleroma.Object{data: %{"content" => content}} = object} =
603 Fetcher.fetch_object_from_id(object_id)
604
605 assert content_markdown ==
606 "Support this and our other Michigan!/usr/group videos and meetings. Learn more at http://mug.org/membership\n\nTwenty Years in Jail: FreeBSD's Jails, Then and Now\n\nJails started as a limited virtualization system, but over the last two years they've..."
607
608 assert content ==
609 "<p>Support this and our other Michigan!/usr/group videos and meetings. Learn more at <a href=\"http://mug.org/membership\">http://mug.org/membership</a></p><p>Twenty Years in Jail: FreeBSD’s Jails, Then and Now</p><p>Jails started as a limited virtualization system, but over the last two years they’ve…</p>"
610
611 assert object.data["mediaType"] == "text/html"
612 end
613
614 test "it remaps video URLs as attachments if necessary" do
615 {:ok, object} =
616 Fetcher.fetch_object_from_id(
617 "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
618 )
619
620 assert object.data["url"] ==
621 "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
622
623 assert object.data["attachment"] == [
624 %{
625 "type" => "Link",
626 "mediaType" => "video/mp4",
627 "url" => [
628 %{
629 "href" =>
630 "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
631 "mediaType" => "video/mp4",
632 "type" => "Link"
633 }
634 ]
635 }
636 ]
637
638 {:ok, object} =
639 Fetcher.fetch_object_from_id(
640 "https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206"
641 )
642
643 assert object.data["attachment"] == [
644 %{
645 "type" => "Link",
646 "mediaType" => "video/mp4",
647 "url" => [
648 %{
649 "href" =>
650 "https://framatube.org/static/webseed/6050732a-8a7a-43d4-a6cd-809525a1d206-1080.mp4",
651 "mediaType" => "video/mp4",
652 "type" => "Link"
653 }
654 ]
655 }
656 ]
657
658 assert object.data["url"] ==
659 "https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206"
660 end
661
662 test "it accepts Flag activities" do
663 user = insert(:user)
664 other_user = insert(:user)
665
666 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
667 object = Object.normalize(activity)
668
669 note_obj = %{
670 "type" => "Note",
671 "id" => activity.data["id"],
672 "content" => "test post",
673 "published" => object.data["published"],
674 "actor" => AccountView.render("show.json", %{user: user, skip_visibility_check: true})
675 }
676
677 message = %{
678 "@context" => "https://www.w3.org/ns/activitystreams",
679 "cc" => [user.ap_id],
680 "object" => [user.ap_id, activity.data["id"]],
681 "type" => "Flag",
682 "content" => "blocked AND reported!!!",
683 "actor" => other_user.ap_id
684 }
685
686 assert {:ok, activity} = Transmogrifier.handle_incoming(message)
687
688 assert activity.data["object"] == [user.ap_id, note_obj]
689 assert activity.data["content"] == "blocked AND reported!!!"
690 assert activity.data["actor"] == other_user.ap_id
691 assert activity.data["cc"] == [user.ap_id]
692 end
693
694 test "it correctly processes messages with non-array to field" do
695 user = insert(:user)
696
697 message = %{
698 "@context" => "https://www.w3.org/ns/activitystreams",
699 "to" => "https://www.w3.org/ns/activitystreams#Public",
700 "type" => "Create",
701 "object" => %{
702 "content" => "blah blah blah",
703 "type" => "Note",
704 "attributedTo" => user.ap_id,
705 "inReplyTo" => nil
706 },
707 "actor" => user.ap_id
708 }
709
710 assert {:ok, activity} = Transmogrifier.handle_incoming(message)
711
712 assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["to"]
713 end
714
715 test "it correctly processes messages with non-array cc field" do
716 user = insert(:user)
717
718 message = %{
719 "@context" => "https://www.w3.org/ns/activitystreams",
720 "to" => user.follower_address,
721 "cc" => "https://www.w3.org/ns/activitystreams#Public",
722 "type" => "Create",
723 "object" => %{
724 "content" => "blah blah blah",
725 "type" => "Note",
726 "attributedTo" => user.ap_id,
727 "inReplyTo" => nil
728 },
729 "actor" => user.ap_id
730 }
731
732 assert {:ok, activity} = Transmogrifier.handle_incoming(message)
733
734 assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"]
735 assert [user.follower_address] == activity.data["to"]
736 end
737
738 test "it correctly processes messages with weirdness in address fields" do
739 user = insert(:user)
740
741 message = %{
742 "@context" => "https://www.w3.org/ns/activitystreams",
743 "to" => [nil, user.follower_address],
744 "cc" => ["https://www.w3.org/ns/activitystreams#Public", ["¿"]],
745 "type" => "Create",
746 "object" => %{
747 "content" => "…",
748 "type" => "Note",
749 "attributedTo" => user.ap_id,
750 "inReplyTo" => nil
751 },
752 "actor" => user.ap_id
753 }
754
755 assert {:ok, activity} = Transmogrifier.handle_incoming(message)
756
757 assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"]
758 assert [user.follower_address] == activity.data["to"]
759 end
760
761 test "it accepts Move activities" do
762 old_user = insert(:user)
763 new_user = insert(:user)
764
765 message = %{
766 "@context" => "https://www.w3.org/ns/activitystreams",
767 "type" => "Move",
768 "actor" => old_user.ap_id,
769 "object" => old_user.ap_id,
770 "target" => new_user.ap_id
771 }
772
773 assert :error = Transmogrifier.handle_incoming(message)
774
775 {:ok, _new_user} = User.update_and_set_cache(new_user, %{also_known_as: [old_user.ap_id]})
776
777 assert {:ok, %Activity{} = activity} = Transmogrifier.handle_incoming(message)
778 assert activity.actor == old_user.ap_id
779 assert activity.data["actor"] == old_user.ap_id
780 assert activity.data["object"] == old_user.ap_id
781 assert activity.data["target"] == new_user.ap_id
782 assert activity.data["type"] == "Move"
783 end
784 end
785
786 describe "`handle_incoming/2`, Mastodon format `replies` handling" do
787 setup do: clear_config([:activitypub, :note_replies_output_limit], 5)
788 setup do: clear_config([:instance, :federation_incoming_replies_max_depth])
789
790 setup do
791 data =
792 "test/fixtures/mastodon-post-activity.json"
793 |> File.read!()
794 |> Poison.decode!()
795
796 items = get_in(data, ["object", "replies", "first", "items"])
797 assert length(items) > 0
798
799 %{data: data, items: items}
800 end
801
802 test "schedules background fetching of `replies` items if max thread depth limit allows", %{
803 data: data,
804 items: items
805 } do
806 Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 10)
807
808 {:ok, _activity} = Transmogrifier.handle_incoming(data)
809
810 for id <- items do
811 job_args = %{"op" => "fetch_remote", "id" => id, "depth" => 1}
812 assert_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker, args: job_args)
813 end
814 end
815
816 test "does NOT schedule background fetching of `replies` beyond max thread depth limit allows",
817 %{data: data} do
818 Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0)
819
820 {:ok, _activity} = Transmogrifier.handle_incoming(data)
821
822 assert all_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker) == []
823 end
824 end
825
826 describe "`handle_incoming/2`, Pleroma format `replies` handling" do
827 setup do: clear_config([:activitypub, :note_replies_output_limit], 5)
828 setup do: clear_config([:instance, :federation_incoming_replies_max_depth])
829
830 setup do
831 user = insert(:user)
832
833 {:ok, activity} = CommonAPI.post(user, %{status: "post1"})
834
835 {:ok, reply1} =
836 CommonAPI.post(user, %{status: "reply1", in_reply_to_status_id: activity.id})
837
838 {:ok, reply2} =
839 CommonAPI.post(user, %{status: "reply2", in_reply_to_status_id: activity.id})
840
841 replies_uris = Enum.map([reply1, reply2], fn a -> a.object.data["id"] end)
842
843 {:ok, federation_output} = Transmogrifier.prepare_outgoing(activity.data)
844
845 Repo.delete(activity.object)
846 Repo.delete(activity)
847
848 %{federation_output: federation_output, replies_uris: replies_uris}
849 end
850
851 test "schedules background fetching of `replies` items if max thread depth limit allows", %{
852 federation_output: federation_output,
853 replies_uris: replies_uris
854 } do
855 Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 1)
856
857 {:ok, _activity} = Transmogrifier.handle_incoming(federation_output)
858
859 for id <- replies_uris do
860 job_args = %{"op" => "fetch_remote", "id" => id, "depth" => 1}
861 assert_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker, args: job_args)
862 end
863 end
864
865 test "does NOT schedule background fetching of `replies` beyond max thread depth limit allows",
866 %{federation_output: federation_output} do
867 Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0)
868
869 {:ok, _activity} = Transmogrifier.handle_incoming(federation_output)
870
871 assert all_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker) == []
872 end
873 end
874
875 describe "prepare outgoing" do
876 test "it inlines private announced objects" do
877 user = insert(:user)
878
879 {:ok, activity} = CommonAPI.post(user, %{status: "hey", visibility: "private"})
880
881 {:ok, announce_activity} = CommonAPI.repeat(activity.id, user)
882
883 {:ok, modified} = Transmogrifier.prepare_outgoing(announce_activity.data)
884
885 assert modified["object"]["content"] == "hey"
886 assert modified["object"]["actor"] == modified["object"]["attributedTo"]
887 end
888
889 test "it turns mentions into tags" do
890 user = insert(:user)
891 other_user = insert(:user)
892
893 {:ok, activity} =
894 CommonAPI.post(user, %{status: "hey, @#{other_user.nickname}, how are ya? #2hu"})
895
896 with_mock Pleroma.Notification,
897 get_notified_from_activity: fn _, _ -> [] end do
898 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
899
900 object = modified["object"]
901
902 expected_mention = %{
903 "href" => other_user.ap_id,
904 "name" => "@#{other_user.nickname}",
905 "type" => "Mention"
906 }
907
908 expected_tag = %{
909 "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
910 "type" => "Hashtag",
911 "name" => "#2hu"
912 }
913
914 refute called(Pleroma.Notification.get_notified_from_activity(:_, :_))
915 assert Enum.member?(object["tag"], expected_tag)
916 assert Enum.member?(object["tag"], expected_mention)
917 end
918 end
919
920 test "it adds the sensitive property" do
921 user = insert(:user)
922
923 {:ok, activity} = CommonAPI.post(user, %{status: "#nsfw hey"})
924 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
925
926 assert modified["object"]["sensitive"]
927 end
928
929 test "it adds the json-ld context and the conversation property" do
930 user = insert(:user)
931
932 {:ok, activity} = CommonAPI.post(user, %{status: "hey"})
933 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
934
935 assert modified["@context"] ==
936 Pleroma.Web.ActivityPub.Utils.make_json_ld_header()["@context"]
937
938 assert modified["object"]["conversation"] == modified["context"]
939 end
940
941 test "it sets the 'attributedTo' property to the actor of the object if it doesn't have one" do
942 user = insert(:user)
943
944 {:ok, activity} = CommonAPI.post(user, %{status: "hey"})
945 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
946
947 assert modified["object"]["actor"] == modified["object"]["attributedTo"]
948 end
949
950 test "it strips internal hashtag data" do
951 user = insert(:user)
952
953 {:ok, activity} = CommonAPI.post(user, %{status: "#2hu"})
954
955 expected_tag = %{
956 "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
957 "type" => "Hashtag",
958 "name" => "#2hu"
959 }
960
961 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
962
963 assert modified["object"]["tag"] == [expected_tag]
964 end
965
966 test "it strips internal fields" do
967 user = insert(:user)
968
969 {:ok, activity} = CommonAPI.post(user, %{status: "#2hu :firefox:"})
970
971 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
972
973 assert length(modified["object"]["tag"]) == 2
974
975 assert is_nil(modified["object"]["emoji"])
976 assert is_nil(modified["object"]["like_count"])
977 assert is_nil(modified["object"]["announcements"])
978 assert is_nil(modified["object"]["announcement_count"])
979 assert is_nil(modified["object"]["context_id"])
980 end
981
982 test "it strips internal fields of article" do
983 activity = insert(:article_activity)
984
985 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
986
987 assert length(modified["object"]["tag"]) == 2
988
989 assert is_nil(modified["object"]["emoji"])
990 assert is_nil(modified["object"]["like_count"])
991 assert is_nil(modified["object"]["announcements"])
992 assert is_nil(modified["object"]["announcement_count"])
993 assert is_nil(modified["object"]["context_id"])
994 assert is_nil(modified["object"]["likes"])
995 end
996
997 test "the directMessage flag is present" do
998 user = insert(:user)
999 other_user = insert(:user)
1000
1001 {:ok, activity} = CommonAPI.post(user, %{status: "2hu :moominmamma:"})
1002
1003 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1004
1005 assert modified["directMessage"] == false
1006
1007 {:ok, activity} = CommonAPI.post(user, %{status: "@#{other_user.nickname} :moominmamma:"})
1008
1009 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1010
1011 assert modified["directMessage"] == false
1012
1013 {:ok, activity} =
1014 CommonAPI.post(user, %{
1015 status: "@#{other_user.nickname} :moominmamma:",
1016 visibility: "direct"
1017 })
1018
1019 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1020
1021 assert modified["directMessage"] == true
1022 end
1023
1024 test "it strips BCC field" do
1025 user = insert(:user)
1026 {:ok, list} = Pleroma.List.create("foo", user)
1027
1028 {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
1029
1030 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1031
1032 assert is_nil(modified["bcc"])
1033 end
1034
1035 test "it can handle Listen activities" do
1036 listen_activity = insert(:listen)
1037
1038 {:ok, modified} = Transmogrifier.prepare_outgoing(listen_activity.data)
1039
1040 assert modified["type"] == "Listen"
1041
1042 user = insert(:user)
1043
1044 {:ok, activity} = CommonAPI.listen(user, %{"title" => "lain radio episode 1"})
1045
1046 {:ok, _modified} = Transmogrifier.prepare_outgoing(activity.data)
1047 end
1048 end
1049
1050 describe "user upgrade" do
1051 test "it upgrades a user to activitypub" do
1052 user =
1053 insert(:user, %{
1054 nickname: "rye@niu.moe",
1055 local: false,
1056 ap_id: "https://niu.moe/users/rye",
1057 follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"})
1058 })
1059
1060 user_two = insert(:user)
1061 Pleroma.FollowingRelationship.follow(user_two, user, :follow_accept)
1062
1063 {:ok, activity} = CommonAPI.post(user, %{status: "test"})
1064 {:ok, unrelated_activity} = CommonAPI.post(user_two, %{status: "test"})
1065 assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients
1066
1067 user = User.get_cached_by_id(user.id)
1068 assert user.note_count == 1
1069
1070 {:ok, user} = Transmogrifier.upgrade_user_from_ap_id("https://niu.moe/users/rye")
1071 ObanHelpers.perform_all()
1072
1073 assert user.ap_enabled
1074 assert user.note_count == 1
1075 assert user.follower_address == "https://niu.moe/users/rye/followers"
1076 assert user.following_address == "https://niu.moe/users/rye/following"
1077
1078 user = User.get_cached_by_id(user.id)
1079 assert user.note_count == 1
1080
1081 activity = Activity.get_by_id(activity.id)
1082 assert user.follower_address in activity.recipients
1083
1084 assert %{
1085 "url" => [
1086 %{
1087 "href" =>
1088 "https://cdn.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
1089 }
1090 ]
1091 } = user.avatar
1092
1093 assert %{
1094 "url" => [
1095 %{
1096 "href" =>
1097 "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
1098 }
1099 ]
1100 } = user.banner
1101
1102 refute "..." in activity.recipients
1103
1104 unrelated_activity = Activity.get_by_id(unrelated_activity.id)
1105 refute user.follower_address in unrelated_activity.recipients
1106
1107 user_two = User.get_cached_by_id(user_two.id)
1108 assert User.following?(user_two, user)
1109 refute "..." in User.following(user_two)
1110 end
1111 end
1112
1113 describe "actor rewriting" do
1114 test "it fixes the actor URL property to be a proper URI" do
1115 data = %{
1116 "url" => %{"href" => "http://example.com"}
1117 }
1118
1119 rewritten = Transmogrifier.maybe_fix_user_object(data)
1120 assert rewritten["url"] == "http://example.com"
1121 end
1122 end
1123
1124 describe "actor origin containment" do
1125 test "it rejects activities which reference objects with bogus origins" do
1126 data = %{
1127 "@context" => "https://www.w3.org/ns/activitystreams",
1128 "id" => "http://mastodon.example.org/users/admin/activities/1234",
1129 "actor" => "http://mastodon.example.org/users/admin",
1130 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1131 "object" => "https://info.pleroma.site/activity.json",
1132 "type" => "Announce"
1133 }
1134
1135 assert capture_log(fn ->
1136 {:error, _} = Transmogrifier.handle_incoming(data)
1137 end) =~ "Object containment failed"
1138 end
1139
1140 test "it rejects activities which reference objects that have an incorrect attribution (variant 1)" do
1141 data = %{
1142 "@context" => "https://www.w3.org/ns/activitystreams",
1143 "id" => "http://mastodon.example.org/users/admin/activities/1234",
1144 "actor" => "http://mastodon.example.org/users/admin",
1145 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1146 "object" => "https://info.pleroma.site/activity2.json",
1147 "type" => "Announce"
1148 }
1149
1150 assert capture_log(fn ->
1151 {:error, _} = Transmogrifier.handle_incoming(data)
1152 end) =~ "Object containment failed"
1153 end
1154
1155 test "it rejects activities which reference objects that have an incorrect attribution (variant 2)" do
1156 data = %{
1157 "@context" => "https://www.w3.org/ns/activitystreams",
1158 "id" => "http://mastodon.example.org/users/admin/activities/1234",
1159 "actor" => "http://mastodon.example.org/users/admin",
1160 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1161 "object" => "https://info.pleroma.site/activity3.json",
1162 "type" => "Announce"
1163 }
1164
1165 assert capture_log(fn ->
1166 {:error, _} = Transmogrifier.handle_incoming(data)
1167 end) =~ "Object containment failed"
1168 end
1169 end
1170
1171 describe "reserialization" do
1172 test "successfully reserializes a message with inReplyTo == nil" do
1173 user = insert(:user)
1174
1175 message = %{
1176 "@context" => "https://www.w3.org/ns/activitystreams",
1177 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1178 "cc" => [],
1179 "type" => "Create",
1180 "object" => %{
1181 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1182 "cc" => [],
1183 "type" => "Note",
1184 "content" => "Hi",
1185 "inReplyTo" => nil,
1186 "attributedTo" => user.ap_id
1187 },
1188 "actor" => user.ap_id
1189 }
1190
1191 {:ok, activity} = Transmogrifier.handle_incoming(message)
1192
1193 {:ok, _} = Transmogrifier.prepare_outgoing(activity.data)
1194 end
1195
1196 test "successfully reserializes a message with AS2 objects in IR" do
1197 user = insert(:user)
1198
1199 message = %{
1200 "@context" => "https://www.w3.org/ns/activitystreams",
1201 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1202 "cc" => [],
1203 "type" => "Create",
1204 "object" => %{
1205 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1206 "cc" => [],
1207 "type" => "Note",
1208 "content" => "Hi",
1209 "inReplyTo" => nil,
1210 "attributedTo" => user.ap_id,
1211 "tag" => [
1212 %{"name" => "#2hu", "href" => "http://example.com/2hu", "type" => "Hashtag"},
1213 %{"name" => "Bob", "href" => "http://example.com/bob", "type" => "Mention"}
1214 ]
1215 },
1216 "actor" => user.ap_id
1217 }
1218
1219 {:ok, activity} = Transmogrifier.handle_incoming(message)
1220
1221 {:ok, _} = Transmogrifier.prepare_outgoing(activity.data)
1222 end
1223 end
1224
1225 describe "fix_explicit_addressing" do
1226 setup do
1227 user = insert(:user)
1228 [user: user]
1229 end
1230
1231 test "moves non-explicitly mentioned actors to cc", %{user: user} do
1232 explicitly_mentioned_actors = [
1233 "https://pleroma.gold/users/user1",
1234 "https://pleroma.gold/user2"
1235 ]
1236
1237 object = %{
1238 "actor" => user.ap_id,
1239 "to" => explicitly_mentioned_actors ++ ["https://social.beepboop.ga/users/dirb"],
1240 "cc" => [],
1241 "tag" =>
1242 Enum.map(explicitly_mentioned_actors, fn href ->
1243 %{"type" => "Mention", "href" => href}
1244 end)
1245 }
1246
1247 fixed_object = Transmogrifier.fix_explicit_addressing(object)
1248 assert Enum.all?(explicitly_mentioned_actors, &(&1 in fixed_object["to"]))
1249 refute "https://social.beepboop.ga/users/dirb" in fixed_object["to"]
1250 assert "https://social.beepboop.ga/users/dirb" in fixed_object["cc"]
1251 end
1252
1253 test "does not move actor's follower collection to cc", %{user: user} do
1254 object = %{
1255 "actor" => user.ap_id,
1256 "to" => [user.follower_address],
1257 "cc" => []
1258 }
1259
1260 fixed_object = Transmogrifier.fix_explicit_addressing(object)
1261 assert user.follower_address in fixed_object["to"]
1262 refute user.follower_address in fixed_object["cc"]
1263 end
1264
1265 test "removes recipient's follower collection from cc", %{user: user} do
1266 recipient = insert(:user)
1267
1268 object = %{
1269 "actor" => user.ap_id,
1270 "to" => [recipient.ap_id, "https://www.w3.org/ns/activitystreams#Public"],
1271 "cc" => [user.follower_address, recipient.follower_address]
1272 }
1273
1274 fixed_object = Transmogrifier.fix_explicit_addressing(object)
1275
1276 assert user.follower_address in fixed_object["cc"]
1277 refute recipient.follower_address in fixed_object["cc"]
1278 refute recipient.follower_address in fixed_object["to"]
1279 end
1280 end
1281
1282 describe "fix_summary/1" do
1283 test "returns fixed object" do
1284 assert Transmogrifier.fix_summary(%{"summary" => nil}) == %{"summary" => ""}
1285 assert Transmogrifier.fix_summary(%{"summary" => "ok"}) == %{"summary" => "ok"}
1286 assert Transmogrifier.fix_summary(%{}) == %{"summary" => ""}
1287 end
1288 end
1289
1290 describe "fix_in_reply_to/2" do
1291 setup do: clear_config([:instance, :federation_incoming_replies_max_depth])
1292
1293 setup do
1294 data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json"))
1295 [data: data]
1296 end
1297
1298 test "returns not modified object when hasn't containts inReplyTo field", %{data: data} do
1299 assert Transmogrifier.fix_in_reply_to(data) == data
1300 end
1301
1302 test "returns object with inReplyToAtomUri when denied incoming reply", %{data: data} do
1303 Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0)
1304
1305 object_with_reply =
1306 Map.put(data["object"], "inReplyTo", "https://shitposter.club/notice/2827873")
1307
1308 modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
1309 assert modified_object["inReplyTo"] == "https://shitposter.club/notice/2827873"
1310 assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
1311
1312 object_with_reply =
1313 Map.put(data["object"], "inReplyTo", %{"id" => "https://shitposter.club/notice/2827873"})
1314
1315 modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
1316 assert modified_object["inReplyTo"] == %{"id" => "https://shitposter.club/notice/2827873"}
1317 assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
1318
1319 object_with_reply =
1320 Map.put(data["object"], "inReplyTo", ["https://shitposter.club/notice/2827873"])
1321
1322 modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
1323 assert modified_object["inReplyTo"] == ["https://shitposter.club/notice/2827873"]
1324 assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
1325
1326 object_with_reply = Map.put(data["object"], "inReplyTo", [])
1327 modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
1328 assert modified_object["inReplyTo"] == []
1329 assert modified_object["inReplyToAtomUri"] == ""
1330 end
1331
1332 @tag capture_log: true
1333 test "returns modified object when allowed incoming reply", %{data: data} do
1334 object_with_reply =
1335 Map.put(
1336 data["object"],
1337 "inReplyTo",
1338 "https://shitposter.club/notice/2827873"
1339 )
1340
1341 Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 5)
1342 modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
1343
1344 assert modified_object["inReplyTo"] ==
1345 "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
1346
1347 assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
1348
1349 assert modified_object["context"] ==
1350 "tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26"
1351 end
1352 end
1353
1354 describe "fix_url/1" do
1355 test "fixes data for object when url is map" do
1356 object = %{
1357 "url" => %{
1358 "type" => "Link",
1359 "mimeType" => "video/mp4",
1360 "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4"
1361 }
1362 }
1363
1364 assert Transmogrifier.fix_url(object) == %{
1365 "url" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4"
1366 }
1367 end
1368
1369 test "fixes data for video object" do
1370 object = %{
1371 "type" => "Video",
1372 "url" => [
1373 %{
1374 "type" => "Link",
1375 "mimeType" => "video/mp4",
1376 "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4"
1377 },
1378 %{
1379 "type" => "Link",
1380 "mimeType" => "video/mp4",
1381 "href" => "https://peertube46fb-ad81-2d4c2d1630e3-240.mp4"
1382 },
1383 %{
1384 "type" => "Link",
1385 "mimeType" => "text/html",
1386 "href" => "https://peertube.-2d4c2d1630e3"
1387 },
1388 %{
1389 "type" => "Link",
1390 "mimeType" => "text/html",
1391 "href" => "https://peertube.-2d4c2d16377-42"
1392 }
1393 ]
1394 }
1395
1396 assert Transmogrifier.fix_url(object) == %{
1397 "attachment" => [
1398 %{
1399 "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4",
1400 "mimeType" => "video/mp4",
1401 "type" => "Link"
1402 }
1403 ],
1404 "type" => "Video",
1405 "url" => "https://peertube.-2d4c2d1630e3"
1406 }
1407 end
1408
1409 test "fixes url for not Video object" do
1410 object = %{
1411 "type" => "Text",
1412 "url" => [
1413 %{
1414 "type" => "Link",
1415 "mimeType" => "text/html",
1416 "href" => "https://peertube.-2d4c2d1630e3"
1417 },
1418 %{
1419 "type" => "Link",
1420 "mimeType" => "text/html",
1421 "href" => "https://peertube.-2d4c2d16377-42"
1422 }
1423 ]
1424 }
1425
1426 assert Transmogrifier.fix_url(object) == %{
1427 "type" => "Text",
1428 "url" => "https://peertube.-2d4c2d1630e3"
1429 }
1430
1431 assert Transmogrifier.fix_url(%{"type" => "Text", "url" => []}) == %{
1432 "type" => "Text",
1433 "url" => ""
1434 }
1435 end
1436
1437 test "retunrs not modified object" do
1438 assert Transmogrifier.fix_url(%{"type" => "Text"}) == %{"type" => "Text"}
1439 end
1440 end
1441
1442 describe "get_obj_helper/2" do
1443 test "returns nil when cannot normalize object" do
1444 assert capture_log(fn ->
1445 refute Transmogrifier.get_obj_helper("test-obj-id")
1446 end) =~ "Unsupported URI scheme"
1447 end
1448
1449 @tag capture_log: true
1450 test "returns {:ok, %Object{}} for success case" do
1451 assert {:ok, %Object{}} =
1452 Transmogrifier.get_obj_helper("https://shitposter.club/notice/2827873")
1453 end
1454 end
1455
1456 describe "fix_attachments/1" do
1457 test "returns not modified object" do
1458 data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json"))
1459 assert Transmogrifier.fix_attachments(data) == data
1460 end
1461
1462 test "returns modified object when attachment is map" do
1463 assert Transmogrifier.fix_attachments(%{
1464 "attachment" => %{
1465 "mediaType" => "video/mp4",
1466 "url" => "https://peertube.moe/stat-480.mp4"
1467 }
1468 }) == %{
1469 "attachment" => [
1470 %{
1471 "mediaType" => "video/mp4",
1472 "type" => "Document",
1473 "url" => [
1474 %{
1475 "href" => "https://peertube.moe/stat-480.mp4",
1476 "mediaType" => "video/mp4",
1477 "type" => "Link"
1478 }
1479 ]
1480 }
1481 ]
1482 }
1483 end
1484
1485 test "returns modified object when attachment is list" do
1486 assert Transmogrifier.fix_attachments(%{
1487 "attachment" => [
1488 %{"mediaType" => "video/mp4", "url" => "https://pe.er/stat-480.mp4"},
1489 %{"mimeType" => "video/mp4", "href" => "https://pe.er/stat-480.mp4"}
1490 ]
1491 }) == %{
1492 "attachment" => [
1493 %{
1494 "mediaType" => "video/mp4",
1495 "type" => "Document",
1496 "url" => [
1497 %{
1498 "href" => "https://pe.er/stat-480.mp4",
1499 "mediaType" => "video/mp4",
1500 "type" => "Link"
1501 }
1502 ]
1503 },
1504 %{
1505 "mediaType" => "video/mp4",
1506 "type" => "Document",
1507 "url" => [
1508 %{
1509 "href" => "https://pe.er/stat-480.mp4",
1510 "mediaType" => "video/mp4",
1511 "type" => "Link"
1512 }
1513 ]
1514 }
1515 ]
1516 }
1517 end
1518 end
1519
1520 describe "fix_emoji/1" do
1521 test "returns not modified object when object not contains tags" do
1522 data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json"))
1523 assert Transmogrifier.fix_emoji(data) == data
1524 end
1525
1526 test "returns object with emoji when object contains list tags" do
1527 assert Transmogrifier.fix_emoji(%{
1528 "tag" => [
1529 %{"type" => "Emoji", "name" => ":bib:", "icon" => %{"url" => "/test"}},
1530 %{"type" => "Hashtag"}
1531 ]
1532 }) == %{
1533 "emoji" => %{"bib" => "/test"},
1534 "tag" => [
1535 %{"icon" => %{"url" => "/test"}, "name" => ":bib:", "type" => "Emoji"},
1536 %{"type" => "Hashtag"}
1537 ]
1538 }
1539 end
1540
1541 test "returns object with emoji when object contains map tag" do
1542 assert Transmogrifier.fix_emoji(%{
1543 "tag" => %{"type" => "Emoji", "name" => ":bib:", "icon" => %{"url" => "/test"}}
1544 }) == %{
1545 "emoji" => %{"bib" => "/test"},
1546 "tag" => %{"icon" => %{"url" => "/test"}, "name" => ":bib:", "type" => "Emoji"}
1547 }
1548 end
1549 end
1550
1551 describe "set_replies/1" do
1552 setup do: clear_config([:activitypub, :note_replies_output_limit], 2)
1553
1554 test "returns unmodified object if activity doesn't have self-replies" do
1555 data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json"))
1556 assert Transmogrifier.set_replies(data) == data
1557 end
1558
1559 test "sets `replies` collection with a limited number of self-replies" do
1560 [user, another_user] = insert_list(2, :user)
1561
1562 {:ok, %{id: id1} = activity} = CommonAPI.post(user, %{status: "1"})
1563
1564 {:ok, %{id: id2} = self_reply1} =
1565 CommonAPI.post(user, %{status: "self-reply 1", in_reply_to_status_id: id1})
1566
1567 {:ok, self_reply2} =
1568 CommonAPI.post(user, %{status: "self-reply 2", in_reply_to_status_id: id1})
1569
1570 # Assuming to _not_ be present in `replies` due to :note_replies_output_limit is set to 2
1571 {:ok, _} = CommonAPI.post(user, %{status: "self-reply 3", in_reply_to_status_id: id1})
1572
1573 {:ok, _} =
1574 CommonAPI.post(user, %{
1575 status: "self-reply to self-reply",
1576 in_reply_to_status_id: id2
1577 })
1578
1579 {:ok, _} =
1580 CommonAPI.post(another_user, %{
1581 status: "another user's reply",
1582 in_reply_to_status_id: id1
1583 })
1584
1585 object = Object.normalize(activity)
1586 replies_uris = Enum.map([self_reply1, self_reply2], fn a -> a.object.data["id"] end)
1587
1588 assert %{"type" => "Collection", "items" => ^replies_uris} =
1589 Transmogrifier.set_replies(object.data)["replies"]
1590 end
1591 end
1592
1593 test "take_emoji_tags/1" do
1594 user = insert(:user, %{emoji: %{"firefox" => "https://example.org/firefox.png"}})
1595
1596 assert Transmogrifier.take_emoji_tags(user) == [
1597 %{
1598 "icon" => %{"type" => "Image", "url" => "https://example.org/firefox.png"},
1599 "id" => "https://example.org/firefox.png",
1600 "name" => ":firefox:",
1601 "type" => "Emoji",
1602 "updated" => "1970-01-01T00:00:00Z"
1603 }
1604 ]
1605 end
1606 end