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