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