7e2c8769d2132e47d54f495a92eea84f3a0882d8
[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 with custom profile fields" do
513 {:ok, activity} =
514 "test/fixtures/mastodon-post-activity.json"
515 |> File.read!()
516 |> Poison.decode!()
517 |> Transmogrifier.handle_incoming()
518
519 user = User.get_cached_by_ap_id(activity.actor)
520
521 assert User.Info.fields(user.info) == [
522 %{"name" => "foo", "value" => "bar"},
523 %{"name" => "foo1", "value" => "bar1"}
524 ]
525
526 update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
527
528 object =
529 update_data["object"]
530 |> Map.put("actor", user.ap_id)
531 |> Map.put("id", user.ap_id)
532
533 update_data =
534 update_data
535 |> Map.put("actor", user.ap_id)
536 |> Map.put("object", object)
537
538 {:ok, _update_activity} = Transmogrifier.handle_incoming(update_data)
539
540 user = User.get_cached_by_ap_id(user.ap_id)
541
542 assert User.Info.fields(user.info) == [
543 %{"name" => "foo", "value" => "updated"},
544 %{"name" => "foo1", "value" => "updated"}
545 ]
546 end
547
548 test "it works for incoming update activities which lock the account" do
549 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
550
551 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
552 update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
553
554 object =
555 update_data["object"]
556 |> Map.put("actor", data["actor"])
557 |> Map.put("id", data["actor"])
558 |> Map.put("manuallyApprovesFollowers", true)
559
560 update_data =
561 update_data
562 |> Map.put("actor", data["actor"])
563 |> Map.put("object", object)
564
565 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
566
567 user = User.get_cached_by_ap_id(data["actor"])
568 assert user.info.locked == true
569 end
570
571 test "it works for incoming deletes" do
572 activity = insert(:note_activity)
573
574 data =
575 File.read!("test/fixtures/mastodon-delete.json")
576 |> Poison.decode!()
577
578 object =
579 data["object"]
580 |> Map.put("id", activity.data["object"])
581
582 data =
583 data
584 |> Map.put("object", object)
585 |> Map.put("actor", activity.data["actor"])
586
587 {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data)
588
589 refute Activity.get_by_id(activity.id)
590 end
591
592 test "it fails for incoming deletes with spoofed origin" do
593 activity = insert(:note_activity)
594
595 data =
596 File.read!("test/fixtures/mastodon-delete.json")
597 |> Poison.decode!()
598
599 object =
600 data["object"]
601 |> Map.put("id", activity.data["object"])
602
603 data =
604 data
605 |> Map.put("object", object)
606
607 assert capture_log(fn ->
608 :error = Transmogrifier.handle_incoming(data)
609 end) =~
610 "[error] Could not decode user at fetch http://mastodon.example.org/users/gargron, {:error, {:error, :nxdomain}}"
611
612 assert Activity.get_by_id(activity.id)
613 end
614
615 test "it works for incoming user deletes" do
616 %{ap_id: ap_id} = insert(:user, ap_id: "http://mastodon.example.org/users/admin")
617
618 data =
619 File.read!("test/fixtures/mastodon-delete-user.json")
620 |> Poison.decode!()
621
622 {:ok, _} = Transmogrifier.handle_incoming(data)
623
624 refute User.get_cached_by_ap_id(ap_id)
625 end
626
627 test "it fails for incoming user deletes with spoofed origin" do
628 %{ap_id: ap_id} = insert(:user)
629
630 data =
631 File.read!("test/fixtures/mastodon-delete-user.json")
632 |> Poison.decode!()
633 |> Map.put("actor", ap_id)
634
635 assert :error == Transmogrifier.handle_incoming(data)
636 assert User.get_cached_by_ap_id(ap_id)
637 end
638
639 test "it works for incoming unannounces with an existing notice" do
640 user = insert(:user)
641 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
642
643 announce_data =
644 File.read!("test/fixtures/mastodon-announce.json")
645 |> Poison.decode!()
646 |> Map.put("object", activity.data["object"])
647
648 {:ok, %Activity{data: announce_data, local: false}} =
649 Transmogrifier.handle_incoming(announce_data)
650
651 data =
652 File.read!("test/fixtures/mastodon-undo-announce.json")
653 |> Poison.decode!()
654 |> Map.put("object", announce_data)
655 |> Map.put("actor", announce_data["actor"])
656
657 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
658
659 assert data["type"] == "Undo"
660 assert object_data = data["object"]
661 assert object_data["type"] == "Announce"
662 assert object_data["object"] == activity.data["object"]
663
664 assert object_data["id"] ==
665 "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
666 end
667
668 test "it works for incomming unfollows with an existing follow" do
669 user = insert(:user)
670
671 follow_data =
672 File.read!("test/fixtures/mastodon-follow-activity.json")
673 |> Poison.decode!()
674 |> Map.put("object", user.ap_id)
675
676 {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(follow_data)
677
678 data =
679 File.read!("test/fixtures/mastodon-unfollow-activity.json")
680 |> Poison.decode!()
681 |> Map.put("object", follow_data)
682
683 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
684
685 assert data["type"] == "Undo"
686 assert data["object"]["type"] == "Follow"
687 assert data["object"]["object"] == user.ap_id
688 assert data["actor"] == "http://mastodon.example.org/users/admin"
689
690 refute User.following?(User.get_cached_by_ap_id(data["actor"]), user)
691 end
692
693 test "it works for incoming blocks" do
694 user = insert(:user)
695
696 data =
697 File.read!("test/fixtures/mastodon-block-activity.json")
698 |> Poison.decode!()
699 |> Map.put("object", user.ap_id)
700
701 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
702
703 assert data["type"] == "Block"
704 assert data["object"] == user.ap_id
705 assert data["actor"] == "http://mastodon.example.org/users/admin"
706
707 blocker = User.get_cached_by_ap_id(data["actor"])
708
709 assert User.blocks?(blocker, user)
710 end
711
712 test "incoming blocks successfully tear down any follow relationship" do
713 blocker = insert(:user)
714 blocked = insert(:user)
715
716 data =
717 File.read!("test/fixtures/mastodon-block-activity.json")
718 |> Poison.decode!()
719 |> Map.put("object", blocked.ap_id)
720 |> Map.put("actor", blocker.ap_id)
721
722 {:ok, blocker} = User.follow(blocker, blocked)
723 {:ok, blocked} = User.follow(blocked, blocker)
724
725 assert User.following?(blocker, blocked)
726 assert User.following?(blocked, blocker)
727
728 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
729
730 assert data["type"] == "Block"
731 assert data["object"] == blocked.ap_id
732 assert data["actor"] == blocker.ap_id
733
734 blocker = User.get_cached_by_ap_id(data["actor"])
735 blocked = User.get_cached_by_ap_id(data["object"])
736
737 assert User.blocks?(blocker, blocked)
738
739 refute User.following?(blocker, blocked)
740 refute User.following?(blocked, blocker)
741 end
742
743 test "it works for incoming unblocks with an existing block" do
744 user = insert(:user)
745
746 block_data =
747 File.read!("test/fixtures/mastodon-block-activity.json")
748 |> Poison.decode!()
749 |> Map.put("object", user.ap_id)
750
751 {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(block_data)
752
753 data =
754 File.read!("test/fixtures/mastodon-unblock-activity.json")
755 |> Poison.decode!()
756 |> Map.put("object", block_data)
757
758 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
759 assert data["type"] == "Undo"
760 assert data["object"]["type"] == "Block"
761 assert data["object"]["object"] == user.ap_id
762 assert data["actor"] == "http://mastodon.example.org/users/admin"
763
764 blocker = User.get_cached_by_ap_id(data["actor"])
765
766 refute User.blocks?(blocker, user)
767 end
768
769 test "it works for incoming accepts which were pre-accepted" do
770 follower = insert(:user)
771 followed = insert(:user)
772
773 {:ok, follower} = User.follow(follower, followed)
774 assert User.following?(follower, followed) == true
775
776 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
777
778 accept_data =
779 File.read!("test/fixtures/mastodon-accept-activity.json")
780 |> Poison.decode!()
781 |> Map.put("actor", followed.ap_id)
782
783 object =
784 accept_data["object"]
785 |> Map.put("actor", follower.ap_id)
786 |> Map.put("id", follow_activity.data["id"])
787
788 accept_data = Map.put(accept_data, "object", object)
789
790 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
791 refute activity.local
792
793 assert activity.data["object"] == follow_activity.data["id"]
794
795 follower = User.get_cached_by_id(follower.id)
796
797 assert User.following?(follower, followed) == true
798 end
799
800 test "it works for incoming accepts which were orphaned" do
801 follower = insert(:user)
802 followed = insert(:user, %{info: %User.Info{locked: true}})
803
804 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
805
806 accept_data =
807 File.read!("test/fixtures/mastodon-accept-activity.json")
808 |> Poison.decode!()
809 |> Map.put("actor", followed.ap_id)
810
811 accept_data =
812 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
813
814 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
815 assert activity.data["object"] == follow_activity.data["id"]
816
817 follower = User.get_cached_by_id(follower.id)
818
819 assert User.following?(follower, followed) == true
820 end
821
822 test "it works for incoming accepts which are referenced by IRI only" do
823 follower = insert(:user)
824 followed = insert(:user, %{info: %User.Info{locked: true}})
825
826 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
827
828 accept_data =
829 File.read!("test/fixtures/mastodon-accept-activity.json")
830 |> Poison.decode!()
831 |> Map.put("actor", followed.ap_id)
832 |> Map.put("object", follow_activity.data["id"])
833
834 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
835 assert activity.data["object"] == follow_activity.data["id"]
836
837 follower = User.get_cached_by_id(follower.id)
838
839 assert User.following?(follower, followed) == true
840 end
841
842 test "it fails for incoming accepts which cannot be correlated" do
843 follower = insert(:user)
844 followed = insert(:user, %{info: %User.Info{locked: true}})
845
846 accept_data =
847 File.read!("test/fixtures/mastodon-accept-activity.json")
848 |> Poison.decode!()
849 |> Map.put("actor", followed.ap_id)
850
851 accept_data =
852 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
853
854 :error = Transmogrifier.handle_incoming(accept_data)
855
856 follower = User.get_cached_by_id(follower.id)
857
858 refute User.following?(follower, followed) == true
859 end
860
861 test "it fails for incoming rejects which cannot be correlated" do
862 follower = insert(:user)
863 followed = insert(:user, %{info: %User.Info{locked: true}})
864
865 accept_data =
866 File.read!("test/fixtures/mastodon-reject-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 :error = Transmogrifier.handle_incoming(accept_data)
874
875 follower = User.get_cached_by_id(follower.id)
876
877 refute User.following?(follower, followed) == true
878 end
879
880 test "it works for incoming rejects which are orphaned" do
881 follower = insert(:user)
882 followed = insert(:user, %{info: %User.Info{locked: true}})
883
884 {:ok, follower} = User.follow(follower, followed)
885 {:ok, _follow_activity} = ActivityPub.follow(follower, followed)
886
887 assert User.following?(follower, followed) == true
888
889 reject_data =
890 File.read!("test/fixtures/mastodon-reject-activity.json")
891 |> Poison.decode!()
892 |> Map.put("actor", followed.ap_id)
893
894 reject_data =
895 Map.put(reject_data, "object", Map.put(reject_data["object"], "actor", follower.ap_id))
896
897 {:ok, activity} = Transmogrifier.handle_incoming(reject_data)
898 refute activity.local
899
900 follower = User.get_cached_by_id(follower.id)
901
902 assert User.following?(follower, followed) == false
903 end
904
905 test "it works for incoming rejects which are referenced by IRI only" do
906 follower = insert(:user)
907 followed = insert(:user, %{info: %User.Info{locked: true}})
908
909 {:ok, follower} = User.follow(follower, followed)
910 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
911
912 assert User.following?(follower, followed) == true
913
914 reject_data =
915 File.read!("test/fixtures/mastodon-reject-activity.json")
916 |> Poison.decode!()
917 |> Map.put("actor", followed.ap_id)
918 |> Map.put("object", follow_activity.data["id"])
919
920 {:ok, %Activity{data: _}} = Transmogrifier.handle_incoming(reject_data)
921
922 follower = User.get_cached_by_id(follower.id)
923
924 assert User.following?(follower, followed) == false
925 end
926
927 test "it rejects activities without a valid ID" do
928 user = insert(:user)
929
930 data =
931 File.read!("test/fixtures/mastodon-follow-activity.json")
932 |> Poison.decode!()
933 |> Map.put("object", user.ap_id)
934 |> Map.put("id", "")
935
936 :error = Transmogrifier.handle_incoming(data)
937 end
938
939 test "it remaps video URLs as attachments if necessary" do
940 {:ok, object} =
941 Fetcher.fetch_object_from_id(
942 "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
943 )
944
945 attachment = %{
946 "type" => "Link",
947 "mediaType" => "video/mp4",
948 "href" =>
949 "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
950 "mimeType" => "video/mp4",
951 "size" => 5_015_880,
952 "url" => [
953 %{
954 "href" =>
955 "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
956 "mediaType" => "video/mp4",
957 "type" => "Link"
958 }
959 ],
960 "width" => 480
961 }
962
963 assert object.data["url"] ==
964 "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
965
966 assert object.data["attachment"] == [attachment]
967 end
968
969 test "it accepts Flag activities" do
970 user = insert(:user)
971 other_user = insert(:user)
972
973 {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"})
974 object = Object.normalize(activity)
975
976 message = %{
977 "@context" => "https://www.w3.org/ns/activitystreams",
978 "cc" => [user.ap_id],
979 "object" => [user.ap_id, object.data["id"]],
980 "type" => "Flag",
981 "content" => "blocked AND reported!!!",
982 "actor" => other_user.ap_id
983 }
984
985 assert {:ok, activity} = Transmogrifier.handle_incoming(message)
986
987 assert activity.data["object"] == [user.ap_id, object.data["id"]]
988 assert activity.data["content"] == "blocked AND reported!!!"
989 assert activity.data["actor"] == other_user.ap_id
990 assert activity.data["cc"] == [user.ap_id]
991 end
992 end
993
994 describe "prepare outgoing" do
995 test "it turns mentions into tags" do
996 user = insert(:user)
997 other_user = insert(:user)
998
999 {:ok, activity} =
1000 CommonAPI.post(user, %{"status" => "hey, @#{other_user.nickname}, how are ya? #2hu"})
1001
1002 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1003 object = modified["object"]
1004
1005 expected_mention = %{
1006 "href" => other_user.ap_id,
1007 "name" => "@#{other_user.nickname}",
1008 "type" => "Mention"
1009 }
1010
1011 expected_tag = %{
1012 "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
1013 "type" => "Hashtag",
1014 "name" => "#2hu"
1015 }
1016
1017 assert Enum.member?(object["tag"], expected_tag)
1018 assert Enum.member?(object["tag"], expected_mention)
1019 end
1020
1021 test "it adds the sensitive property" do
1022 user = insert(:user)
1023
1024 {:ok, activity} = CommonAPI.post(user, %{"status" => "#nsfw hey"})
1025 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1026
1027 assert modified["object"]["sensitive"]
1028 end
1029
1030 test "it adds the json-ld context and the conversation property" do
1031 user = insert(:user)
1032
1033 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
1034 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1035
1036 assert modified["@context"] ==
1037 Pleroma.Web.ActivityPub.Utils.make_json_ld_header()["@context"]
1038
1039 assert modified["object"]["conversation"] == modified["context"]
1040 end
1041
1042 test "it sets the 'attributedTo' property to the actor of the object if it doesn't have one" do
1043 user = insert(:user)
1044
1045 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
1046 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1047
1048 assert modified["object"]["actor"] == modified["object"]["attributedTo"]
1049 end
1050
1051 test "it translates ostatus IDs to external URLs" do
1052 incoming = File.read!("test/fixtures/incoming_note_activity.xml")
1053 {:ok, [referent_activity]} = OStatus.handle_incoming(incoming)
1054
1055 user = insert(:user)
1056
1057 {:ok, activity, _} = CommonAPI.favorite(referent_activity.id, user)
1058 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1059
1060 assert modified["object"] == "http://gs.example.org:4040/index.php/notice/29"
1061 end
1062
1063 test "it translates ostatus reply_to IDs to external URLs" do
1064 incoming = File.read!("test/fixtures/incoming_note_activity.xml")
1065 {:ok, [referred_activity]} = OStatus.handle_incoming(incoming)
1066
1067 user = insert(:user)
1068
1069 {:ok, activity} =
1070 CommonAPI.post(user, %{"status" => "HI!", "in_reply_to_status_id" => referred_activity.id})
1071
1072 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1073
1074 assert modified["object"]["inReplyTo"] == "http://gs.example.org:4040/index.php/notice/29"
1075 end
1076
1077 test "it strips internal hashtag data" do
1078 user = insert(:user)
1079
1080 {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu"})
1081
1082 expected_tag = %{
1083 "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
1084 "type" => "Hashtag",
1085 "name" => "#2hu"
1086 }
1087
1088 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1089
1090 assert modified["object"]["tag"] == [expected_tag]
1091 end
1092
1093 test "it strips internal fields" do
1094 user = insert(:user)
1095
1096 {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu :firefox:"})
1097
1098 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1099
1100 assert length(modified["object"]["tag"]) == 2
1101
1102 assert is_nil(modified["object"]["emoji"])
1103 assert is_nil(modified["object"]["like_count"])
1104 assert is_nil(modified["object"]["announcements"])
1105 assert is_nil(modified["object"]["announcement_count"])
1106 assert is_nil(modified["object"]["context_id"])
1107 end
1108
1109 test "it strips internal fields of article" do
1110 activity = insert(:article_activity)
1111
1112 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1113
1114 assert length(modified["object"]["tag"]) == 2
1115
1116 assert is_nil(modified["object"]["emoji"])
1117 assert is_nil(modified["object"]["like_count"])
1118 assert is_nil(modified["object"]["announcements"])
1119 assert is_nil(modified["object"]["announcement_count"])
1120 assert is_nil(modified["object"]["context_id"])
1121 assert is_nil(modified["object"]["likes"])
1122 end
1123
1124 test "the directMessage flag is present" do
1125 user = insert(:user)
1126 other_user = insert(:user)
1127
1128 {:ok, activity} = CommonAPI.post(user, %{"status" => "2hu :moominmamma:"})
1129
1130 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1131
1132 assert modified["directMessage"] == false
1133
1134 {:ok, activity} =
1135 CommonAPI.post(user, %{"status" => "@#{other_user.nickname} :moominmamma:"})
1136
1137 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1138
1139 assert modified["directMessage"] == false
1140
1141 {:ok, activity} =
1142 CommonAPI.post(user, %{
1143 "status" => "@#{other_user.nickname} :moominmamma:",
1144 "visibility" => "direct"
1145 })
1146
1147 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1148
1149 assert modified["directMessage"] == true
1150 end
1151
1152 test "it strips BCC field" do
1153 user = insert(:user)
1154 {:ok, list} = Pleroma.List.create("foo", user)
1155
1156 {:ok, activity} =
1157 CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"})
1158
1159 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1160
1161 assert is_nil(modified["bcc"])
1162 end
1163 end
1164
1165 describe "user upgrade" do
1166 test "it upgrades a user to activitypub" do
1167 user =
1168 insert(:user, %{
1169 nickname: "rye@niu.moe",
1170 local: false,
1171 ap_id: "https://niu.moe/users/rye",
1172 follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"})
1173 })
1174
1175 user_two = insert(:user, %{following: [user.follower_address]})
1176
1177 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
1178 {:ok, unrelated_activity} = CommonAPI.post(user_two, %{"status" => "test"})
1179 assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients
1180
1181 user = User.get_cached_by_id(user.id)
1182 assert user.info.note_count == 1
1183
1184 {:ok, user} = Transmogrifier.upgrade_user_from_ap_id("https://niu.moe/users/rye")
1185 assert user.info.ap_enabled
1186 assert user.info.note_count == 1
1187 assert user.follower_address == "https://niu.moe/users/rye/followers"
1188 assert user.following_address == "https://niu.moe/users/rye/following"
1189
1190 user = User.get_cached_by_id(user.id)
1191 assert user.info.note_count == 1
1192
1193 activity = Activity.get_by_id(activity.id)
1194 assert user.follower_address in activity.recipients
1195
1196 assert %{
1197 "url" => [
1198 %{
1199 "href" =>
1200 "https://cdn.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
1201 }
1202 ]
1203 } = user.avatar
1204
1205 assert %{
1206 "url" => [
1207 %{
1208 "href" =>
1209 "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
1210 }
1211 ]
1212 } = user.info.banner
1213
1214 refute "..." in activity.recipients
1215
1216 unrelated_activity = Activity.get_by_id(unrelated_activity.id)
1217 refute user.follower_address in unrelated_activity.recipients
1218
1219 user_two = User.get_cached_by_id(user_two.id)
1220 assert user.follower_address in user_two.following
1221 refute "..." in user_two.following
1222 end
1223 end
1224
1225 describe "maybe_retire_websub" do
1226 test "it deletes all websub client subscripitions with the user as topic" do
1227 subscription = %WebsubClientSubscription{topic: "https://niu.moe/users/rye.atom"}
1228 {:ok, ws} = Repo.insert(subscription)
1229
1230 subscription = %WebsubClientSubscription{topic: "https://niu.moe/users/pasty.atom"}
1231 {:ok, ws2} = Repo.insert(subscription)
1232
1233 Transmogrifier.maybe_retire_websub("https://niu.moe/users/rye")
1234
1235 refute Repo.get(WebsubClientSubscription, ws.id)
1236 assert Repo.get(WebsubClientSubscription, ws2.id)
1237 end
1238 end
1239
1240 describe "actor rewriting" do
1241 test "it fixes the actor URL property to be a proper URI" do
1242 data = %{
1243 "url" => %{"href" => "http://example.com"}
1244 }
1245
1246 rewritten = Transmogrifier.maybe_fix_user_object(data)
1247 assert rewritten["url"] == "http://example.com"
1248 end
1249 end
1250
1251 describe "actor origin containment" do
1252 test "it rejects activities which reference objects with bogus origins" do
1253 data = %{
1254 "@context" => "https://www.w3.org/ns/activitystreams",
1255 "id" => "http://mastodon.example.org/users/admin/activities/1234",
1256 "actor" => "http://mastodon.example.org/users/admin",
1257 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1258 "object" => "https://info.pleroma.site/activity.json",
1259 "type" => "Announce"
1260 }
1261
1262 :error = Transmogrifier.handle_incoming(data)
1263 end
1264
1265 test "it rejects activities which reference objects that have an incorrect attribution (variant 1)" do
1266 data = %{
1267 "@context" => "https://www.w3.org/ns/activitystreams",
1268 "id" => "http://mastodon.example.org/users/admin/activities/1234",
1269 "actor" => "http://mastodon.example.org/users/admin",
1270 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1271 "object" => "https://info.pleroma.site/activity2.json",
1272 "type" => "Announce"
1273 }
1274
1275 :error = Transmogrifier.handle_incoming(data)
1276 end
1277
1278 test "it rejects activities which reference objects that have an incorrect attribution (variant 2)" do
1279 data = %{
1280 "@context" => "https://www.w3.org/ns/activitystreams",
1281 "id" => "http://mastodon.example.org/users/admin/activities/1234",
1282 "actor" => "http://mastodon.example.org/users/admin",
1283 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1284 "object" => "https://info.pleroma.site/activity3.json",
1285 "type" => "Announce"
1286 }
1287
1288 :error = Transmogrifier.handle_incoming(data)
1289 end
1290 end
1291
1292 describe "reserialization" do
1293 test "successfully reserializes a message with inReplyTo == nil" do
1294 user = insert(:user)
1295
1296 message = %{
1297 "@context" => "https://www.w3.org/ns/activitystreams",
1298 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1299 "cc" => [],
1300 "type" => "Create",
1301 "object" => %{
1302 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1303 "cc" => [],
1304 "type" => "Note",
1305 "content" => "Hi",
1306 "inReplyTo" => nil,
1307 "attributedTo" => user.ap_id
1308 },
1309 "actor" => user.ap_id
1310 }
1311
1312 {:ok, activity} = Transmogrifier.handle_incoming(message)
1313
1314 {:ok, _} = Transmogrifier.prepare_outgoing(activity.data)
1315 end
1316
1317 test "successfully reserializes a message with AS2 objects in IR" do
1318 user = insert(:user)
1319
1320 message = %{
1321 "@context" => "https://www.w3.org/ns/activitystreams",
1322 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1323 "cc" => [],
1324 "type" => "Create",
1325 "object" => %{
1326 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1327 "cc" => [],
1328 "type" => "Note",
1329 "content" => "Hi",
1330 "inReplyTo" => nil,
1331 "attributedTo" => user.ap_id,
1332 "tag" => [
1333 %{"name" => "#2hu", "href" => "http://example.com/2hu", "type" => "Hashtag"},
1334 %{"name" => "Bob", "href" => "http://example.com/bob", "type" => "Mention"}
1335 ]
1336 },
1337 "actor" => user.ap_id
1338 }
1339
1340 {:ok, activity} = Transmogrifier.handle_incoming(message)
1341
1342 {:ok, _} = Transmogrifier.prepare_outgoing(activity.data)
1343 end
1344 end
1345
1346 test "Rewrites Answers to Notes" do
1347 user = insert(:user)
1348
1349 {:ok, poll_activity} =
1350 CommonAPI.post(user, %{
1351 "status" => "suya...",
1352 "poll" => %{"options" => ["suya", "suya.", "suya.."], "expires_in" => 10}
1353 })
1354
1355 poll_object = Object.normalize(poll_activity)
1356 # TODO: Replace with CommonAPI vote creation when implemented
1357 data =
1358 File.read!("test/fixtures/mastodon-vote.json")
1359 |> Poison.decode!()
1360 |> Kernel.put_in(["to"], user.ap_id)
1361 |> Kernel.put_in(["object", "inReplyTo"], poll_object.data["id"])
1362 |> Kernel.put_in(["object", "to"], user.ap_id)
1363
1364 {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data)
1365 {:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
1366
1367 assert data["object"]["type"] == "Note"
1368 end
1369
1370 describe "fix_explicit_addressing" do
1371 setup do
1372 user = insert(:user)
1373 [user: user]
1374 end
1375
1376 test "moves non-explicitly mentioned actors to cc", %{user: user} do
1377 explicitly_mentioned_actors = [
1378 "https://pleroma.gold/users/user1",
1379 "https://pleroma.gold/user2"
1380 ]
1381
1382 object = %{
1383 "actor" => user.ap_id,
1384 "to" => explicitly_mentioned_actors ++ ["https://social.beepboop.ga/users/dirb"],
1385 "cc" => [],
1386 "tag" =>
1387 Enum.map(explicitly_mentioned_actors, fn href ->
1388 %{"type" => "Mention", "href" => href}
1389 end)
1390 }
1391
1392 fixed_object = Transmogrifier.fix_explicit_addressing(object)
1393 assert Enum.all?(explicitly_mentioned_actors, &(&1 in fixed_object["to"]))
1394 refute "https://social.beepboop.ga/users/dirb" in fixed_object["to"]
1395 assert "https://social.beepboop.ga/users/dirb" in fixed_object["cc"]
1396 end
1397
1398 test "does not move actor's follower collection to cc", %{user: user} do
1399 object = %{
1400 "actor" => user.ap_id,
1401 "to" => [user.follower_address],
1402 "cc" => []
1403 }
1404
1405 fixed_object = Transmogrifier.fix_explicit_addressing(object)
1406 assert user.follower_address in fixed_object["to"]
1407 refute user.follower_address in fixed_object["cc"]
1408 end
1409
1410 test "removes recipient's follower collection from cc", %{user: user} do
1411 recipient = insert(:user)
1412
1413 object = %{
1414 "actor" => user.ap_id,
1415 "to" => [recipient.ap_id, "https://www.w3.org/ns/activitystreams#Public"],
1416 "cc" => [user.follower_address, recipient.follower_address]
1417 }
1418
1419 fixed_object = Transmogrifier.fix_explicit_addressing(object)
1420
1421 assert user.follower_address in fixed_object["cc"]
1422 refute recipient.follower_address in fixed_object["cc"]
1423 refute recipient.follower_address in fixed_object["to"]
1424 end
1425 end
1426 end