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