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