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