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