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