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