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