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