Merge branch 'issue/1936' 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.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 "skip converting the content when it is nil" do
388 object_id = "https://peertube.social/videos/watch/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe"
389
390 {:ok, object} = Fetcher.fetch_and_contain_remote_object_from_id(object_id)
391
392 result =
393 Pleroma.Web.ActivityPub.Transmogrifier.fix_object(Map.merge(object, %{"content" => nil}))
394
395 assert result["content"] == nil
396 end
397
398 test "it converts content of object to html" do
399 object_id = "https://peertube.social/videos/watch/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe"
400
401 {:ok, %{"content" => content_markdown}} =
402 Fetcher.fetch_and_contain_remote_object_from_id(object_id)
403
404 {:ok, %Pleroma.Object{data: %{"content" => content}} = object} =
405 Fetcher.fetch_object_from_id(object_id)
406
407 assert content_markdown ==
408 "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..."
409
410 assert content ==
411 "<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>"
412
413 assert object.data["mediaType"] == "text/html"
414 end
415
416 test "it remaps video URLs as attachments if necessary" do
417 {:ok, object} =
418 Fetcher.fetch_object_from_id(
419 "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
420 )
421
422 assert object.data["url"] ==
423 "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
424
425 assert object.data["attachment"] == [
426 %{
427 "type" => "Link",
428 "mediaType" => "video/mp4",
429 "url" => [
430 %{
431 "href" =>
432 "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
433 "mediaType" => "video/mp4",
434 "type" => "Link"
435 }
436 ]
437 }
438 ]
439
440 {:ok, object} =
441 Fetcher.fetch_object_from_id(
442 "https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206"
443 )
444
445 assert object.data["attachment"] == [
446 %{
447 "type" => "Link",
448 "mediaType" => "video/mp4",
449 "url" => [
450 %{
451 "href" =>
452 "https://framatube.org/static/webseed/6050732a-8a7a-43d4-a6cd-809525a1d206-1080.mp4",
453 "mediaType" => "video/mp4",
454 "type" => "Link"
455 }
456 ]
457 }
458 ]
459
460 assert object.data["url"] ==
461 "https://framatube.org/videos/watch/6050732a-8a7a-43d4-a6cd-809525a1d206"
462 end
463
464 test "it accepts Flag activities" do
465 user = insert(:user)
466 other_user = insert(:user)
467
468 {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
469 object = Object.normalize(activity)
470
471 note_obj = %{
472 "type" => "Note",
473 "id" => activity.data["id"],
474 "content" => "test post",
475 "published" => object.data["published"],
476 "actor" => AccountView.render("show.json", %{user: user, skip_visibility_check: true})
477 }
478
479 message = %{
480 "@context" => "https://www.w3.org/ns/activitystreams",
481 "cc" => [user.ap_id],
482 "object" => [user.ap_id, activity.data["id"]],
483 "type" => "Flag",
484 "content" => "blocked AND reported!!!",
485 "actor" => other_user.ap_id
486 }
487
488 assert {:ok, activity} = Transmogrifier.handle_incoming(message)
489
490 assert activity.data["object"] == [user.ap_id, note_obj]
491 assert activity.data["content"] == "blocked AND reported!!!"
492 assert activity.data["actor"] == other_user.ap_id
493 assert activity.data["cc"] == [user.ap_id]
494 end
495
496 test "it correctly processes messages with non-array to field" do
497 user = insert(:user)
498
499 message = %{
500 "@context" => "https://www.w3.org/ns/activitystreams",
501 "to" => "https://www.w3.org/ns/activitystreams#Public",
502 "type" => "Create",
503 "object" => %{
504 "content" => "blah blah blah",
505 "type" => "Note",
506 "attributedTo" => user.ap_id,
507 "inReplyTo" => nil
508 },
509 "actor" => user.ap_id
510 }
511
512 assert {:ok, activity} = Transmogrifier.handle_incoming(message)
513
514 assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["to"]
515 end
516
517 test "it correctly processes messages with non-array cc field" do
518 user = insert(:user)
519
520 message = %{
521 "@context" => "https://www.w3.org/ns/activitystreams",
522 "to" => user.follower_address,
523 "cc" => "https://www.w3.org/ns/activitystreams#Public",
524 "type" => "Create",
525 "object" => %{
526 "content" => "blah blah blah",
527 "type" => "Note",
528 "attributedTo" => user.ap_id,
529 "inReplyTo" => nil
530 },
531 "actor" => user.ap_id
532 }
533
534 assert {:ok, activity} = Transmogrifier.handle_incoming(message)
535
536 assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"]
537 assert [user.follower_address] == activity.data["to"]
538 end
539
540 test "it correctly processes messages with weirdness in address fields" do
541 user = insert(:user)
542
543 message = %{
544 "@context" => "https://www.w3.org/ns/activitystreams",
545 "to" => [nil, user.follower_address],
546 "cc" => ["https://www.w3.org/ns/activitystreams#Public", ["¿"]],
547 "type" => "Create",
548 "object" => %{
549 "content" => "…",
550 "type" => "Note",
551 "attributedTo" => user.ap_id,
552 "inReplyTo" => nil
553 },
554 "actor" => user.ap_id
555 }
556
557 assert {:ok, activity} = Transmogrifier.handle_incoming(message)
558
559 assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"]
560 assert [user.follower_address] == activity.data["to"]
561 end
562
563 test "it accepts Move activities" do
564 old_user = insert(:user)
565 new_user = insert(:user)
566
567 message = %{
568 "@context" => "https://www.w3.org/ns/activitystreams",
569 "type" => "Move",
570 "actor" => old_user.ap_id,
571 "object" => old_user.ap_id,
572 "target" => new_user.ap_id
573 }
574
575 assert :error = Transmogrifier.handle_incoming(message)
576
577 {:ok, _new_user} = User.update_and_set_cache(new_user, %{also_known_as: [old_user.ap_id]})
578
579 assert {:ok, %Activity{} = activity} = Transmogrifier.handle_incoming(message)
580 assert activity.actor == old_user.ap_id
581 assert activity.data["actor"] == old_user.ap_id
582 assert activity.data["object"] == old_user.ap_id
583 assert activity.data["target"] == new_user.ap_id
584 assert activity.data["type"] == "Move"
585 end
586 end
587
588 describe "`handle_incoming/2`, Mastodon format `replies` handling" do
589 setup do: clear_config([:activitypub, :note_replies_output_limit], 5)
590 setup do: clear_config([:instance, :federation_incoming_replies_max_depth])
591
592 setup do
593 data =
594 "test/fixtures/mastodon-post-activity.json"
595 |> File.read!()
596 |> Poison.decode!()
597
598 items = get_in(data, ["object", "replies", "first", "items"])
599 assert length(items) > 0
600
601 %{data: data, items: items}
602 end
603
604 test "schedules background fetching of `replies` items if max thread depth limit allows", %{
605 data: data,
606 items: items
607 } do
608 Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 10)
609
610 {:ok, _activity} = Transmogrifier.handle_incoming(data)
611
612 for id <- items do
613 job_args = %{"op" => "fetch_remote", "id" => id, "depth" => 1}
614 assert_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker, args: job_args)
615 end
616 end
617
618 test "does NOT schedule background fetching of `replies` beyond max thread depth limit allows",
619 %{data: data} do
620 Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0)
621
622 {:ok, _activity} = Transmogrifier.handle_incoming(data)
623
624 assert all_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker) == []
625 end
626 end
627
628 describe "`handle_incoming/2`, Pleroma format `replies` handling" do
629 setup do: clear_config([:activitypub, :note_replies_output_limit], 5)
630 setup do: clear_config([:instance, :federation_incoming_replies_max_depth])
631
632 setup do
633 user = insert(:user)
634
635 {:ok, activity} = CommonAPI.post(user, %{status: "post1"})
636
637 {:ok, reply1} =
638 CommonAPI.post(user, %{status: "reply1", in_reply_to_status_id: activity.id})
639
640 {:ok, reply2} =
641 CommonAPI.post(user, %{status: "reply2", in_reply_to_status_id: activity.id})
642
643 replies_uris = Enum.map([reply1, reply2], fn a -> a.object.data["id"] end)
644
645 {:ok, federation_output} = Transmogrifier.prepare_outgoing(activity.data)
646
647 Repo.delete(activity.object)
648 Repo.delete(activity)
649
650 %{federation_output: federation_output, replies_uris: replies_uris}
651 end
652
653 test "schedules background fetching of `replies` items if max thread depth limit allows", %{
654 federation_output: federation_output,
655 replies_uris: replies_uris
656 } do
657 Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 1)
658
659 {:ok, _activity} = Transmogrifier.handle_incoming(federation_output)
660
661 for id <- replies_uris do
662 job_args = %{"op" => "fetch_remote", "id" => id, "depth" => 1}
663 assert_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker, args: job_args)
664 end
665 end
666
667 test "does NOT schedule background fetching of `replies` beyond max thread depth limit allows",
668 %{federation_output: federation_output} do
669 Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0)
670
671 {:ok, _activity} = Transmogrifier.handle_incoming(federation_output)
672
673 assert all_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker) == []
674 end
675 end
676
677 describe "prepare outgoing" do
678 test "it inlines private announced objects" do
679 user = insert(:user)
680
681 {:ok, activity} = CommonAPI.post(user, %{status: "hey", visibility: "private"})
682
683 {:ok, announce_activity} = CommonAPI.repeat(activity.id, user)
684
685 {:ok, modified} = Transmogrifier.prepare_outgoing(announce_activity.data)
686
687 assert modified["object"]["content"] == "hey"
688 assert modified["object"]["actor"] == modified["object"]["attributedTo"]
689 end
690
691 test "it turns mentions into tags" do
692 user = insert(:user)
693 other_user = insert(:user)
694
695 {:ok, activity} =
696 CommonAPI.post(user, %{status: "hey, @#{other_user.nickname}, how are ya? #2hu"})
697
698 with_mock Pleroma.Notification,
699 get_notified_from_activity: fn _, _ -> [] end do
700 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
701
702 object = modified["object"]
703
704 expected_mention = %{
705 "href" => other_user.ap_id,
706 "name" => "@#{other_user.nickname}",
707 "type" => "Mention"
708 }
709
710 expected_tag = %{
711 "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
712 "type" => "Hashtag",
713 "name" => "#2hu"
714 }
715
716 refute called(Pleroma.Notification.get_notified_from_activity(:_, :_))
717 assert Enum.member?(object["tag"], expected_tag)
718 assert Enum.member?(object["tag"], expected_mention)
719 end
720 end
721
722 test "it adds the sensitive property" do
723 user = insert(:user)
724
725 {:ok, activity} = CommonAPI.post(user, %{status: "#nsfw hey"})
726 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
727
728 assert modified["object"]["sensitive"]
729 end
730
731 test "it adds the json-ld context and the conversation property" do
732 user = insert(:user)
733
734 {:ok, activity} = CommonAPI.post(user, %{status: "hey"})
735 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
736
737 assert modified["@context"] ==
738 Pleroma.Web.ActivityPub.Utils.make_json_ld_header()["@context"]
739
740 assert modified["object"]["conversation"] == modified["context"]
741 end
742
743 test "it sets the 'attributedTo' property to the actor of the object if it doesn't have one" do
744 user = insert(:user)
745
746 {:ok, activity} = CommonAPI.post(user, %{status: "hey"})
747 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
748
749 assert modified["object"]["actor"] == modified["object"]["attributedTo"]
750 end
751
752 test "it strips internal hashtag data" do
753 user = insert(:user)
754
755 {:ok, activity} = CommonAPI.post(user, %{status: "#2hu"})
756
757 expected_tag = %{
758 "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
759 "type" => "Hashtag",
760 "name" => "#2hu"
761 }
762
763 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
764
765 assert modified["object"]["tag"] == [expected_tag]
766 end
767
768 test "it strips internal fields" do
769 user = insert(:user)
770
771 {:ok, activity} = CommonAPI.post(user, %{status: "#2hu :firefox:"})
772
773 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
774
775 assert length(modified["object"]["tag"]) == 2
776
777 assert is_nil(modified["object"]["emoji"])
778 assert is_nil(modified["object"]["like_count"])
779 assert is_nil(modified["object"]["announcements"])
780 assert is_nil(modified["object"]["announcement_count"])
781 assert is_nil(modified["object"]["context_id"])
782 end
783
784 test "it strips internal fields of article" do
785 activity = insert(:article_activity)
786
787 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
788
789 assert length(modified["object"]["tag"]) == 2
790
791 assert is_nil(modified["object"]["emoji"])
792 assert is_nil(modified["object"]["like_count"])
793 assert is_nil(modified["object"]["announcements"])
794 assert is_nil(modified["object"]["announcement_count"])
795 assert is_nil(modified["object"]["context_id"])
796 assert is_nil(modified["object"]["likes"])
797 end
798
799 test "the directMessage flag is present" do
800 user = insert(:user)
801 other_user = insert(:user)
802
803 {:ok, activity} = CommonAPI.post(user, %{status: "2hu :moominmamma:"})
804
805 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
806
807 assert modified["directMessage"] == false
808
809 {:ok, activity} = CommonAPI.post(user, %{status: "@#{other_user.nickname} :moominmamma:"})
810
811 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
812
813 assert modified["directMessage"] == false
814
815 {:ok, activity} =
816 CommonAPI.post(user, %{
817 status: "@#{other_user.nickname} :moominmamma:",
818 visibility: "direct"
819 })
820
821 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
822
823 assert modified["directMessage"] == true
824 end
825
826 test "it strips BCC field" do
827 user = insert(:user)
828 {:ok, list} = Pleroma.List.create("foo", user)
829
830 {:ok, activity} = CommonAPI.post(user, %{status: "foobar", visibility: "list:#{list.id}"})
831
832 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
833
834 assert is_nil(modified["bcc"])
835 end
836
837 test "it can handle Listen activities" do
838 listen_activity = insert(:listen)
839
840 {:ok, modified} = Transmogrifier.prepare_outgoing(listen_activity.data)
841
842 assert modified["type"] == "Listen"
843
844 user = insert(:user)
845
846 {:ok, activity} = CommonAPI.listen(user, %{"title" => "lain radio episode 1"})
847
848 {:ok, _modified} = Transmogrifier.prepare_outgoing(activity.data)
849 end
850 end
851
852 describe "user upgrade" do
853 test "it upgrades a user to activitypub" do
854 user =
855 insert(:user, %{
856 nickname: "rye@niu.moe",
857 local: false,
858 ap_id: "https://niu.moe/users/rye",
859 follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"})
860 })
861
862 user_two = insert(:user)
863 Pleroma.FollowingRelationship.follow(user_two, user, :follow_accept)
864
865 {:ok, activity} = CommonAPI.post(user, %{status: "test"})
866 {:ok, unrelated_activity} = CommonAPI.post(user_two, %{status: "test"})
867 assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients
868
869 user = User.get_cached_by_id(user.id)
870 assert user.note_count == 1
871
872 {:ok, user} = Transmogrifier.upgrade_user_from_ap_id("https://niu.moe/users/rye")
873 ObanHelpers.perform_all()
874
875 assert user.ap_enabled
876 assert user.note_count == 1
877 assert user.follower_address == "https://niu.moe/users/rye/followers"
878 assert user.following_address == "https://niu.moe/users/rye/following"
879
880 user = User.get_cached_by_id(user.id)
881 assert user.note_count == 1
882
883 activity = Activity.get_by_id(activity.id)
884 assert user.follower_address in activity.recipients
885
886 assert %{
887 "url" => [
888 %{
889 "href" =>
890 "https://cdn.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
891 }
892 ]
893 } = user.avatar
894
895 assert %{
896 "url" => [
897 %{
898 "href" =>
899 "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
900 }
901 ]
902 } = user.banner
903
904 refute "..." in activity.recipients
905
906 unrelated_activity = Activity.get_by_id(unrelated_activity.id)
907 refute user.follower_address in unrelated_activity.recipients
908
909 user_two = User.get_cached_by_id(user_two.id)
910 assert User.following?(user_two, user)
911 refute "..." in User.following(user_two)
912 end
913 end
914
915 describe "actor rewriting" do
916 test "it fixes the actor URL property to be a proper URI" do
917 data = %{
918 "url" => %{"href" => "http://example.com"}
919 }
920
921 rewritten = Transmogrifier.maybe_fix_user_object(data)
922 assert rewritten["url"] == "http://example.com"
923 end
924 end
925
926 describe "actor origin containment" do
927 test "it rejects activities which reference objects with bogus origins" do
928 data = %{
929 "@context" => "https://www.w3.org/ns/activitystreams",
930 "id" => "http://mastodon.example.org/users/admin/activities/1234",
931 "actor" => "http://mastodon.example.org/users/admin",
932 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
933 "object" => "https://info.pleroma.site/activity.json",
934 "type" => "Announce"
935 }
936
937 assert capture_log(fn ->
938 {:error, _} = Transmogrifier.handle_incoming(data)
939 end) =~ "Object containment failed"
940 end
941
942 test "it rejects activities which reference objects that have an incorrect attribution (variant 1)" do
943 data = %{
944 "@context" => "https://www.w3.org/ns/activitystreams",
945 "id" => "http://mastodon.example.org/users/admin/activities/1234",
946 "actor" => "http://mastodon.example.org/users/admin",
947 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
948 "object" => "https://info.pleroma.site/activity2.json",
949 "type" => "Announce"
950 }
951
952 assert capture_log(fn ->
953 {:error, _} = Transmogrifier.handle_incoming(data)
954 end) =~ "Object containment failed"
955 end
956
957 test "it rejects activities which reference objects that have an incorrect attribution (variant 2)" do
958 data = %{
959 "@context" => "https://www.w3.org/ns/activitystreams",
960 "id" => "http://mastodon.example.org/users/admin/activities/1234",
961 "actor" => "http://mastodon.example.org/users/admin",
962 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
963 "object" => "https://info.pleroma.site/activity3.json",
964 "type" => "Announce"
965 }
966
967 assert capture_log(fn ->
968 {:error, _} = Transmogrifier.handle_incoming(data)
969 end) =~ "Object containment failed"
970 end
971 end
972
973 describe "reserialization" do
974 test "successfully reserializes a message with inReplyTo == nil" do
975 user = insert(:user)
976
977 message = %{
978 "@context" => "https://www.w3.org/ns/activitystreams",
979 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
980 "cc" => [],
981 "type" => "Create",
982 "object" => %{
983 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
984 "cc" => [],
985 "type" => "Note",
986 "content" => "Hi",
987 "inReplyTo" => nil,
988 "attributedTo" => user.ap_id
989 },
990 "actor" => user.ap_id
991 }
992
993 {:ok, activity} = Transmogrifier.handle_incoming(message)
994
995 {:ok, _} = Transmogrifier.prepare_outgoing(activity.data)
996 end
997
998 test "successfully reserializes a message with AS2 objects in IR" do
999 user = insert(:user)
1000
1001 message = %{
1002 "@context" => "https://www.w3.org/ns/activitystreams",
1003 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1004 "cc" => [],
1005 "type" => "Create",
1006 "object" => %{
1007 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1008 "cc" => [],
1009 "type" => "Note",
1010 "content" => "Hi",
1011 "inReplyTo" => nil,
1012 "attributedTo" => user.ap_id,
1013 "tag" => [
1014 %{"name" => "#2hu", "href" => "http://example.com/2hu", "type" => "Hashtag"},
1015 %{"name" => "Bob", "href" => "http://example.com/bob", "type" => "Mention"}
1016 ]
1017 },
1018 "actor" => user.ap_id
1019 }
1020
1021 {:ok, activity} = Transmogrifier.handle_incoming(message)
1022
1023 {:ok, _} = Transmogrifier.prepare_outgoing(activity.data)
1024 end
1025 end
1026
1027 describe "fix_explicit_addressing" do
1028 setup do
1029 user = insert(:user)
1030 [user: user]
1031 end
1032
1033 test "moves non-explicitly mentioned actors to cc", %{user: user} do
1034 explicitly_mentioned_actors = [
1035 "https://pleroma.gold/users/user1",
1036 "https://pleroma.gold/user2"
1037 ]
1038
1039 object = %{
1040 "actor" => user.ap_id,
1041 "to" => explicitly_mentioned_actors ++ ["https://social.beepboop.ga/users/dirb"],
1042 "cc" => [],
1043 "tag" =>
1044 Enum.map(explicitly_mentioned_actors, fn href ->
1045 %{"type" => "Mention", "href" => href}
1046 end)
1047 }
1048
1049 fixed_object = Transmogrifier.fix_explicit_addressing(object)
1050 assert Enum.all?(explicitly_mentioned_actors, &(&1 in fixed_object["to"]))
1051 refute "https://social.beepboop.ga/users/dirb" in fixed_object["to"]
1052 assert "https://social.beepboop.ga/users/dirb" in fixed_object["cc"]
1053 end
1054
1055 test "does not move actor's follower collection to cc", %{user: user} do
1056 object = %{
1057 "actor" => user.ap_id,
1058 "to" => [user.follower_address],
1059 "cc" => []
1060 }
1061
1062 fixed_object = Transmogrifier.fix_explicit_addressing(object)
1063 assert user.follower_address in fixed_object["to"]
1064 refute user.follower_address in fixed_object["cc"]
1065 end
1066
1067 test "removes recipient's follower collection from cc", %{user: user} do
1068 recipient = insert(:user)
1069
1070 object = %{
1071 "actor" => user.ap_id,
1072 "to" => [recipient.ap_id, "https://www.w3.org/ns/activitystreams#Public"],
1073 "cc" => [user.follower_address, recipient.follower_address]
1074 }
1075
1076 fixed_object = Transmogrifier.fix_explicit_addressing(object)
1077
1078 assert user.follower_address in fixed_object["cc"]
1079 refute recipient.follower_address in fixed_object["cc"]
1080 refute recipient.follower_address in fixed_object["to"]
1081 end
1082 end
1083
1084 describe "fix_summary/1" do
1085 test "returns fixed object" do
1086 assert Transmogrifier.fix_summary(%{"summary" => nil}) == %{"summary" => ""}
1087 assert Transmogrifier.fix_summary(%{"summary" => "ok"}) == %{"summary" => "ok"}
1088 assert Transmogrifier.fix_summary(%{}) == %{"summary" => ""}
1089 end
1090 end
1091
1092 describe "fix_in_reply_to/2" do
1093 setup do: clear_config([:instance, :federation_incoming_replies_max_depth])
1094
1095 setup do
1096 data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json"))
1097 [data: data]
1098 end
1099
1100 test "returns not modified object when hasn't containts inReplyTo field", %{data: data} do
1101 assert Transmogrifier.fix_in_reply_to(data) == data
1102 end
1103
1104 test "returns object with inReplyToAtomUri when denied incoming reply", %{data: data} do
1105 Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0)
1106
1107 object_with_reply =
1108 Map.put(data["object"], "inReplyTo", "https://shitposter.club/notice/2827873")
1109
1110 modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
1111 assert modified_object["inReplyTo"] == "https://shitposter.club/notice/2827873"
1112 assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
1113
1114 object_with_reply =
1115 Map.put(data["object"], "inReplyTo", %{"id" => "https://shitposter.club/notice/2827873"})
1116
1117 modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
1118 assert modified_object["inReplyTo"] == %{"id" => "https://shitposter.club/notice/2827873"}
1119 assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
1120
1121 object_with_reply =
1122 Map.put(data["object"], "inReplyTo", ["https://shitposter.club/notice/2827873"])
1123
1124 modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
1125 assert modified_object["inReplyTo"] == ["https://shitposter.club/notice/2827873"]
1126 assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
1127
1128 object_with_reply = Map.put(data["object"], "inReplyTo", [])
1129 modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
1130 assert modified_object["inReplyTo"] == []
1131 assert modified_object["inReplyToAtomUri"] == ""
1132 end
1133
1134 @tag capture_log: true
1135 test "returns modified object when allowed incoming reply", %{data: data} do
1136 object_with_reply =
1137 Map.put(
1138 data["object"],
1139 "inReplyTo",
1140 "https://shitposter.club/notice/2827873"
1141 )
1142
1143 Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 5)
1144 modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
1145
1146 assert modified_object["inReplyTo"] ==
1147 "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
1148
1149 assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
1150
1151 assert modified_object["context"] ==
1152 "tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26"
1153 end
1154 end
1155
1156 describe "fix_url/1" do
1157 test "fixes data for object when url is map" do
1158 object = %{
1159 "url" => %{
1160 "type" => "Link",
1161 "mimeType" => "video/mp4",
1162 "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4"
1163 }
1164 }
1165
1166 assert Transmogrifier.fix_url(object) == %{
1167 "url" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4"
1168 }
1169 end
1170
1171 test "fixes data for video object" do
1172 object = %{
1173 "type" => "Video",
1174 "url" => [
1175 %{
1176 "type" => "Link",
1177 "mimeType" => "video/mp4",
1178 "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4"
1179 },
1180 %{
1181 "type" => "Link",
1182 "mimeType" => "video/mp4",
1183 "href" => "https://peertube46fb-ad81-2d4c2d1630e3-240.mp4"
1184 },
1185 %{
1186 "type" => "Link",
1187 "mimeType" => "text/html",
1188 "href" => "https://peertube.-2d4c2d1630e3"
1189 },
1190 %{
1191 "type" => "Link",
1192 "mimeType" => "text/html",
1193 "href" => "https://peertube.-2d4c2d16377-42"
1194 }
1195 ]
1196 }
1197
1198 assert Transmogrifier.fix_url(object) == %{
1199 "attachment" => [
1200 %{
1201 "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4",
1202 "mimeType" => "video/mp4",
1203 "type" => "Link"
1204 }
1205 ],
1206 "type" => "Video",
1207 "url" => "https://peertube.-2d4c2d1630e3"
1208 }
1209 end
1210
1211 test "fixes url for not Video object" do
1212 object = %{
1213 "type" => "Text",
1214 "url" => [
1215 %{
1216 "type" => "Link",
1217 "mimeType" => "text/html",
1218 "href" => "https://peertube.-2d4c2d1630e3"
1219 },
1220 %{
1221 "type" => "Link",
1222 "mimeType" => "text/html",
1223 "href" => "https://peertube.-2d4c2d16377-42"
1224 }
1225 ]
1226 }
1227
1228 assert Transmogrifier.fix_url(object) == %{
1229 "type" => "Text",
1230 "url" => "https://peertube.-2d4c2d1630e3"
1231 }
1232
1233 assert Transmogrifier.fix_url(%{"type" => "Text", "url" => []}) == %{
1234 "type" => "Text",
1235 "url" => ""
1236 }
1237 end
1238
1239 test "retunrs not modified object" do
1240 assert Transmogrifier.fix_url(%{"type" => "Text"}) == %{"type" => "Text"}
1241 end
1242 end
1243
1244 describe "get_obj_helper/2" do
1245 test "returns nil when cannot normalize object" do
1246 assert capture_log(fn ->
1247 refute Transmogrifier.get_obj_helper("test-obj-id")
1248 end) =~ "Unsupported URI scheme"
1249 end
1250
1251 @tag capture_log: true
1252 test "returns {:ok, %Object{}} for success case" do
1253 assert {:ok, %Object{}} =
1254 Transmogrifier.get_obj_helper("https://shitposter.club/notice/2827873")
1255 end
1256 end
1257
1258 describe "fix_attachments/1" do
1259 test "returns not modified object" do
1260 data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json"))
1261 assert Transmogrifier.fix_attachments(data) == data
1262 end
1263
1264 test "returns modified object when attachment is map" do
1265 assert Transmogrifier.fix_attachments(%{
1266 "attachment" => %{
1267 "mediaType" => "video/mp4",
1268 "url" => "https://peertube.moe/stat-480.mp4"
1269 }
1270 }) == %{
1271 "attachment" => [
1272 %{
1273 "mediaType" => "video/mp4",
1274 "type" => "Document",
1275 "url" => [
1276 %{
1277 "href" => "https://peertube.moe/stat-480.mp4",
1278 "mediaType" => "video/mp4",
1279 "type" => "Link"
1280 }
1281 ]
1282 }
1283 ]
1284 }
1285 end
1286
1287 test "returns modified object when attachment is list" do
1288 assert Transmogrifier.fix_attachments(%{
1289 "attachment" => [
1290 %{"mediaType" => "video/mp4", "url" => "https://pe.er/stat-480.mp4"},
1291 %{"mimeType" => "video/mp4", "href" => "https://pe.er/stat-480.mp4"}
1292 ]
1293 }) == %{
1294 "attachment" => [
1295 %{
1296 "mediaType" => "video/mp4",
1297 "type" => "Document",
1298 "url" => [
1299 %{
1300 "href" => "https://pe.er/stat-480.mp4",
1301 "mediaType" => "video/mp4",
1302 "type" => "Link"
1303 }
1304 ]
1305 },
1306 %{
1307 "mediaType" => "video/mp4",
1308 "type" => "Document",
1309 "url" => [
1310 %{
1311 "href" => "https://pe.er/stat-480.mp4",
1312 "mediaType" => "video/mp4",
1313 "type" => "Link"
1314 }
1315 ]
1316 }
1317 ]
1318 }
1319 end
1320 end
1321
1322 describe "fix_emoji/1" do
1323 test "returns not modified object when object not contains tags" do
1324 data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json"))
1325 assert Transmogrifier.fix_emoji(data) == data
1326 end
1327
1328 test "returns object with emoji when object contains list tags" do
1329 assert Transmogrifier.fix_emoji(%{
1330 "tag" => [
1331 %{"type" => "Emoji", "name" => ":bib:", "icon" => %{"url" => "/test"}},
1332 %{"type" => "Hashtag"}
1333 ]
1334 }) == %{
1335 "emoji" => %{"bib" => "/test"},
1336 "tag" => [
1337 %{"icon" => %{"url" => "/test"}, "name" => ":bib:", "type" => "Emoji"},
1338 %{"type" => "Hashtag"}
1339 ]
1340 }
1341 end
1342
1343 test "returns object with emoji when object contains map tag" do
1344 assert Transmogrifier.fix_emoji(%{
1345 "tag" => %{"type" => "Emoji", "name" => ":bib:", "icon" => %{"url" => "/test"}}
1346 }) == %{
1347 "emoji" => %{"bib" => "/test"},
1348 "tag" => %{"icon" => %{"url" => "/test"}, "name" => ":bib:", "type" => "Emoji"}
1349 }
1350 end
1351 end
1352
1353 describe "set_replies/1" do
1354 setup do: clear_config([:activitypub, :note_replies_output_limit], 2)
1355
1356 test "returns unmodified object if activity doesn't have self-replies" do
1357 data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json"))
1358 assert Transmogrifier.set_replies(data) == data
1359 end
1360
1361 test "sets `replies` collection with a limited number of self-replies" do
1362 [user, another_user] = insert_list(2, :user)
1363
1364 {:ok, %{id: id1} = activity} = CommonAPI.post(user, %{status: "1"})
1365
1366 {:ok, %{id: id2} = self_reply1} =
1367 CommonAPI.post(user, %{status: "self-reply 1", in_reply_to_status_id: id1})
1368
1369 {:ok, self_reply2} =
1370 CommonAPI.post(user, %{status: "self-reply 2", in_reply_to_status_id: id1})
1371
1372 # Assuming to _not_ be present in `replies` due to :note_replies_output_limit is set to 2
1373 {:ok, _} = CommonAPI.post(user, %{status: "self-reply 3", in_reply_to_status_id: id1})
1374
1375 {:ok, _} =
1376 CommonAPI.post(user, %{
1377 status: "self-reply to self-reply",
1378 in_reply_to_status_id: id2
1379 })
1380
1381 {:ok, _} =
1382 CommonAPI.post(another_user, %{
1383 status: "another user's reply",
1384 in_reply_to_status_id: id1
1385 })
1386
1387 object = Object.normalize(activity)
1388 replies_uris = Enum.map([self_reply1, self_reply2], fn a -> a.object.data["id"] end)
1389
1390 assert %{"type" => "Collection", "items" => ^replies_uris} =
1391 Transmogrifier.set_replies(object.data)["replies"]
1392 end
1393 end
1394
1395 test "take_emoji_tags/1" do
1396 user = insert(:user, %{emoji: %{"firefox" => "https://example.org/firefox.png"}})
1397
1398 assert Transmogrifier.take_emoji_tags(user) == [
1399 %{
1400 "icon" => %{"type" => "Image", "url" => "https://example.org/firefox.png"},
1401 "id" => "https://example.org/firefox.png",
1402 "name" => ":firefox:",
1403 "type" => "Emoji",
1404 "updated" => "1970-01-01T00:00:00Z"
1405 }
1406 ]
1407 end
1408 end