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