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