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