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