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