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