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