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