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