Fix warning in TransmogrifierTest
[akkoma] / test / web / activity_pub / transmogrifier_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
6 use Pleroma.DataCase
7 alias Pleroma.Activity
8 alias Pleroma.Object
9 alias Pleroma.Object.Fetcher
10 alias Pleroma.Repo
11 alias Pleroma.Tests.ObanHelpers
12 alias Pleroma.User
13 alias Pleroma.Web.ActivityPub.ActivityPub
14 alias Pleroma.Web.ActivityPub.Transmogrifier
15 alias Pleroma.Web.CommonAPI
16 alias Pleroma.Web.OStatus
17 alias Pleroma.Web.Websub.WebsubClientSubscription
18
19 import Mock
20 import Pleroma.Factory
21 import ExUnit.CaptureLog
22
23 setup_all do
24 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
25 :ok
26 end
27
28 clear_config([:instance, :max_remote_account_fields])
29
30 describe "handle_incoming" do
31 test "it ignores an incoming notice if we already have it" do
32 activity = insert(:note_activity)
33
34 data =
35 File.read!("test/fixtures/mastodon-post-activity.json")
36 |> Poison.decode!()
37 |> Map.put("object", Object.normalize(activity).data)
38
39 {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
40
41 assert activity == returned_activity
42 end
43
44 test "it fetches replied-to activities if we don't have them" do
45 data =
46 File.read!("test/fixtures/mastodon-post-activity.json")
47 |> Poison.decode!()
48
49 object =
50 data["object"]
51 |> Map.put("inReplyTo", "https://shitposter.club/notice/2827873")
52
53 data = Map.put(data, "object", object)
54 {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
55 returned_object = Object.normalize(returned_activity, false)
56
57 assert activity =
58 Activity.get_create_by_object_ap_id(
59 "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
60 )
61
62 assert returned_object.data["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
63 end
64
65 test "it does not fetch replied-to activities beyond max_replies_depth" do
66 data =
67 File.read!("test/fixtures/mastodon-post-activity.json")
68 |> Poison.decode!()
69
70 object =
71 data["object"]
72 |> Map.put("inReplyTo", "https://shitposter.club/notice/2827873")
73
74 data = Map.put(data, "object", object)
75
76 with_mock Pleroma.Web.Federator,
77 allowed_incoming_reply_depth?: fn _ -> false end do
78 {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
79
80 returned_object = Object.normalize(returned_activity, false)
81
82 refute Activity.get_create_by_object_ap_id(
83 "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
84 )
85
86 assert returned_object.data["inReplyToAtomUri"] ==
87 "https://shitposter.club/notice/2827873"
88 end
89 end
90
91 test "it does not crash if the object in inReplyTo can't be fetched" do
92 data =
93 File.read!("test/fixtures/mastodon-post-activity.json")
94 |> Poison.decode!()
95
96 object =
97 data["object"]
98 |> Map.put("inReplyTo", "https://404.site/whatever")
99
100 data =
101 data
102 |> Map.put("object", object)
103
104 assert capture_log(fn ->
105 {:ok, _returned_activity} = Transmogrifier.handle_incoming(data)
106 end) =~ "[error] Couldn't fetch \"https://404.site/whatever\", error: nil"
107 end
108
109 test "it works for incoming notices" do
110 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
111
112 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
113
114 assert data["id"] ==
115 "http://mastodon.example.org/users/admin/statuses/99512778738411822/activity"
116
117 assert data["context"] ==
118 "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation"
119
120 assert data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
121
122 assert data["cc"] == [
123 "http://mastodon.example.org/users/admin/followers",
124 "http://localtesting.pleroma.lol/users/lain"
125 ]
126
127 assert data["actor"] == "http://mastodon.example.org/users/admin"
128
129 object_data = Object.normalize(data["object"]).data
130
131 assert object_data["id"] ==
132 "http://mastodon.example.org/users/admin/statuses/99512778738411822"
133
134 assert object_data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
135
136 assert object_data["cc"] == [
137 "http://mastodon.example.org/users/admin/followers",
138 "http://localtesting.pleroma.lol/users/lain"
139 ]
140
141 assert object_data["actor"] == "http://mastodon.example.org/users/admin"
142 assert object_data["attributedTo"] == "http://mastodon.example.org/users/admin"
143
144 assert object_data["context"] ==
145 "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation"
146
147 assert object_data["sensitive"] == true
148
149 user = User.get_cached_by_ap_id(object_data["actor"])
150
151 assert user.info.note_count == 1
152 end
153
154 test "it works for incoming notices with hashtags" do
155 data = File.read!("test/fixtures/mastodon-post-activity-hashtag.json") |> Poison.decode!()
156
157 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
158 object = Object.normalize(data["object"])
159
160 assert Enum.at(object.data["tag"], 2) == "moo"
161 end
162
163 test "it works for incoming questions" do
164 data = File.read!("test/fixtures/mastodon-question-activity.json") |> Poison.decode!()
165
166 {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data)
167
168 object = Object.normalize(activity)
169
170 assert Enum.all?(object.data["oneOf"], fn choice ->
171 choice["name"] in [
172 "Dunno",
173 "Everyone knows that!",
174 "25 char limit is dumb",
175 "I can't even fit a funny"
176 ]
177 end)
178 end
179
180 test "it works for incoming listens" do
181 data = %{
182 "@context" => "https://www.w3.org/ns/activitystreams",
183 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
184 "cc" => [],
185 "type" => "Listen",
186 "id" => "http://mastodon.example.org/users/admin/listens/1234/activity",
187 "actor" => "http://mastodon.example.org/users/admin",
188 "object" => %{
189 "type" => "Audio",
190 "id" => "http://mastodon.example.org/users/admin/listens/1234",
191 "attributedTo" => "http://mastodon.example.org/users/admin",
192 "title" => "lain radio episode 1",
193 "artist" => "lain",
194 "album" => "lain radio",
195 "length" => 180_000
196 }
197 }
198
199 {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data)
200
201 object = Object.normalize(activity)
202
203 assert object.data["title"] == "lain radio episode 1"
204 assert object.data["artist"] == "lain"
205 assert object.data["album"] == "lain radio"
206 assert object.data["length"] == 180_000
207 end
208
209 test "it rewrites Note votes to Answers and increments vote counters on question activities" do
210 user = insert(:user)
211
212 {:ok, activity} =
213 CommonAPI.post(user, %{
214 "status" => "suya...",
215 "poll" => %{"options" => ["suya", "suya.", "suya.."], "expires_in" => 10}
216 })
217
218 object = Object.normalize(activity)
219
220 data =
221 File.read!("test/fixtures/mastodon-vote.json")
222 |> Poison.decode!()
223 |> Kernel.put_in(["to"], user.ap_id)
224 |> Kernel.put_in(["object", "inReplyTo"], object.data["id"])
225 |> Kernel.put_in(["object", "to"], user.ap_id)
226
227 {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data)
228 answer_object = Object.normalize(activity)
229 assert answer_object.data["type"] == "Answer"
230 object = Object.get_by_ap_id(object.data["id"])
231
232 assert Enum.any?(
233 object.data["oneOf"],
234 fn
235 %{"name" => "suya..", "replies" => %{"totalItems" => 1}} -> true
236 _ -> false
237 end
238 )
239 end
240
241 test "it works for incoming notices with contentMap" do
242 data =
243 File.read!("test/fixtures/mastodon-post-activity-contentmap.json") |> Poison.decode!()
244
245 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
246 object = Object.normalize(data["object"])
247
248 assert object.data["content"] ==
249 "<p><span class=\"h-card\"><a href=\"http://localtesting.pleroma.lol/users/lain\" class=\"u-url mention\">@<span>lain</span></a></span></p>"
250 end
251
252 test "it works for incoming notices with to/cc not being an array (kroeg)" do
253 data = File.read!("test/fixtures/kroeg-post-activity.json") |> Poison.decode!()
254
255 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
256 object = Object.normalize(data["object"])
257
258 assert object.data["content"] ==
259 "<p>henlo from my Psion netBook</p><p>message sent from my Psion netBook</p>"
260 end
261
262 test "it works for incoming announces with actor being inlined (kroeg)" do
263 data = File.read!("test/fixtures/kroeg-announce-with-inline-actor.json") |> Poison.decode!()
264
265 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
266
267 assert data["actor"] == "https://puckipedia.com/"
268 end
269
270 test "it works for incoming notices with tag not being an array (kroeg)" do
271 data = File.read!("test/fixtures/kroeg-array-less-emoji.json") |> Poison.decode!()
272
273 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
274 object = Object.normalize(data["object"])
275
276 assert object.data["emoji"] == %{
277 "icon_e_smile" => "https://puckipedia.com/forum/images/smilies/icon_e_smile.png"
278 }
279
280 data = File.read!("test/fixtures/kroeg-array-less-hashtag.json") |> Poison.decode!()
281
282 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
283 object = Object.normalize(data["object"])
284
285 assert "test" in object.data["tag"]
286 end
287
288 test "it works for incoming notices with url not being a string (prismo)" do
289 data = File.read!("test/fixtures/prismo-url-map.json") |> Poison.decode!()
290
291 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
292 object = Object.normalize(data["object"])
293
294 assert object.data["url"] == "https://prismo.news/posts/83"
295 end
296
297 test "it cleans up incoming notices which are not really DMs" do
298 user = insert(:user)
299 other_user = insert(:user)
300
301 to = [user.ap_id, other_user.ap_id]
302
303 data =
304 File.read!("test/fixtures/mastodon-post-activity.json")
305 |> Poison.decode!()
306 |> Map.put("to", to)
307 |> Map.put("cc", [])
308
309 object =
310 data["object"]
311 |> Map.put("to", to)
312 |> Map.put("cc", [])
313
314 data = Map.put(data, "object", object)
315
316 {:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data)
317
318 assert data["to"] == []
319 assert data["cc"] == to
320
321 object_data = Object.normalize(activity).data
322
323 assert object_data["to"] == []
324 assert object_data["cc"] == to
325 end
326
327 test "it works for incoming likes" do
328 user = insert(:user)
329 {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
330
331 data =
332 File.read!("test/fixtures/mastodon-like.json")
333 |> Poison.decode!()
334 |> Map.put("object", activity.data["object"])
335
336 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
337
338 assert data["actor"] == "http://mastodon.example.org/users/admin"
339 assert data["type"] == "Like"
340 assert data["id"] == "http://mastodon.example.org/users/admin#likes/2"
341 assert data["object"] == activity.data["object"]
342 end
343
344 test "it returns an error for incoming unlikes wihout a like activity" do
345 user = insert(:user)
346 {:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"})
347
348 data =
349 File.read!("test/fixtures/mastodon-undo-like.json")
350 |> Poison.decode!()
351 |> Map.put("object", activity.data["object"])
352
353 assert Transmogrifier.handle_incoming(data) == :error
354 end
355
356 test "it works for incoming unlikes with an existing like activity" do
357 user = insert(:user)
358 {:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"})
359
360 like_data =
361 File.read!("test/fixtures/mastodon-like.json")
362 |> Poison.decode!()
363 |> Map.put("object", activity.data["object"])
364
365 {:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data)
366
367 data =
368 File.read!("test/fixtures/mastodon-undo-like.json")
369 |> Poison.decode!()
370 |> Map.put("object", like_data)
371 |> Map.put("actor", like_data["actor"])
372
373 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
374
375 assert data["actor"] == "http://mastodon.example.org/users/admin"
376 assert data["type"] == "Undo"
377 assert data["id"] == "http://mastodon.example.org/users/admin#likes/2/undo"
378 assert data["object"]["id"] == "http://mastodon.example.org/users/admin#likes/2"
379 end
380
381 test "it works for incoming announces" do
382 data = File.read!("test/fixtures/mastodon-announce.json") |> Poison.decode!()
383
384 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
385
386 assert data["actor"] == "http://mastodon.example.org/users/admin"
387 assert data["type"] == "Announce"
388
389 assert data["id"] ==
390 "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
391
392 assert data["object"] ==
393 "http://mastodon.example.org/users/admin/statuses/99541947525187367"
394
395 assert Activity.get_create_by_object_ap_id(data["object"])
396 end
397
398 test "it works for incoming announces with an existing activity" do
399 user = insert(:user)
400 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
401
402 data =
403 File.read!("test/fixtures/mastodon-announce.json")
404 |> Poison.decode!()
405 |> Map.put("object", activity.data["object"])
406
407 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
408
409 assert data["actor"] == "http://mastodon.example.org/users/admin"
410 assert data["type"] == "Announce"
411
412 assert data["id"] ==
413 "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
414
415 assert data["object"] == activity.data["object"]
416
417 assert Activity.get_create_by_object_ap_id(data["object"]).id == activity.id
418 end
419
420 test "it does not clobber the addressing on announce activities" do
421 user = insert(:user)
422 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
423
424 data =
425 File.read!("test/fixtures/mastodon-announce.json")
426 |> Poison.decode!()
427 |> Map.put("object", Object.normalize(activity).data["id"])
428 |> Map.put("to", ["http://mastodon.example.org/users/admin/followers"])
429 |> Map.put("cc", [])
430
431 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
432
433 assert data["to"] == ["http://mastodon.example.org/users/admin/followers"]
434 end
435
436 test "it ensures that as:Public activities make it to their followers collection" do
437 user = insert(:user)
438
439 data =
440 File.read!("test/fixtures/mastodon-post-activity.json")
441 |> Poison.decode!()
442 |> Map.put("actor", user.ap_id)
443 |> Map.put("to", ["https://www.w3.org/ns/activitystreams#Public"])
444 |> Map.put("cc", [])
445
446 object =
447 data["object"]
448 |> Map.put("attributedTo", user.ap_id)
449 |> Map.put("to", ["https://www.w3.org/ns/activitystreams#Public"])
450 |> Map.put("cc", [])
451 |> Map.put("id", user.ap_id <> "/activities/12345678")
452
453 data = Map.put(data, "object", object)
454
455 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
456
457 assert data["cc"] == [User.ap_followers(user)]
458 end
459
460 test "it ensures that address fields become lists" do
461 user = insert(:user)
462
463 data =
464 File.read!("test/fixtures/mastodon-post-activity.json")
465 |> Poison.decode!()
466 |> Map.put("actor", user.ap_id)
467 |> Map.put("to", nil)
468 |> Map.put("cc", nil)
469
470 object =
471 data["object"]
472 |> Map.put("attributedTo", user.ap_id)
473 |> Map.put("to", nil)
474 |> Map.put("cc", nil)
475 |> Map.put("id", user.ap_id <> "/activities/12345678")
476
477 data = Map.put(data, "object", object)
478
479 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
480
481 assert !is_nil(data["to"])
482 assert !is_nil(data["cc"])
483 end
484
485 test "it strips internal likes" do
486 data =
487 File.read!("test/fixtures/mastodon-post-activity.json")
488 |> Poison.decode!()
489
490 likes = %{
491 "first" =>
492 "http://mastodon.example.org/objects/dbdbc507-52c8-490d-9b7c-1e1d52e5c132/likes?page=1",
493 "id" => "http://mastodon.example.org/objects/dbdbc507-52c8-490d-9b7c-1e1d52e5c132/likes",
494 "totalItems" => 3,
495 "type" => "OrderedCollection"
496 }
497
498 object = Map.put(data["object"], "likes", likes)
499 data = Map.put(data, "object", object)
500
501 {:ok, %Activity{object: object}} = Transmogrifier.handle_incoming(data)
502
503 refute Map.has_key?(object.data, "likes")
504 end
505
506 test "it works for incoming update activities" do
507 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
508
509 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
510 update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
511
512 object =
513 update_data["object"]
514 |> Map.put("actor", data["actor"])
515 |> Map.put("id", data["actor"])
516
517 update_data =
518 update_data
519 |> Map.put("actor", data["actor"])
520 |> Map.put("object", object)
521
522 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
523
524 user = User.get_cached_by_ap_id(data["actor"])
525 assert user.name == "gargle"
526
527 assert user.avatar["url"] == [
528 %{
529 "href" =>
530 "https://cd.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
531 }
532 ]
533
534 assert user.info.banner["url"] == [
535 %{
536 "href" =>
537 "https://cd.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
538 }
539 ]
540
541 assert user.bio == "<p>Some bio</p>"
542 end
543
544 test "it works with custom profile fields" do
545 {:ok, activity} =
546 "test/fixtures/mastodon-post-activity.json"
547 |> File.read!()
548 |> Poison.decode!()
549 |> Transmogrifier.handle_incoming()
550
551 user = User.get_cached_by_ap_id(activity.actor)
552
553 assert User.Info.fields(user.info) == [
554 %{"name" => "foo", "value" => "bar"},
555 %{"name" => "foo1", "value" => "bar1"}
556 ]
557
558 update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
559
560 object =
561 update_data["object"]
562 |> Map.put("actor", user.ap_id)
563 |> Map.put("id", user.ap_id)
564
565 update_data =
566 update_data
567 |> Map.put("actor", user.ap_id)
568 |> Map.put("object", object)
569
570 {:ok, _update_activity} = Transmogrifier.handle_incoming(update_data)
571
572 user = User.get_cached_by_ap_id(user.ap_id)
573
574 assert User.Info.fields(user.info) == [
575 %{"name" => "foo", "value" => "updated"},
576 %{"name" => "foo1", "value" => "updated"}
577 ]
578
579 Pleroma.Config.put([:instance, :max_remote_account_fields], 2)
580
581 update_data =
582 put_in(update_data, ["object", "attachment"], [
583 %{"name" => "foo", "type" => "PropertyValue", "value" => "bar"},
584 %{"name" => "foo11", "type" => "PropertyValue", "value" => "bar11"},
585 %{"name" => "foo22", "type" => "PropertyValue", "value" => "bar22"}
586 ])
587
588 {:ok, _} = Transmogrifier.handle_incoming(update_data)
589
590 user = User.get_cached_by_ap_id(user.ap_id)
591
592 assert User.Info.fields(user.info) == [
593 %{"name" => "foo", "value" => "updated"},
594 %{"name" => "foo1", "value" => "updated"}
595 ]
596
597 update_data = put_in(update_data, ["object", "attachment"], [])
598
599 {:ok, _} = Transmogrifier.handle_incoming(update_data)
600
601 user = User.get_cached_by_ap_id(user.ap_id)
602
603 assert User.Info.fields(user.info) == []
604 end
605
606 test "it works for incoming update activities which lock the account" do
607 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
608
609 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
610 update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
611
612 object =
613 update_data["object"]
614 |> Map.put("actor", data["actor"])
615 |> Map.put("id", data["actor"])
616 |> Map.put("manuallyApprovesFollowers", true)
617
618 update_data =
619 update_data
620 |> Map.put("actor", data["actor"])
621 |> Map.put("object", object)
622
623 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
624
625 user = User.get_cached_by_ap_id(data["actor"])
626 assert user.info.locked == true
627 end
628
629 test "it works for incoming deletes" do
630 activity = insert(:note_activity)
631
632 data =
633 File.read!("test/fixtures/mastodon-delete.json")
634 |> Poison.decode!()
635
636 object =
637 data["object"]
638 |> Map.put("id", activity.data["object"])
639
640 data =
641 data
642 |> Map.put("object", object)
643 |> Map.put("actor", activity.data["actor"])
644
645 {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data)
646
647 refute Activity.get_by_id(activity.id)
648 end
649
650 test "it fails for incoming deletes with spoofed origin" do
651 activity = insert(:note_activity)
652
653 data =
654 File.read!("test/fixtures/mastodon-delete.json")
655 |> Poison.decode!()
656
657 object =
658 data["object"]
659 |> Map.put("id", activity.data["object"])
660
661 data =
662 data
663 |> Map.put("object", object)
664
665 assert capture_log(fn ->
666 :error = Transmogrifier.handle_incoming(data)
667 end) =~
668 "[error] Could not decode user at fetch http://mastodon.example.org/users/gargron, {:error, {:error, :nxdomain}}"
669
670 assert Activity.get_by_id(activity.id)
671 end
672
673 test "it works for incoming user deletes" do
674 %{ap_id: ap_id} = insert(:user, ap_id: "http://mastodon.example.org/users/admin")
675
676 data =
677 File.read!("test/fixtures/mastodon-delete-user.json")
678 |> Poison.decode!()
679
680 {:ok, _} = Transmogrifier.handle_incoming(data)
681 ObanHelpers.perform_all()
682
683 refute User.get_cached_by_ap_id(ap_id)
684 end
685
686 test "it fails for incoming user deletes with spoofed origin" do
687 %{ap_id: ap_id} = insert(:user)
688
689 data =
690 File.read!("test/fixtures/mastodon-delete-user.json")
691 |> Poison.decode!()
692 |> Map.put("actor", ap_id)
693
694 assert :error == Transmogrifier.handle_incoming(data)
695 assert User.get_cached_by_ap_id(ap_id)
696 end
697
698 test "it works for incoming unannounces with an existing notice" do
699 user = insert(:user)
700 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
701
702 announce_data =
703 File.read!("test/fixtures/mastodon-announce.json")
704 |> Poison.decode!()
705 |> Map.put("object", activity.data["object"])
706
707 {:ok, %Activity{data: announce_data, local: false}} =
708 Transmogrifier.handle_incoming(announce_data)
709
710 data =
711 File.read!("test/fixtures/mastodon-undo-announce.json")
712 |> Poison.decode!()
713 |> Map.put("object", announce_data)
714 |> Map.put("actor", announce_data["actor"])
715
716 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
717
718 assert data["type"] == "Undo"
719 assert object_data = data["object"]
720 assert object_data["type"] == "Announce"
721 assert object_data["object"] == activity.data["object"]
722
723 assert object_data["id"] ==
724 "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
725 end
726
727 test "it works for incomming unfollows with an existing follow" do
728 user = insert(:user)
729
730 follow_data =
731 File.read!("test/fixtures/mastodon-follow-activity.json")
732 |> Poison.decode!()
733 |> Map.put("object", user.ap_id)
734
735 {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(follow_data)
736
737 data =
738 File.read!("test/fixtures/mastodon-unfollow-activity.json")
739 |> Poison.decode!()
740 |> Map.put("object", follow_data)
741
742 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
743
744 assert data["type"] == "Undo"
745 assert data["object"]["type"] == "Follow"
746 assert data["object"]["object"] == user.ap_id
747 assert data["actor"] == "http://mastodon.example.org/users/admin"
748
749 refute User.following?(User.get_cached_by_ap_id(data["actor"]), user)
750 end
751
752 test "it works for incoming blocks" do
753 user = insert(:user)
754
755 data =
756 File.read!("test/fixtures/mastodon-block-activity.json")
757 |> Poison.decode!()
758 |> Map.put("object", user.ap_id)
759
760 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
761
762 assert data["type"] == "Block"
763 assert data["object"] == user.ap_id
764 assert data["actor"] == "http://mastodon.example.org/users/admin"
765
766 blocker = User.get_cached_by_ap_id(data["actor"])
767
768 assert User.blocks?(blocker, user)
769 end
770
771 test "incoming blocks successfully tear down any follow relationship" do
772 blocker = insert(:user)
773 blocked = insert(:user)
774
775 data =
776 File.read!("test/fixtures/mastodon-block-activity.json")
777 |> Poison.decode!()
778 |> Map.put("object", blocked.ap_id)
779 |> Map.put("actor", blocker.ap_id)
780
781 {:ok, blocker} = User.follow(blocker, blocked)
782 {:ok, blocked} = User.follow(blocked, blocker)
783
784 assert User.following?(blocker, blocked)
785 assert User.following?(blocked, blocker)
786
787 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
788
789 assert data["type"] == "Block"
790 assert data["object"] == blocked.ap_id
791 assert data["actor"] == blocker.ap_id
792
793 blocker = User.get_cached_by_ap_id(data["actor"])
794 blocked = User.get_cached_by_ap_id(data["object"])
795
796 assert User.blocks?(blocker, blocked)
797
798 refute User.following?(blocker, blocked)
799 refute User.following?(blocked, blocker)
800 end
801
802 test "it works for incoming unblocks with an existing block" do
803 user = insert(:user)
804
805 block_data =
806 File.read!("test/fixtures/mastodon-block-activity.json")
807 |> Poison.decode!()
808 |> Map.put("object", user.ap_id)
809
810 {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(block_data)
811
812 data =
813 File.read!("test/fixtures/mastodon-unblock-activity.json")
814 |> Poison.decode!()
815 |> Map.put("object", block_data)
816
817 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
818 assert data["type"] == "Undo"
819 assert data["object"]["type"] == "Block"
820 assert data["object"]["object"] == user.ap_id
821 assert data["actor"] == "http://mastodon.example.org/users/admin"
822
823 blocker = User.get_cached_by_ap_id(data["actor"])
824
825 refute User.blocks?(blocker, user)
826 end
827
828 test "it works for incoming accepts which were pre-accepted" do
829 follower = insert(:user)
830 followed = insert(:user)
831
832 {:ok, follower} = User.follow(follower, followed)
833 assert User.following?(follower, followed) == true
834
835 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
836
837 accept_data =
838 File.read!("test/fixtures/mastodon-accept-activity.json")
839 |> Poison.decode!()
840 |> Map.put("actor", followed.ap_id)
841
842 object =
843 accept_data["object"]
844 |> Map.put("actor", follower.ap_id)
845 |> Map.put("id", follow_activity.data["id"])
846
847 accept_data = Map.put(accept_data, "object", object)
848
849 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
850 refute activity.local
851
852 assert activity.data["object"] == follow_activity.data["id"]
853
854 follower = User.get_cached_by_id(follower.id)
855
856 assert User.following?(follower, followed) == true
857 end
858
859 test "it works for incoming accepts which were orphaned" do
860 follower = insert(:user)
861 followed = insert(:user, %{info: %User.Info{locked: true}})
862
863 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
864
865 accept_data =
866 File.read!("test/fixtures/mastodon-accept-activity.json")
867 |> Poison.decode!()
868 |> Map.put("actor", followed.ap_id)
869
870 accept_data =
871 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
872
873 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
874 assert activity.data["object"] == follow_activity.data["id"]
875
876 follower = User.get_cached_by_id(follower.id)
877
878 assert User.following?(follower, followed) == true
879 end
880
881 test "it works for incoming accepts which are referenced by IRI only" do
882 follower = insert(:user)
883 followed = insert(:user, %{info: %User.Info{locked: true}})
884
885 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
886
887 accept_data =
888 File.read!("test/fixtures/mastodon-accept-activity.json")
889 |> Poison.decode!()
890 |> Map.put("actor", followed.ap_id)
891 |> Map.put("object", follow_activity.data["id"])
892
893 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
894 assert activity.data["object"] == follow_activity.data["id"]
895
896 follower = User.get_cached_by_id(follower.id)
897
898 assert User.following?(follower, followed) == true
899 end
900
901 test "it fails for incoming accepts which cannot be correlated" do
902 follower = insert(:user)
903 followed = insert(:user, %{info: %User.Info{locked: true}})
904
905 accept_data =
906 File.read!("test/fixtures/mastodon-accept-activity.json")
907 |> Poison.decode!()
908 |> Map.put("actor", followed.ap_id)
909
910 accept_data =
911 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
912
913 :error = Transmogrifier.handle_incoming(accept_data)
914
915 follower = User.get_cached_by_id(follower.id)
916
917 refute User.following?(follower, followed) == true
918 end
919
920 test "it fails for incoming rejects which cannot be correlated" do
921 follower = insert(:user)
922 followed = insert(:user, %{info: %User.Info{locked: true}})
923
924 accept_data =
925 File.read!("test/fixtures/mastodon-reject-activity.json")
926 |> Poison.decode!()
927 |> Map.put("actor", followed.ap_id)
928
929 accept_data =
930 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
931
932 :error = Transmogrifier.handle_incoming(accept_data)
933
934 follower = User.get_cached_by_id(follower.id)
935
936 refute User.following?(follower, followed) == true
937 end
938
939 test "it works for incoming rejects which are orphaned" do
940 follower = insert(:user)
941 followed = insert(:user, %{info: %User.Info{locked: true}})
942
943 {:ok, follower} = User.follow(follower, followed)
944 {:ok, _follow_activity} = ActivityPub.follow(follower, followed)
945
946 assert User.following?(follower, followed) == true
947
948 reject_data =
949 File.read!("test/fixtures/mastodon-reject-activity.json")
950 |> Poison.decode!()
951 |> Map.put("actor", followed.ap_id)
952
953 reject_data =
954 Map.put(reject_data, "object", Map.put(reject_data["object"], "actor", follower.ap_id))
955
956 {:ok, activity} = Transmogrifier.handle_incoming(reject_data)
957 refute activity.local
958
959 follower = User.get_cached_by_id(follower.id)
960
961 assert User.following?(follower, followed) == false
962 end
963
964 test "it works for incoming rejects which are referenced by IRI only" do
965 follower = insert(:user)
966 followed = insert(:user, %{info: %User.Info{locked: true}})
967
968 {:ok, follower} = User.follow(follower, followed)
969 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
970
971 assert User.following?(follower, followed) == true
972
973 reject_data =
974 File.read!("test/fixtures/mastodon-reject-activity.json")
975 |> Poison.decode!()
976 |> Map.put("actor", followed.ap_id)
977 |> Map.put("object", follow_activity.data["id"])
978
979 {:ok, %Activity{data: _}} = Transmogrifier.handle_incoming(reject_data)
980
981 follower = User.get_cached_by_id(follower.id)
982
983 assert User.following?(follower, followed) == false
984 end
985
986 test "it rejects activities without a valid ID" do
987 user = insert(:user)
988
989 data =
990 File.read!("test/fixtures/mastodon-follow-activity.json")
991 |> Poison.decode!()
992 |> Map.put("object", user.ap_id)
993 |> Map.put("id", "")
994
995 :error = Transmogrifier.handle_incoming(data)
996 end
997
998 test "it remaps video URLs as attachments if necessary" do
999 {:ok, object} =
1000 Fetcher.fetch_object_from_id(
1001 "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
1002 )
1003
1004 attachment = %{
1005 "type" => "Link",
1006 "mediaType" => "video/mp4",
1007 "href" =>
1008 "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
1009 "mimeType" => "video/mp4",
1010 "size" => 5_015_880,
1011 "url" => [
1012 %{
1013 "href" =>
1014 "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
1015 "mediaType" => "video/mp4",
1016 "type" => "Link"
1017 }
1018 ],
1019 "width" => 480
1020 }
1021
1022 assert object.data["url"] ==
1023 "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
1024
1025 assert object.data["attachment"] == [attachment]
1026 end
1027
1028 test "it accepts Flag activities" do
1029 user = insert(:user)
1030 other_user = insert(:user)
1031
1032 {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"})
1033 object = Object.normalize(activity)
1034
1035 message = %{
1036 "@context" => "https://www.w3.org/ns/activitystreams",
1037 "cc" => [user.ap_id],
1038 "object" => [user.ap_id, object.data["id"]],
1039 "type" => "Flag",
1040 "content" => "blocked AND reported!!!",
1041 "actor" => other_user.ap_id
1042 }
1043
1044 assert {:ok, activity} = Transmogrifier.handle_incoming(message)
1045
1046 assert activity.data["object"] == [user.ap_id, object.data["id"]]
1047 assert activity.data["content"] == "blocked AND reported!!!"
1048 assert activity.data["actor"] == other_user.ap_id
1049 assert activity.data["cc"] == [user.ap_id]
1050 end
1051 end
1052
1053 describe "prepare outgoing" do
1054 test "it turns mentions into tags" do
1055 user = insert(:user)
1056 other_user = insert(:user)
1057
1058 {:ok, activity} =
1059 CommonAPI.post(user, %{"status" => "hey, @#{other_user.nickname}, how are ya? #2hu"})
1060
1061 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1062 object = modified["object"]
1063
1064 expected_mention = %{
1065 "href" => other_user.ap_id,
1066 "name" => "@#{other_user.nickname}",
1067 "type" => "Mention"
1068 }
1069
1070 expected_tag = %{
1071 "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
1072 "type" => "Hashtag",
1073 "name" => "#2hu"
1074 }
1075
1076 assert Enum.member?(object["tag"], expected_tag)
1077 assert Enum.member?(object["tag"], expected_mention)
1078 end
1079
1080 test "it adds the sensitive property" do
1081 user = insert(:user)
1082
1083 {:ok, activity} = CommonAPI.post(user, %{"status" => "#nsfw hey"})
1084 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1085
1086 assert modified["object"]["sensitive"]
1087 end
1088
1089 test "it adds the json-ld context and the conversation property" do
1090 user = insert(:user)
1091
1092 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
1093 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1094
1095 assert modified["@context"] ==
1096 Pleroma.Web.ActivityPub.Utils.make_json_ld_header()["@context"]
1097
1098 assert modified["object"]["conversation"] == modified["context"]
1099 end
1100
1101 test "it sets the 'attributedTo' property to the actor of the object if it doesn't have one" do
1102 user = insert(:user)
1103
1104 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
1105 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1106
1107 assert modified["object"]["actor"] == modified["object"]["attributedTo"]
1108 end
1109
1110 test "it translates ostatus IDs to external URLs" do
1111 incoming = File.read!("test/fixtures/incoming_note_activity.xml")
1112 {:ok, [referent_activity]} = OStatus.handle_incoming(incoming)
1113
1114 user = insert(:user)
1115
1116 {:ok, activity, _} = CommonAPI.favorite(referent_activity.id, user)
1117 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1118
1119 assert modified["object"] == "http://gs.example.org:4040/index.php/notice/29"
1120 end
1121
1122 test "it translates ostatus reply_to IDs to external URLs" do
1123 incoming = File.read!("test/fixtures/incoming_note_activity.xml")
1124 {:ok, [referred_activity]} = OStatus.handle_incoming(incoming)
1125
1126 user = insert(:user)
1127
1128 {:ok, activity} =
1129 CommonAPI.post(user, %{"status" => "HI!", "in_reply_to_status_id" => referred_activity.id})
1130
1131 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1132
1133 assert modified["object"]["inReplyTo"] == "http://gs.example.org:4040/index.php/notice/29"
1134 end
1135
1136 test "it strips internal hashtag data" do
1137 user = insert(:user)
1138
1139 {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu"})
1140
1141 expected_tag = %{
1142 "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
1143 "type" => "Hashtag",
1144 "name" => "#2hu"
1145 }
1146
1147 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1148
1149 assert modified["object"]["tag"] == [expected_tag]
1150 end
1151
1152 test "it strips internal fields" do
1153 user = insert(:user)
1154
1155 {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu :firefox:"})
1156
1157 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1158
1159 assert length(modified["object"]["tag"]) == 2
1160
1161 assert is_nil(modified["object"]["emoji"])
1162 assert is_nil(modified["object"]["like_count"])
1163 assert is_nil(modified["object"]["announcements"])
1164 assert is_nil(modified["object"]["announcement_count"])
1165 assert is_nil(modified["object"]["context_id"])
1166 end
1167
1168 test "it strips internal fields of article" do
1169 activity = insert(:article_activity)
1170
1171 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1172
1173 assert length(modified["object"]["tag"]) == 2
1174
1175 assert is_nil(modified["object"]["emoji"])
1176 assert is_nil(modified["object"]["like_count"])
1177 assert is_nil(modified["object"]["announcements"])
1178 assert is_nil(modified["object"]["announcement_count"])
1179 assert is_nil(modified["object"]["context_id"])
1180 assert is_nil(modified["object"]["likes"])
1181 end
1182
1183 test "the directMessage flag is present" do
1184 user = insert(:user)
1185 other_user = insert(:user)
1186
1187 {:ok, activity} = CommonAPI.post(user, %{"status" => "2hu :moominmamma:"})
1188
1189 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1190
1191 assert modified["directMessage"] == false
1192
1193 {:ok, activity} =
1194 CommonAPI.post(user, %{"status" => "@#{other_user.nickname} :moominmamma:"})
1195
1196 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1197
1198 assert modified["directMessage"] == false
1199
1200 {:ok, activity} =
1201 CommonAPI.post(user, %{
1202 "status" => "@#{other_user.nickname} :moominmamma:",
1203 "visibility" => "direct"
1204 })
1205
1206 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1207
1208 assert modified["directMessage"] == true
1209 end
1210
1211 test "it strips BCC field" do
1212 user = insert(:user)
1213 {:ok, list} = Pleroma.List.create("foo", user)
1214
1215 {:ok, activity} =
1216 CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"})
1217
1218 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1219
1220 assert is_nil(modified["bcc"])
1221 end
1222
1223 test "it can handle Listen activities" do
1224 listen_activity = insert(:listen)
1225
1226 {:ok, modified} = Transmogrifier.prepare_outgoing(listen_activity.data)
1227
1228 assert modified["type"] == "Listen"
1229
1230 user = insert(:user)
1231
1232 {:ok, activity} = CommonAPI.listen(user, %{"title" => "lain radio episode 1"})
1233
1234 {:ok, _modified} = Transmogrifier.prepare_outgoing(activity.data)
1235 end
1236 end
1237
1238 describe "user upgrade" do
1239 test "it upgrades a user to activitypub" do
1240 user =
1241 insert(:user, %{
1242 nickname: "rye@niu.moe",
1243 local: false,
1244 ap_id: "https://niu.moe/users/rye",
1245 follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"})
1246 })
1247
1248 user_two = insert(:user, %{following: [user.follower_address]})
1249
1250 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
1251 {:ok, unrelated_activity} = CommonAPI.post(user_two, %{"status" => "test"})
1252 assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients
1253
1254 user = User.get_cached_by_id(user.id)
1255 assert user.info.note_count == 1
1256
1257 {:ok, user} = Transmogrifier.upgrade_user_from_ap_id("https://niu.moe/users/rye")
1258 ObanHelpers.perform_all()
1259
1260 assert user.info.ap_enabled
1261 assert user.info.note_count == 1
1262 assert user.follower_address == "https://niu.moe/users/rye/followers"
1263 assert user.following_address == "https://niu.moe/users/rye/following"
1264
1265 user = User.get_cached_by_id(user.id)
1266 assert user.info.note_count == 1
1267
1268 activity = Activity.get_by_id(activity.id)
1269 assert user.follower_address in activity.recipients
1270
1271 assert %{
1272 "url" => [
1273 %{
1274 "href" =>
1275 "https://cdn.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
1276 }
1277 ]
1278 } = user.avatar
1279
1280 assert %{
1281 "url" => [
1282 %{
1283 "href" =>
1284 "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
1285 }
1286 ]
1287 } = user.info.banner
1288
1289 refute "..." in activity.recipients
1290
1291 unrelated_activity = Activity.get_by_id(unrelated_activity.id)
1292 refute user.follower_address in unrelated_activity.recipients
1293
1294 user_two = User.get_cached_by_id(user_two.id)
1295 assert user.follower_address in user_two.following
1296 refute "..." in user_two.following
1297 end
1298 end
1299
1300 describe "maybe_retire_websub" do
1301 test "it deletes all websub client subscripitions with the user as topic" do
1302 subscription = %WebsubClientSubscription{topic: "https://niu.moe/users/rye.atom"}
1303 {:ok, ws} = Repo.insert(subscription)
1304
1305 subscription = %WebsubClientSubscription{topic: "https://niu.moe/users/pasty.atom"}
1306 {:ok, ws2} = Repo.insert(subscription)
1307
1308 Transmogrifier.maybe_retire_websub("https://niu.moe/users/rye")
1309
1310 refute Repo.get(WebsubClientSubscription, ws.id)
1311 assert Repo.get(WebsubClientSubscription, ws2.id)
1312 end
1313 end
1314
1315 describe "actor rewriting" do
1316 test "it fixes the actor URL property to be a proper URI" do
1317 data = %{
1318 "url" => %{"href" => "http://example.com"}
1319 }
1320
1321 rewritten = Transmogrifier.maybe_fix_user_object(data)
1322 assert rewritten["url"] == "http://example.com"
1323 end
1324 end
1325
1326 describe "actor origin containment" do
1327 test "it rejects activities which reference objects with bogus origins" do
1328 data = %{
1329 "@context" => "https://www.w3.org/ns/activitystreams",
1330 "id" => "http://mastodon.example.org/users/admin/activities/1234",
1331 "actor" => "http://mastodon.example.org/users/admin",
1332 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1333 "object" => "https://info.pleroma.site/activity.json",
1334 "type" => "Announce"
1335 }
1336
1337 :error = Transmogrifier.handle_incoming(data)
1338 end
1339
1340 test "it rejects activities which reference objects that have an incorrect attribution (variant 1)" do
1341 data = %{
1342 "@context" => "https://www.w3.org/ns/activitystreams",
1343 "id" => "http://mastodon.example.org/users/admin/activities/1234",
1344 "actor" => "http://mastodon.example.org/users/admin",
1345 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1346 "object" => "https://info.pleroma.site/activity2.json",
1347 "type" => "Announce"
1348 }
1349
1350 :error = Transmogrifier.handle_incoming(data)
1351 end
1352
1353 test "it rejects activities which reference objects that have an incorrect attribution (variant 2)" do
1354 data = %{
1355 "@context" => "https://www.w3.org/ns/activitystreams",
1356 "id" => "http://mastodon.example.org/users/admin/activities/1234",
1357 "actor" => "http://mastodon.example.org/users/admin",
1358 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1359 "object" => "https://info.pleroma.site/activity3.json",
1360 "type" => "Announce"
1361 }
1362
1363 :error = Transmogrifier.handle_incoming(data)
1364 end
1365 end
1366
1367 describe "reserialization" do
1368 test "successfully reserializes a message with inReplyTo == nil" do
1369 user = insert(:user)
1370
1371 message = %{
1372 "@context" => "https://www.w3.org/ns/activitystreams",
1373 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1374 "cc" => [],
1375 "type" => "Create",
1376 "object" => %{
1377 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1378 "cc" => [],
1379 "type" => "Note",
1380 "content" => "Hi",
1381 "inReplyTo" => nil,
1382 "attributedTo" => user.ap_id
1383 },
1384 "actor" => user.ap_id
1385 }
1386
1387 {:ok, activity} = Transmogrifier.handle_incoming(message)
1388
1389 {:ok, _} = Transmogrifier.prepare_outgoing(activity.data)
1390 end
1391
1392 test "successfully reserializes a message with AS2 objects in IR" do
1393 user = insert(:user)
1394
1395 message = %{
1396 "@context" => "https://www.w3.org/ns/activitystreams",
1397 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1398 "cc" => [],
1399 "type" => "Create",
1400 "object" => %{
1401 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1402 "cc" => [],
1403 "type" => "Note",
1404 "content" => "Hi",
1405 "inReplyTo" => nil,
1406 "attributedTo" => user.ap_id,
1407 "tag" => [
1408 %{"name" => "#2hu", "href" => "http://example.com/2hu", "type" => "Hashtag"},
1409 %{"name" => "Bob", "href" => "http://example.com/bob", "type" => "Mention"}
1410 ]
1411 },
1412 "actor" => user.ap_id
1413 }
1414
1415 {:ok, activity} = Transmogrifier.handle_incoming(message)
1416
1417 {:ok, _} = Transmogrifier.prepare_outgoing(activity.data)
1418 end
1419 end
1420
1421 test "Rewrites Answers to Notes" do
1422 user = insert(:user)
1423
1424 {:ok, poll_activity} =
1425 CommonAPI.post(user, %{
1426 "status" => "suya...",
1427 "poll" => %{"options" => ["suya", "suya.", "suya.."], "expires_in" => 10}
1428 })
1429
1430 poll_object = Object.normalize(poll_activity)
1431 # TODO: Replace with CommonAPI vote creation when implemented
1432 data =
1433 File.read!("test/fixtures/mastodon-vote.json")
1434 |> Poison.decode!()
1435 |> Kernel.put_in(["to"], user.ap_id)
1436 |> Kernel.put_in(["object", "inReplyTo"], poll_object.data["id"])
1437 |> Kernel.put_in(["object", "to"], user.ap_id)
1438
1439 {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data)
1440 {:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
1441
1442 assert data["object"]["type"] == "Note"
1443 end
1444
1445 describe "fix_explicit_addressing" do
1446 setup do
1447 user = insert(:user)
1448 [user: user]
1449 end
1450
1451 test "moves non-explicitly mentioned actors to cc", %{user: user} do
1452 explicitly_mentioned_actors = [
1453 "https://pleroma.gold/users/user1",
1454 "https://pleroma.gold/user2"
1455 ]
1456
1457 object = %{
1458 "actor" => user.ap_id,
1459 "to" => explicitly_mentioned_actors ++ ["https://social.beepboop.ga/users/dirb"],
1460 "cc" => [],
1461 "tag" =>
1462 Enum.map(explicitly_mentioned_actors, fn href ->
1463 %{"type" => "Mention", "href" => href}
1464 end)
1465 }
1466
1467 fixed_object = Transmogrifier.fix_explicit_addressing(object)
1468 assert Enum.all?(explicitly_mentioned_actors, &(&1 in fixed_object["to"]))
1469 refute "https://social.beepboop.ga/users/dirb" in fixed_object["to"]
1470 assert "https://social.beepboop.ga/users/dirb" in fixed_object["cc"]
1471 end
1472
1473 test "does not move actor's follower collection to cc", %{user: user} do
1474 object = %{
1475 "actor" => user.ap_id,
1476 "to" => [user.follower_address],
1477 "cc" => []
1478 }
1479
1480 fixed_object = Transmogrifier.fix_explicit_addressing(object)
1481 assert user.follower_address in fixed_object["to"]
1482 refute user.follower_address in fixed_object["cc"]
1483 end
1484
1485 test "removes recipient's follower collection from cc", %{user: user} do
1486 recipient = insert(:user)
1487
1488 object = %{
1489 "actor" => user.ap_id,
1490 "to" => [recipient.ap_id, "https://www.w3.org/ns/activitystreams#Public"],
1491 "cc" => [user.follower_address, recipient.follower_address]
1492 }
1493
1494 fixed_object = Transmogrifier.fix_explicit_addressing(object)
1495
1496 assert user.follower_address in fixed_object["cc"]
1497 refute recipient.follower_address in fixed_object["cc"]
1498 refute recipient.follower_address in fixed_object["to"]
1499 end
1500 end
1501
1502 describe "fix_summary/1" do
1503 test "returns fixed object" do
1504 assert Transmogrifier.fix_summary(%{"summary" => nil}) == %{"summary" => ""}
1505 assert Transmogrifier.fix_summary(%{"summary" => "ok"}) == %{"summary" => "ok"}
1506 assert Transmogrifier.fix_summary(%{}) == %{"summary" => ""}
1507 end
1508 end
1509
1510 describe "fix_in_reply_to/2" do
1511 clear_config([:instance, :federation_incoming_replies_max_depth])
1512
1513 setup do
1514 data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json"))
1515 [data: data]
1516 end
1517
1518 test "returns not modified object when hasn't containts inReplyTo field", %{data: data} do
1519 assert Transmogrifier.fix_in_reply_to(data) == data
1520 end
1521
1522 test "returns object with inReplyToAtomUri when denied incoming reply", %{data: data} do
1523 Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0)
1524
1525 object_with_reply =
1526 Map.put(data["object"], "inReplyTo", "https://shitposter.club/notice/2827873")
1527
1528 modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
1529 assert modified_object["inReplyTo"] == "https://shitposter.club/notice/2827873"
1530 assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
1531
1532 object_with_reply =
1533 Map.put(data["object"], "inReplyTo", %{"id" => "https://shitposter.club/notice/2827873"})
1534
1535 modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
1536 assert modified_object["inReplyTo"] == %{"id" => "https://shitposter.club/notice/2827873"}
1537 assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
1538
1539 object_with_reply =
1540 Map.put(data["object"], "inReplyTo", ["https://shitposter.club/notice/2827873"])
1541
1542 modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
1543 assert modified_object["inReplyTo"] == ["https://shitposter.club/notice/2827873"]
1544 assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
1545
1546 object_with_reply = Map.put(data["object"], "inReplyTo", [])
1547 modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
1548 assert modified_object["inReplyTo"] == []
1549 assert modified_object["inReplyToAtomUri"] == ""
1550 end
1551
1552 test "returns modified object when allowed incoming reply", %{data: data} do
1553 object_with_reply =
1554 Map.put(
1555 data["object"],
1556 "inReplyTo",
1557 "https://shitposter.club/notice/2827873"
1558 )
1559
1560 Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 5)
1561 modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
1562
1563 assert modified_object["inReplyTo"] ==
1564 "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
1565
1566 assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
1567
1568 assert modified_object["conversation"] ==
1569 "tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26"
1570
1571 assert modified_object["context"] ==
1572 "tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26"
1573 end
1574 end
1575
1576 describe "fix_url/1" do
1577 test "fixes data for object when url is map" do
1578 object = %{
1579 "url" => %{
1580 "type" => "Link",
1581 "mimeType" => "video/mp4",
1582 "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4"
1583 }
1584 }
1585
1586 assert Transmogrifier.fix_url(object) == %{
1587 "url" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4"
1588 }
1589 end
1590
1591 test "fixes data for video object" do
1592 object = %{
1593 "type" => "Video",
1594 "url" => [
1595 %{
1596 "type" => "Link",
1597 "mimeType" => "video/mp4",
1598 "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4"
1599 },
1600 %{
1601 "type" => "Link",
1602 "mimeType" => "video/mp4",
1603 "href" => "https://peertube46fb-ad81-2d4c2d1630e3-240.mp4"
1604 },
1605 %{
1606 "type" => "Link",
1607 "mimeType" => "text/html",
1608 "href" => "https://peertube.-2d4c2d1630e3"
1609 },
1610 %{
1611 "type" => "Link",
1612 "mimeType" => "text/html",
1613 "href" => "https://peertube.-2d4c2d16377-42"
1614 }
1615 ]
1616 }
1617
1618 assert Transmogrifier.fix_url(object) == %{
1619 "attachment" => [
1620 %{
1621 "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4",
1622 "mimeType" => "video/mp4",
1623 "type" => "Link"
1624 }
1625 ],
1626 "type" => "Video",
1627 "url" => "https://peertube.-2d4c2d1630e3"
1628 }
1629 end
1630
1631 test "fixes url for not Video object" do
1632 object = %{
1633 "type" => "Text",
1634 "url" => [
1635 %{
1636 "type" => "Link",
1637 "mimeType" => "text/html",
1638 "href" => "https://peertube.-2d4c2d1630e3"
1639 },
1640 %{
1641 "type" => "Link",
1642 "mimeType" => "text/html",
1643 "href" => "https://peertube.-2d4c2d16377-42"
1644 }
1645 ]
1646 }
1647
1648 assert Transmogrifier.fix_url(object) == %{
1649 "type" => "Text",
1650 "url" => "https://peertube.-2d4c2d1630e3"
1651 }
1652
1653 assert Transmogrifier.fix_url(%{"type" => "Text", "url" => []}) == %{
1654 "type" => "Text",
1655 "url" => ""
1656 }
1657 end
1658
1659 test "retunrs not modified object" do
1660 assert Transmogrifier.fix_url(%{"type" => "Text"}) == %{"type" => "Text"}
1661 end
1662 end
1663
1664 describe "get_obj_helper/2" do
1665 test "returns nil when cannot normalize object" do
1666 refute Transmogrifier.get_obj_helper("test-obj-id")
1667 end
1668
1669 test "returns {:ok, %Object{}} for success case" do
1670 assert {:ok, %Object{}} =
1671 Transmogrifier.get_obj_helper("https://shitposter.club/notice/2827873")
1672 end
1673 end
1674
1675 describe "fix_attachments/1" do
1676 test "returns not modified object" do
1677 data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json"))
1678 assert Transmogrifier.fix_attachments(data) == data
1679 end
1680
1681 test "returns modified object when attachment is map" do
1682 assert Transmogrifier.fix_attachments(%{
1683 "attachment" => %{
1684 "mediaType" => "video/mp4",
1685 "url" => "https://peertube.moe/stat-480.mp4"
1686 }
1687 }) == %{
1688 "attachment" => [
1689 %{
1690 "mediaType" => "video/mp4",
1691 "url" => [
1692 %{
1693 "href" => "https://peertube.moe/stat-480.mp4",
1694 "mediaType" => "video/mp4",
1695 "type" => "Link"
1696 }
1697 ]
1698 }
1699 ]
1700 }
1701 end
1702
1703 test "returns modified object when attachment is list" do
1704 assert Transmogrifier.fix_attachments(%{
1705 "attachment" => [
1706 %{"mediaType" => "video/mp4", "url" => "https://pe.er/stat-480.mp4"},
1707 %{"mimeType" => "video/mp4", "href" => "https://pe.er/stat-480.mp4"}
1708 ]
1709 }) == %{
1710 "attachment" => [
1711 %{
1712 "mediaType" => "video/mp4",
1713 "url" => [
1714 %{
1715 "href" => "https://pe.er/stat-480.mp4",
1716 "mediaType" => "video/mp4",
1717 "type" => "Link"
1718 }
1719 ]
1720 },
1721 %{
1722 "href" => "https://pe.er/stat-480.mp4",
1723 "mediaType" => "video/mp4",
1724 "mimeType" => "video/mp4",
1725 "url" => [
1726 %{
1727 "href" => "https://pe.er/stat-480.mp4",
1728 "mediaType" => "video/mp4",
1729 "type" => "Link"
1730 }
1731 ]
1732 }
1733 ]
1734 }
1735 end
1736 end
1737
1738 describe "fix_emoji/1" do
1739 test "returns not modified object when object not contains tags" do
1740 data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json"))
1741 assert Transmogrifier.fix_emoji(data) == data
1742 end
1743
1744 test "returns object with emoji when object contains list tags" do
1745 assert Transmogrifier.fix_emoji(%{
1746 "tag" => [
1747 %{"type" => "Emoji", "name" => ":bib:", "icon" => %{"url" => "/test"}},
1748 %{"type" => "Hashtag"}
1749 ]
1750 }) == %{
1751 "emoji" => %{"bib" => "/test"},
1752 "tag" => [
1753 %{"icon" => %{"url" => "/test"}, "name" => ":bib:", "type" => "Emoji"},
1754 %{"type" => "Hashtag"}
1755 ]
1756 }
1757 end
1758
1759 test "returns object with emoji when object contains map tag" do
1760 assert Transmogrifier.fix_emoji(%{
1761 "tag" => %{"type" => "Emoji", "name" => ":bib:", "icon" => %{"url" => "/test"}}
1762 }) == %{
1763 "emoji" => %{"bib" => "/test"},
1764 "tag" => %{"icon" => %{"url" => "/test"}, "name" => ":bib:", "type" => "Emoji"}
1765 }
1766 end
1767 end
1768 end