[#1149] Added more oban workers. Refactoring.
[akkoma] / test / web / activity_pub / transmogrifier_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2018 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 describe "handle_incoming" do
29 test "it ignores an incoming notice if we already have it" do
30 activity = insert(:note_activity)
31
32 data =
33 File.read!("test/fixtures/mastodon-post-activity.json")
34 |> Poison.decode!()
35 |> Map.put("object", Object.normalize(activity).data)
36
37 {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
38
39 assert activity == returned_activity
40 end
41
42 test "it fetches replied-to activities if we don't have them" do
43 data =
44 File.read!("test/fixtures/mastodon-post-activity.json")
45 |> Poison.decode!()
46
47 object =
48 data["object"]
49 |> Map.put("inReplyTo", "https://shitposter.club/notice/2827873")
50
51 data = Map.put(data, "object", object)
52 {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
53 returned_object = Object.normalize(returned_activity, false)
54
55 assert activity =
56 Activity.get_create_by_object_ap_id(
57 "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
58 )
59
60 assert returned_object.data["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
61 end
62
63 test "it does not fetch replied-to activities beyond max_replies_depth" do
64 data =
65 File.read!("test/fixtures/mastodon-post-activity.json")
66 |> Poison.decode!()
67
68 object =
69 data["object"]
70 |> Map.put("inReplyTo", "https://shitposter.club/notice/2827873")
71
72 data = Map.put(data, "object", object)
73
74 with_mock Pleroma.Web.Federator,
75 allowed_incoming_reply_depth?: fn _ -> false end do
76 {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
77
78 returned_object = Object.normalize(returned_activity, false)
79
80 refute Activity.get_create_by_object_ap_id(
81 "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
82 )
83
84 assert returned_object.data["inReplyToAtomUri"] ==
85 "https://shitposter.club/notice/2827873"
86 end
87 end
88
89 test "it does not crash if the object in inReplyTo can't be fetched" do
90 data =
91 File.read!("test/fixtures/mastodon-post-activity.json")
92 |> Poison.decode!()
93
94 object =
95 data["object"]
96 |> Map.put("inReplyTo", "https://404.site/whatever")
97
98 data =
99 data
100 |> Map.put("object", object)
101
102 assert capture_log(fn ->
103 {:ok, _returned_activity} = Transmogrifier.handle_incoming(data)
104 end) =~ "[error] Couldn't fetch \"\"https://404.site/whatever\"\", error: nil"
105 end
106
107 test "it works for incoming notices" do
108 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
109
110 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
111
112 assert data["id"] ==
113 "http://mastodon.example.org/users/admin/statuses/99512778738411822/activity"
114
115 assert data["context"] ==
116 "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation"
117
118 assert data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
119
120 assert data["cc"] == [
121 "http://mastodon.example.org/users/admin/followers",
122 "http://localtesting.pleroma.lol/users/lain"
123 ]
124
125 assert data["actor"] == "http://mastodon.example.org/users/admin"
126
127 object_data = Object.normalize(data["object"]).data
128
129 assert object_data["id"] ==
130 "http://mastodon.example.org/users/admin/statuses/99512778738411822"
131
132 assert object_data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
133
134 assert object_data["cc"] == [
135 "http://mastodon.example.org/users/admin/followers",
136 "http://localtesting.pleroma.lol/users/lain"
137 ]
138
139 assert object_data["actor"] == "http://mastodon.example.org/users/admin"
140 assert object_data["attributedTo"] == "http://mastodon.example.org/users/admin"
141
142 assert object_data["context"] ==
143 "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation"
144
145 assert object_data["sensitive"] == true
146
147 user = User.get_cached_by_ap_id(object_data["actor"])
148
149 assert user.info.note_count == 1
150 end
151
152 test "it works for incoming notices with hashtags" do
153 data = File.read!("test/fixtures/mastodon-post-activity-hashtag.json") |> Poison.decode!()
154
155 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
156 object = Object.normalize(data["object"])
157
158 assert Enum.at(object.data["tag"], 2) == "moo"
159 end
160
161 test "it works for incoming questions" do
162 data = File.read!("test/fixtures/mastodon-question-activity.json") |> Poison.decode!()
163
164 {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data)
165
166 object = Object.normalize(activity)
167
168 assert Enum.all?(object.data["oneOf"], fn choice ->
169 choice["name"] in [
170 "Dunno",
171 "Everyone knows that!",
172 "25 char limit is dumb",
173 "I can't even fit a funny"
174 ]
175 end)
176 end
177
178 test "it rewrites Note votes to Answers and increments vote counters on question activities" do
179 user = insert(:user)
180
181 {:ok, activity} =
182 CommonAPI.post(user, %{
183 "status" => "suya...",
184 "poll" => %{"options" => ["suya", "suya.", "suya.."], "expires_in" => 10}
185 })
186
187 object = Object.normalize(activity)
188
189 data =
190 File.read!("test/fixtures/mastodon-vote.json")
191 |> Poison.decode!()
192 |> Kernel.put_in(["to"], user.ap_id)
193 |> Kernel.put_in(["object", "inReplyTo"], object.data["id"])
194 |> Kernel.put_in(["object", "to"], user.ap_id)
195
196 {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data)
197 answer_object = Object.normalize(activity)
198 assert answer_object.data["type"] == "Answer"
199 object = Object.get_by_ap_id(object.data["id"])
200
201 assert Enum.any?(
202 object.data["oneOf"],
203 fn
204 %{"name" => "suya..", "replies" => %{"totalItems" => 1}} -> true
205 _ -> false
206 end
207 )
208 end
209
210 test "it works for incoming notices with contentMap" do
211 data =
212 File.read!("test/fixtures/mastodon-post-activity-contentmap.json") |> Poison.decode!()
213
214 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
215 object = Object.normalize(data["object"])
216
217 assert object.data["content"] ==
218 "<p><span class=\"h-card\"><a href=\"http://localtesting.pleroma.lol/users/lain\" class=\"u-url mention\">@<span>lain</span></a></span></p>"
219 end
220
221 test "it works for incoming notices with to/cc not being an array (kroeg)" do
222 data = File.read!("test/fixtures/kroeg-post-activity.json") |> Poison.decode!()
223
224 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
225 object = Object.normalize(data["object"])
226
227 assert object.data["content"] ==
228 "<p>henlo from my Psion netBook</p><p>message sent from my Psion netBook</p>"
229 end
230
231 test "it works for incoming announces with actor being inlined (kroeg)" do
232 data = File.read!("test/fixtures/kroeg-announce-with-inline-actor.json") |> Poison.decode!()
233
234 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
235
236 assert data["actor"] == "https://puckipedia.com/"
237 end
238
239 test "it works for incoming notices with tag not being an array (kroeg)" do
240 data = File.read!("test/fixtures/kroeg-array-less-emoji.json") |> Poison.decode!()
241
242 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
243 object = Object.normalize(data["object"])
244
245 assert object.data["emoji"] == %{
246 "icon_e_smile" => "https://puckipedia.com/forum/images/smilies/icon_e_smile.png"
247 }
248
249 data = File.read!("test/fixtures/kroeg-array-less-hashtag.json") |> Poison.decode!()
250
251 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
252 object = Object.normalize(data["object"])
253
254 assert "test" in object.data["tag"]
255 end
256
257 test "it works for incoming notices with url not being a string (prismo)" do
258 data = File.read!("test/fixtures/prismo-url-map.json") |> Poison.decode!()
259
260 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
261 object = Object.normalize(data["object"])
262
263 assert object.data["url"] == "https://prismo.news/posts/83"
264 end
265
266 test "it cleans up incoming notices which are not really DMs" do
267 user = insert(:user)
268 other_user = insert(:user)
269
270 to = [user.ap_id, other_user.ap_id]
271
272 data =
273 File.read!("test/fixtures/mastodon-post-activity.json")
274 |> Poison.decode!()
275 |> Map.put("to", to)
276 |> Map.put("cc", [])
277
278 object =
279 data["object"]
280 |> Map.put("to", to)
281 |> Map.put("cc", [])
282
283 data = Map.put(data, "object", object)
284
285 {:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data)
286
287 assert data["to"] == []
288 assert data["cc"] == to
289
290 object_data = Object.normalize(activity).data
291
292 assert object_data["to"] == []
293 assert object_data["cc"] == to
294 end
295
296 test "it works for incoming likes" do
297 user = insert(:user)
298 {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
299
300 data =
301 File.read!("test/fixtures/mastodon-like.json")
302 |> Poison.decode!()
303 |> Map.put("object", activity.data["object"])
304
305 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
306
307 assert data["actor"] == "http://mastodon.example.org/users/admin"
308 assert data["type"] == "Like"
309 assert data["id"] == "http://mastodon.example.org/users/admin#likes/2"
310 assert data["object"] == activity.data["object"]
311 end
312
313 test "it returns an error for incoming unlikes wihout a like activity" do
314 user = insert(:user)
315 {:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"})
316
317 data =
318 File.read!("test/fixtures/mastodon-undo-like.json")
319 |> Poison.decode!()
320 |> Map.put("object", activity.data["object"])
321
322 assert Transmogrifier.handle_incoming(data) == :error
323 end
324
325 test "it works for incoming unlikes with an existing like activity" do
326 user = insert(:user)
327 {:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"})
328
329 like_data =
330 File.read!("test/fixtures/mastodon-like.json")
331 |> Poison.decode!()
332 |> Map.put("object", activity.data["object"])
333
334 {:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data)
335
336 data =
337 File.read!("test/fixtures/mastodon-undo-like.json")
338 |> Poison.decode!()
339 |> Map.put("object", like_data)
340 |> Map.put("actor", like_data["actor"])
341
342 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
343
344 assert data["actor"] == "http://mastodon.example.org/users/admin"
345 assert data["type"] == "Undo"
346 assert data["id"] == "http://mastodon.example.org/users/admin#likes/2/undo"
347 assert data["object"]["id"] == "http://mastodon.example.org/users/admin#likes/2"
348 end
349
350 test "it works for incoming announces" do
351 data = File.read!("test/fixtures/mastodon-announce.json") |> Poison.decode!()
352
353 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
354
355 assert data["actor"] == "http://mastodon.example.org/users/admin"
356 assert data["type"] == "Announce"
357
358 assert data["id"] ==
359 "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
360
361 assert data["object"] ==
362 "http://mastodon.example.org/users/admin/statuses/99541947525187367"
363
364 assert Activity.get_create_by_object_ap_id(data["object"])
365 end
366
367 test "it works for incoming announces with an existing activity" do
368 user = insert(:user)
369 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
370
371 data =
372 File.read!("test/fixtures/mastodon-announce.json")
373 |> Poison.decode!()
374 |> Map.put("object", activity.data["object"])
375
376 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
377
378 assert data["actor"] == "http://mastodon.example.org/users/admin"
379 assert data["type"] == "Announce"
380
381 assert data["id"] ==
382 "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
383
384 assert data["object"] == activity.data["object"]
385
386 assert Activity.get_create_by_object_ap_id(data["object"]).id == activity.id
387 end
388
389 test "it does not clobber the addressing on announce activities" do
390 user = insert(:user)
391 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
392
393 data =
394 File.read!("test/fixtures/mastodon-announce.json")
395 |> Poison.decode!()
396 |> Map.put("object", Object.normalize(activity).data["id"])
397 |> Map.put("to", ["http://mastodon.example.org/users/admin/followers"])
398 |> Map.put("cc", [])
399
400 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
401
402 assert data["to"] == ["http://mastodon.example.org/users/admin/followers"]
403 end
404
405 test "it ensures that as:Public activities make it to their followers collection" do
406 user = insert(:user)
407
408 data =
409 File.read!("test/fixtures/mastodon-post-activity.json")
410 |> Poison.decode!()
411 |> Map.put("actor", user.ap_id)
412 |> Map.put("to", ["https://www.w3.org/ns/activitystreams#Public"])
413 |> Map.put("cc", [])
414
415 object =
416 data["object"]
417 |> Map.put("attributedTo", user.ap_id)
418 |> Map.put("to", ["https://www.w3.org/ns/activitystreams#Public"])
419 |> Map.put("cc", [])
420 |> Map.put("id", user.ap_id <> "/activities/12345678")
421
422 data = Map.put(data, "object", object)
423
424 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
425
426 assert data["cc"] == [User.ap_followers(user)]
427 end
428
429 test "it ensures that address fields become lists" do
430 user = insert(:user)
431
432 data =
433 File.read!("test/fixtures/mastodon-post-activity.json")
434 |> Poison.decode!()
435 |> Map.put("actor", user.ap_id)
436 |> Map.put("to", nil)
437 |> Map.put("cc", nil)
438
439 object =
440 data["object"]
441 |> Map.put("attributedTo", user.ap_id)
442 |> Map.put("to", nil)
443 |> Map.put("cc", nil)
444 |> Map.put("id", user.ap_id <> "/activities/12345678")
445
446 data = Map.put(data, "object", object)
447
448 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
449
450 assert !is_nil(data["to"])
451 assert !is_nil(data["cc"])
452 end
453
454 test "it works for incoming update activities" do
455 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
456
457 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
458 update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
459
460 object =
461 update_data["object"]
462 |> Map.put("actor", data["actor"])
463 |> Map.put("id", data["actor"])
464
465 update_data =
466 update_data
467 |> Map.put("actor", data["actor"])
468 |> Map.put("object", object)
469
470 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
471
472 user = User.get_cached_by_ap_id(data["actor"])
473 assert user.name == "gargle"
474
475 assert user.avatar["url"] == [
476 %{
477 "href" =>
478 "https://cd.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
479 }
480 ]
481
482 assert user.info.banner["url"] == [
483 %{
484 "href" =>
485 "https://cd.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
486 }
487 ]
488
489 assert user.bio == "<p>Some bio</p>"
490 end
491
492 test "it works for incoming update activities which lock the account" do
493 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
494
495 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
496 update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
497
498 object =
499 update_data["object"]
500 |> Map.put("actor", data["actor"])
501 |> Map.put("id", data["actor"])
502 |> Map.put("manuallyApprovesFollowers", true)
503
504 update_data =
505 update_data
506 |> Map.put("actor", data["actor"])
507 |> Map.put("object", object)
508
509 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
510
511 user = User.get_cached_by_ap_id(data["actor"])
512 assert user.info.locked == true
513 end
514
515 test "it works for incoming deletes" do
516 activity = insert(:note_activity)
517
518 data =
519 File.read!("test/fixtures/mastodon-delete.json")
520 |> Poison.decode!()
521
522 object =
523 data["object"]
524 |> Map.put("id", activity.data["object"])
525
526 data =
527 data
528 |> Map.put("object", object)
529 |> Map.put("actor", activity.data["actor"])
530
531 {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data)
532
533 refute Activity.get_by_id(activity.id)
534 end
535
536 test "it fails for incoming deletes with spoofed origin" do
537 activity = insert(:note_activity)
538
539 data =
540 File.read!("test/fixtures/mastodon-delete.json")
541 |> Poison.decode!()
542
543 object =
544 data["object"]
545 |> Map.put("id", activity.data["object"])
546
547 data =
548 data
549 |> Map.put("object", object)
550
551 assert capture_log(fn ->
552 :error = Transmogrifier.handle_incoming(data)
553 end) =~
554 "[error] Could not decode user at fetch http://mastodon.example.org/users/gargron, {:error, {:error, :nxdomain}}"
555
556 assert Activity.get_by_id(activity.id)
557 end
558
559 test "it works for incoming user deletes" do
560 %{ap_id: ap_id} = insert(:user, ap_id: "http://mastodon.example.org/users/admin")
561
562 data =
563 File.read!("test/fixtures/mastodon-delete-user.json")
564 |> Poison.decode!()
565
566 {:ok, _} = Transmogrifier.handle_incoming(data)
567 ObanHelpers.perform_all()
568
569 refute User.get_cached_by_ap_id(ap_id)
570 end
571
572 test "it fails for incoming user deletes with spoofed origin" do
573 %{ap_id: ap_id} = insert(:user)
574
575 data =
576 File.read!("test/fixtures/mastodon-delete-user.json")
577 |> Poison.decode!()
578 |> Map.put("actor", ap_id)
579
580 assert :error == Transmogrifier.handle_incoming(data)
581 assert User.get_cached_by_ap_id(ap_id)
582 end
583
584 test "it works for incoming unannounces with an existing notice" do
585 user = insert(:user)
586 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
587
588 announce_data =
589 File.read!("test/fixtures/mastodon-announce.json")
590 |> Poison.decode!()
591 |> Map.put("object", activity.data["object"])
592
593 {:ok, %Activity{data: announce_data, local: false}} =
594 Transmogrifier.handle_incoming(announce_data)
595
596 data =
597 File.read!("test/fixtures/mastodon-undo-announce.json")
598 |> Poison.decode!()
599 |> Map.put("object", announce_data)
600 |> Map.put("actor", announce_data["actor"])
601
602 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
603
604 assert data["type"] == "Undo"
605 assert object_data = data["object"]
606 assert object_data["type"] == "Announce"
607 assert object_data["object"] == activity.data["object"]
608
609 assert object_data["id"] ==
610 "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
611 end
612
613 test "it works for incomming unfollows with an existing follow" do
614 user = insert(:user)
615
616 follow_data =
617 File.read!("test/fixtures/mastodon-follow-activity.json")
618 |> Poison.decode!()
619 |> Map.put("object", user.ap_id)
620
621 {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(follow_data)
622
623 data =
624 File.read!("test/fixtures/mastodon-unfollow-activity.json")
625 |> Poison.decode!()
626 |> Map.put("object", follow_data)
627
628 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
629
630 assert data["type"] == "Undo"
631 assert data["object"]["type"] == "Follow"
632 assert data["object"]["object"] == user.ap_id
633 assert data["actor"] == "http://mastodon.example.org/users/admin"
634
635 refute User.following?(User.get_cached_by_ap_id(data["actor"]), user)
636 end
637
638 test "it works for incoming blocks" do
639 user = insert(:user)
640
641 data =
642 File.read!("test/fixtures/mastodon-block-activity.json")
643 |> Poison.decode!()
644 |> Map.put("object", user.ap_id)
645
646 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
647
648 assert data["type"] == "Block"
649 assert data["object"] == user.ap_id
650 assert data["actor"] == "http://mastodon.example.org/users/admin"
651
652 blocker = User.get_cached_by_ap_id(data["actor"])
653
654 assert User.blocks?(blocker, user)
655 end
656
657 test "incoming blocks successfully tear down any follow relationship" do
658 blocker = insert(:user)
659 blocked = insert(:user)
660
661 data =
662 File.read!("test/fixtures/mastodon-block-activity.json")
663 |> Poison.decode!()
664 |> Map.put("object", blocked.ap_id)
665 |> Map.put("actor", blocker.ap_id)
666
667 {:ok, blocker} = User.follow(blocker, blocked)
668 {:ok, blocked} = User.follow(blocked, blocker)
669
670 assert User.following?(blocker, blocked)
671 assert User.following?(blocked, blocker)
672
673 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
674
675 assert data["type"] == "Block"
676 assert data["object"] == blocked.ap_id
677 assert data["actor"] == blocker.ap_id
678
679 blocker = User.get_cached_by_ap_id(data["actor"])
680 blocked = User.get_cached_by_ap_id(data["object"])
681
682 assert User.blocks?(blocker, blocked)
683
684 refute User.following?(blocker, blocked)
685 refute User.following?(blocked, blocker)
686 end
687
688 test "it works for incoming unblocks with an existing block" do
689 user = insert(:user)
690
691 block_data =
692 File.read!("test/fixtures/mastodon-block-activity.json")
693 |> Poison.decode!()
694 |> Map.put("object", user.ap_id)
695
696 {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(block_data)
697
698 data =
699 File.read!("test/fixtures/mastodon-unblock-activity.json")
700 |> Poison.decode!()
701 |> Map.put("object", block_data)
702
703 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
704 assert data["type"] == "Undo"
705 assert data["object"]["type"] == "Block"
706 assert data["object"]["object"] == user.ap_id
707 assert data["actor"] == "http://mastodon.example.org/users/admin"
708
709 blocker = User.get_cached_by_ap_id(data["actor"])
710
711 refute User.blocks?(blocker, user)
712 end
713
714 test "it works for incoming accepts which were pre-accepted" do
715 follower = insert(:user)
716 followed = insert(:user)
717
718 {:ok, follower} = User.follow(follower, followed)
719 assert User.following?(follower, followed) == true
720
721 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
722
723 accept_data =
724 File.read!("test/fixtures/mastodon-accept-activity.json")
725 |> Poison.decode!()
726 |> Map.put("actor", followed.ap_id)
727
728 object =
729 accept_data["object"]
730 |> Map.put("actor", follower.ap_id)
731 |> Map.put("id", follow_activity.data["id"])
732
733 accept_data = Map.put(accept_data, "object", object)
734
735 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
736 refute activity.local
737
738 assert activity.data["object"] == follow_activity.data["id"]
739
740 follower = User.get_cached_by_id(follower.id)
741
742 assert User.following?(follower, followed) == true
743 end
744
745 test "it works for incoming accepts which were orphaned" do
746 follower = insert(:user)
747 followed = insert(:user, %{info: %User.Info{locked: true}})
748
749 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
750
751 accept_data =
752 File.read!("test/fixtures/mastodon-accept-activity.json")
753 |> Poison.decode!()
754 |> Map.put("actor", followed.ap_id)
755
756 accept_data =
757 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
758
759 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
760 assert activity.data["object"] == follow_activity.data["id"]
761
762 follower = User.get_cached_by_id(follower.id)
763
764 assert User.following?(follower, followed) == true
765 end
766
767 test "it works for incoming accepts which are referenced by IRI only" do
768 follower = insert(:user)
769 followed = insert(:user, %{info: %User.Info{locked: true}})
770
771 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
772
773 accept_data =
774 File.read!("test/fixtures/mastodon-accept-activity.json")
775 |> Poison.decode!()
776 |> Map.put("actor", followed.ap_id)
777 |> Map.put("object", follow_activity.data["id"])
778
779 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
780 assert activity.data["object"] == follow_activity.data["id"]
781
782 follower = User.get_cached_by_id(follower.id)
783
784 assert User.following?(follower, followed) == true
785 end
786
787 test "it fails for incoming accepts which cannot be correlated" do
788 follower = insert(:user)
789 followed = insert(:user, %{info: %User.Info{locked: true}})
790
791 accept_data =
792 File.read!("test/fixtures/mastodon-accept-activity.json")
793 |> Poison.decode!()
794 |> Map.put("actor", followed.ap_id)
795
796 accept_data =
797 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
798
799 :error = Transmogrifier.handle_incoming(accept_data)
800
801 follower = User.get_cached_by_id(follower.id)
802
803 refute User.following?(follower, followed) == true
804 end
805
806 test "it fails for incoming rejects which cannot be correlated" do
807 follower = insert(:user)
808 followed = insert(:user, %{info: %User.Info{locked: true}})
809
810 accept_data =
811 File.read!("test/fixtures/mastodon-reject-activity.json")
812 |> Poison.decode!()
813 |> Map.put("actor", followed.ap_id)
814
815 accept_data =
816 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
817
818 :error = Transmogrifier.handle_incoming(accept_data)
819
820 follower = User.get_cached_by_id(follower.id)
821
822 refute User.following?(follower, followed) == true
823 end
824
825 test "it works for incoming rejects which are orphaned" do
826 follower = insert(:user)
827 followed = insert(:user, %{info: %User.Info{locked: true}})
828
829 {:ok, follower} = User.follow(follower, followed)
830 {:ok, _follow_activity} = ActivityPub.follow(follower, followed)
831
832 assert User.following?(follower, followed) == true
833
834 reject_data =
835 File.read!("test/fixtures/mastodon-reject-activity.json")
836 |> Poison.decode!()
837 |> Map.put("actor", followed.ap_id)
838
839 reject_data =
840 Map.put(reject_data, "object", Map.put(reject_data["object"], "actor", follower.ap_id))
841
842 {:ok, activity} = Transmogrifier.handle_incoming(reject_data)
843 refute activity.local
844
845 follower = User.get_cached_by_id(follower.id)
846
847 assert User.following?(follower, followed) == false
848 end
849
850 test "it works for incoming rejects which are referenced by IRI only" do
851 follower = insert(:user)
852 followed = insert(:user, %{info: %User.Info{locked: true}})
853
854 {:ok, follower} = User.follow(follower, followed)
855 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
856
857 assert User.following?(follower, followed) == true
858
859 reject_data =
860 File.read!("test/fixtures/mastodon-reject-activity.json")
861 |> Poison.decode!()
862 |> Map.put("actor", followed.ap_id)
863 |> Map.put("object", follow_activity.data["id"])
864
865 {:ok, %Activity{data: _}} = Transmogrifier.handle_incoming(reject_data)
866
867 follower = User.get_cached_by_id(follower.id)
868
869 assert User.following?(follower, followed) == false
870 end
871
872 test "it rejects activities without a valid ID" do
873 user = insert(:user)
874
875 data =
876 File.read!("test/fixtures/mastodon-follow-activity.json")
877 |> Poison.decode!()
878 |> Map.put("object", user.ap_id)
879 |> Map.put("id", "")
880
881 :error = Transmogrifier.handle_incoming(data)
882 end
883
884 test "it remaps video URLs as attachments if necessary" do
885 {:ok, object} =
886 Fetcher.fetch_object_from_id(
887 "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
888 )
889
890 attachment = %{
891 "type" => "Link",
892 "mediaType" => "video/mp4",
893 "href" =>
894 "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
895 "mimeType" => "video/mp4",
896 "size" => 5_015_880,
897 "url" => [
898 %{
899 "href" =>
900 "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
901 "mediaType" => "video/mp4",
902 "type" => "Link"
903 }
904 ],
905 "width" => 480
906 }
907
908 assert object.data["url"] ==
909 "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
910
911 assert object.data["attachment"] == [attachment]
912 end
913
914 test "it accepts Flag activities" do
915 user = insert(:user)
916 other_user = insert(:user)
917
918 {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"})
919 object = Object.normalize(activity)
920
921 message = %{
922 "@context" => "https://www.w3.org/ns/activitystreams",
923 "cc" => [user.ap_id],
924 "object" => [user.ap_id, object.data["id"]],
925 "type" => "Flag",
926 "content" => "blocked AND reported!!!",
927 "actor" => other_user.ap_id
928 }
929
930 assert {:ok, activity} = Transmogrifier.handle_incoming(message)
931
932 assert activity.data["object"] == [user.ap_id, object.data["id"]]
933 assert activity.data["content"] == "blocked AND reported!!!"
934 assert activity.data["actor"] == other_user.ap_id
935 assert activity.data["cc"] == [user.ap_id]
936 end
937 end
938
939 describe "prepare outgoing" do
940 test "it turns mentions into tags" do
941 user = insert(:user)
942 other_user = insert(:user)
943
944 {:ok, activity} =
945 CommonAPI.post(user, %{"status" => "hey, @#{other_user.nickname}, how are ya? #2hu"})
946
947 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
948 object = modified["object"]
949
950 expected_mention = %{
951 "href" => other_user.ap_id,
952 "name" => "@#{other_user.nickname}",
953 "type" => "Mention"
954 }
955
956 expected_tag = %{
957 "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
958 "type" => "Hashtag",
959 "name" => "#2hu"
960 }
961
962 assert Enum.member?(object["tag"], expected_tag)
963 assert Enum.member?(object["tag"], expected_mention)
964 end
965
966 test "it adds the sensitive property" do
967 user = insert(:user)
968
969 {:ok, activity} = CommonAPI.post(user, %{"status" => "#nsfw hey"})
970 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
971
972 assert modified["object"]["sensitive"]
973 end
974
975 test "it adds the json-ld context and the conversation property" do
976 user = insert(:user)
977
978 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
979 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
980
981 assert modified["@context"] ==
982 Pleroma.Web.ActivityPub.Utils.make_json_ld_header()["@context"]
983
984 assert modified["object"]["conversation"] == modified["context"]
985 end
986
987 test "it sets the 'attributedTo' property to the actor of the object if it doesn't have one" do
988 user = insert(:user)
989
990 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
991 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
992
993 assert modified["object"]["actor"] == modified["object"]["attributedTo"]
994 end
995
996 test "it translates ostatus IDs to external URLs" do
997 incoming = File.read!("test/fixtures/incoming_note_activity.xml")
998 {:ok, [referent_activity]} = OStatus.handle_incoming(incoming)
999
1000 user = insert(:user)
1001
1002 {:ok, activity, _} = CommonAPI.favorite(referent_activity.id, user)
1003 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1004
1005 assert modified["object"] == "http://gs.example.org:4040/index.php/notice/29"
1006 end
1007
1008 test "it translates ostatus reply_to IDs to external URLs" do
1009 incoming = File.read!("test/fixtures/incoming_note_activity.xml")
1010 {:ok, [referred_activity]} = OStatus.handle_incoming(incoming)
1011
1012 user = insert(:user)
1013
1014 {:ok, activity} =
1015 CommonAPI.post(user, %{"status" => "HI!", "in_reply_to_status_id" => referred_activity.id})
1016
1017 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1018
1019 assert modified["object"]["inReplyTo"] == "http://gs.example.org:4040/index.php/notice/29"
1020 end
1021
1022 test "it strips internal hashtag data" do
1023 user = insert(:user)
1024
1025 {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu"})
1026
1027 expected_tag = %{
1028 "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
1029 "type" => "Hashtag",
1030 "name" => "#2hu"
1031 }
1032
1033 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1034
1035 assert modified["object"]["tag"] == [expected_tag]
1036 end
1037
1038 test "it strips internal fields" do
1039 user = insert(:user)
1040
1041 {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu :firefox:"})
1042
1043 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1044
1045 assert length(modified["object"]["tag"]) == 2
1046
1047 assert is_nil(modified["object"]["emoji"])
1048 assert is_nil(modified["object"]["like_count"])
1049 assert is_nil(modified["object"]["announcements"])
1050 assert is_nil(modified["object"]["announcement_count"])
1051 assert is_nil(modified["object"]["context_id"])
1052 end
1053
1054 test "it strips internal fields of article" do
1055 activity = insert(:article_activity)
1056
1057 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1058
1059 assert length(modified["object"]["tag"]) == 2
1060
1061 assert is_nil(modified["object"]["emoji"])
1062 assert is_nil(modified["object"]["like_count"])
1063 assert is_nil(modified["object"]["announcements"])
1064 assert is_nil(modified["object"]["announcement_count"])
1065 assert is_nil(modified["object"]["context_id"])
1066 end
1067
1068 test "it adds like collection to object" do
1069 activity = insert(:note_activity)
1070 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1071
1072 assert modified["object"]["likes"]["type"] == "OrderedCollection"
1073 assert modified["object"]["likes"]["totalItems"] == 0
1074 end
1075
1076 test "the directMessage flag is present" do
1077 user = insert(:user)
1078 other_user = insert(:user)
1079
1080 {:ok, activity} = CommonAPI.post(user, %{"status" => "2hu :moominmamma:"})
1081
1082 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1083
1084 assert modified["directMessage"] == false
1085
1086 {:ok, activity} =
1087 CommonAPI.post(user, %{"status" => "@#{other_user.nickname} :moominmamma:"})
1088
1089 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1090
1091 assert modified["directMessage"] == false
1092
1093 {:ok, activity} =
1094 CommonAPI.post(user, %{
1095 "status" => "@#{other_user.nickname} :moominmamma:",
1096 "visibility" => "direct"
1097 })
1098
1099 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1100
1101 assert modified["directMessage"] == true
1102 end
1103
1104 test "it strips BCC field" do
1105 user = insert(:user)
1106 {:ok, list} = Pleroma.List.create("foo", user)
1107
1108 {:ok, activity} =
1109 CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"})
1110
1111 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1112
1113 assert is_nil(modified["bcc"])
1114 end
1115 end
1116
1117 describe "user upgrade" do
1118 test "it upgrades a user to activitypub" do
1119 user =
1120 insert(:user, %{
1121 nickname: "rye@niu.moe",
1122 local: false,
1123 ap_id: "https://niu.moe/users/rye",
1124 follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"})
1125 })
1126
1127 user_two = insert(:user, %{following: [user.follower_address]})
1128
1129 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
1130 {:ok, unrelated_activity} = CommonAPI.post(user_two, %{"status" => "test"})
1131 assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients
1132
1133 user = User.get_cached_by_id(user.id)
1134 assert user.info.note_count == 1
1135
1136 {:ok, user} = Transmogrifier.upgrade_user_from_ap_id("https://niu.moe/users/rye")
1137 ObanHelpers.perform_all()
1138
1139 assert user.info.ap_enabled
1140 assert user.info.note_count == 1
1141 assert user.follower_address == "https://niu.moe/users/rye/followers"
1142 assert user.following_address == "https://niu.moe/users/rye/following"
1143
1144 user = User.get_cached_by_id(user.id)
1145 assert user.info.note_count == 1
1146
1147 activity = Activity.get_by_id(activity.id)
1148 assert user.follower_address in activity.recipients
1149
1150 assert %{
1151 "url" => [
1152 %{
1153 "href" =>
1154 "https://cdn.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
1155 }
1156 ]
1157 } = user.avatar
1158
1159 assert %{
1160 "url" => [
1161 %{
1162 "href" =>
1163 "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
1164 }
1165 ]
1166 } = user.info.banner
1167
1168 refute "..." in activity.recipients
1169
1170 unrelated_activity = Activity.get_by_id(unrelated_activity.id)
1171 refute user.follower_address in unrelated_activity.recipients
1172
1173 user_two = User.get_cached_by_id(user_two.id)
1174 assert user.follower_address in user_two.following
1175 refute "..." in user_two.following
1176 end
1177 end
1178
1179 describe "maybe_retire_websub" do
1180 test "it deletes all websub client subscripitions with the user as topic" do
1181 subscription = %WebsubClientSubscription{topic: "https://niu.moe/users/rye.atom"}
1182 {:ok, ws} = Repo.insert(subscription)
1183
1184 subscription = %WebsubClientSubscription{topic: "https://niu.moe/users/pasty.atom"}
1185 {:ok, ws2} = Repo.insert(subscription)
1186
1187 Transmogrifier.maybe_retire_websub("https://niu.moe/users/rye")
1188
1189 refute Repo.get(WebsubClientSubscription, ws.id)
1190 assert Repo.get(WebsubClientSubscription, ws2.id)
1191 end
1192 end
1193
1194 describe "actor rewriting" do
1195 test "it fixes the actor URL property to be a proper URI" do
1196 data = %{
1197 "url" => %{"href" => "http://example.com"}
1198 }
1199
1200 rewritten = Transmogrifier.maybe_fix_user_object(data)
1201 assert rewritten["url"] == "http://example.com"
1202 end
1203 end
1204
1205 describe "actor origin containment" do
1206 test "it rejects activities which reference objects with bogus origins" do
1207 data = %{
1208 "@context" => "https://www.w3.org/ns/activitystreams",
1209 "id" => "http://mastodon.example.org/users/admin/activities/1234",
1210 "actor" => "http://mastodon.example.org/users/admin",
1211 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1212 "object" => "https://info.pleroma.site/activity.json",
1213 "type" => "Announce"
1214 }
1215
1216 :error = Transmogrifier.handle_incoming(data)
1217 end
1218
1219 test "it rejects activities which reference objects that have an incorrect attribution (variant 1)" do
1220 data = %{
1221 "@context" => "https://www.w3.org/ns/activitystreams",
1222 "id" => "http://mastodon.example.org/users/admin/activities/1234",
1223 "actor" => "http://mastodon.example.org/users/admin",
1224 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1225 "object" => "https://info.pleroma.site/activity2.json",
1226 "type" => "Announce"
1227 }
1228
1229 :error = Transmogrifier.handle_incoming(data)
1230 end
1231
1232 test "it rejects activities which reference objects that have an incorrect attribution (variant 2)" do
1233 data = %{
1234 "@context" => "https://www.w3.org/ns/activitystreams",
1235 "id" => "http://mastodon.example.org/users/admin/activities/1234",
1236 "actor" => "http://mastodon.example.org/users/admin",
1237 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1238 "object" => "https://info.pleroma.site/activity3.json",
1239 "type" => "Announce"
1240 }
1241
1242 :error = Transmogrifier.handle_incoming(data)
1243 end
1244 end
1245
1246 describe "reserialization" do
1247 test "successfully reserializes a message with inReplyTo == nil" do
1248 user = insert(:user)
1249
1250 message = %{
1251 "@context" => "https://www.w3.org/ns/activitystreams",
1252 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1253 "cc" => [],
1254 "type" => "Create",
1255 "object" => %{
1256 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1257 "cc" => [],
1258 "type" => "Note",
1259 "content" => "Hi",
1260 "inReplyTo" => nil,
1261 "attributedTo" => user.ap_id
1262 },
1263 "actor" => user.ap_id
1264 }
1265
1266 {:ok, activity} = Transmogrifier.handle_incoming(message)
1267
1268 {:ok, _} = Transmogrifier.prepare_outgoing(activity.data)
1269 end
1270
1271 test "successfully reserializes a message with AS2 objects in IR" do
1272 user = insert(:user)
1273
1274 message = %{
1275 "@context" => "https://www.w3.org/ns/activitystreams",
1276 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1277 "cc" => [],
1278 "type" => "Create",
1279 "object" => %{
1280 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1281 "cc" => [],
1282 "type" => "Note",
1283 "content" => "Hi",
1284 "inReplyTo" => nil,
1285 "attributedTo" => user.ap_id,
1286 "tag" => [
1287 %{"name" => "#2hu", "href" => "http://example.com/2hu", "type" => "Hashtag"},
1288 %{"name" => "Bob", "href" => "http://example.com/bob", "type" => "Mention"}
1289 ]
1290 },
1291 "actor" => user.ap_id
1292 }
1293
1294 {:ok, activity} = Transmogrifier.handle_incoming(message)
1295
1296 {:ok, _} = Transmogrifier.prepare_outgoing(activity.data)
1297 end
1298 end
1299
1300 test "Rewrites Answers to Notes" do
1301 user = insert(:user)
1302
1303 {:ok, poll_activity} =
1304 CommonAPI.post(user, %{
1305 "status" => "suya...",
1306 "poll" => %{"options" => ["suya", "suya.", "suya.."], "expires_in" => 10}
1307 })
1308
1309 poll_object = Object.normalize(poll_activity)
1310 # TODO: Replace with CommonAPI vote creation when implemented
1311 data =
1312 File.read!("test/fixtures/mastodon-vote.json")
1313 |> Poison.decode!()
1314 |> Kernel.put_in(["to"], user.ap_id)
1315 |> Kernel.put_in(["object", "inReplyTo"], poll_object.data["id"])
1316 |> Kernel.put_in(["object", "to"], user.ap_id)
1317
1318 {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data)
1319 {:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
1320
1321 assert data["object"]["type"] == "Note"
1322 end
1323
1324 describe "fix_explicit_addressing" do
1325 setup do
1326 user = insert(:user)
1327 [user: user]
1328 end
1329
1330 test "moves non-explicitly mentioned actors to cc", %{user: user} do
1331 explicitly_mentioned_actors = [
1332 "https://pleroma.gold/users/user1",
1333 "https://pleroma.gold/user2"
1334 ]
1335
1336 object = %{
1337 "actor" => user.ap_id,
1338 "to" => explicitly_mentioned_actors ++ ["https://social.beepboop.ga/users/dirb"],
1339 "cc" => [],
1340 "tag" =>
1341 Enum.map(explicitly_mentioned_actors, fn href ->
1342 %{"type" => "Mention", "href" => href}
1343 end)
1344 }
1345
1346 fixed_object = Transmogrifier.fix_explicit_addressing(object)
1347 assert Enum.all?(explicitly_mentioned_actors, &(&1 in fixed_object["to"]))
1348 refute "https://social.beepboop.ga/users/dirb" in fixed_object["to"]
1349 assert "https://social.beepboop.ga/users/dirb" in fixed_object["cc"]
1350 end
1351
1352 test "does not move actor's follower collection to cc", %{user: user} do
1353 object = %{
1354 "actor" => user.ap_id,
1355 "to" => [user.follower_address],
1356 "cc" => []
1357 }
1358
1359 fixed_object = Transmogrifier.fix_explicit_addressing(object)
1360 assert user.follower_address in fixed_object["to"]
1361 refute user.follower_address in fixed_object["cc"]
1362 end
1363
1364 test "removes recipient's follower collection from cc", %{user: user} do
1365 recipient = insert(:user)
1366
1367 object = %{
1368 "actor" => user.ap_id,
1369 "to" => [recipient.ap_id, "https://www.w3.org/ns/activitystreams#Public"],
1370 "cc" => [user.follower_address, recipient.follower_address]
1371 }
1372
1373 fixed_object = Transmogrifier.fix_explicit_addressing(object)
1374
1375 assert user.follower_address in fixed_object["cc"]
1376 refute recipient.follower_address in fixed_object["cc"]
1377 refute recipient.follower_address in fixed_object["to"]
1378 end
1379 end
1380 end