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