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