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