Don't crash when email settings are invalid
[akkoma] / test / pleroma / web / activity_pub / transmogrifier / note_handling_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.ActivityPub.Transmogrifier.NoteHandlingTest do
6 use Oban.Testing, repo: Pleroma.Repo
7 use Pleroma.DataCase
8
9 alias Pleroma.Activity
10 alias Pleroma.Object
11 alias Pleroma.User
12 alias Pleroma.Web.ActivityPub.Transmogrifier
13 alias Pleroma.Web.CommonAPI
14
15 import Mock
16 import Pleroma.Factory
17 import ExUnit.CaptureLog
18
19 setup_all do
20 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
21 :ok
22 end
23
24 setup do: clear_config([:instance, :max_remote_account_fields])
25
26 describe "handle_incoming" do
27 test "it works for incoming notices with tag not being an array (kroeg)" do
28 data = File.read!("test/fixtures/kroeg-array-less-emoji.json") |> Jason.decode!()
29
30 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
31 object = Object.normalize(data["object"], fetch: false)
32
33 assert object.data["emoji"] == %{
34 "icon_e_smile" => "https://puckipedia.com/forum/images/smilies/icon_e_smile.png"
35 }
36
37 data = File.read!("test/fixtures/kroeg-array-less-hashtag.json") |> Jason.decode!()
38
39 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
40 object = Object.normalize(data["object"], fetch: false)
41
42 assert "test" in object.data["tag"]
43 end
44
45 test "it cleans up incoming notices which are not really DMs" do
46 user = insert(:user)
47 other_user = insert(:user)
48
49 to = [user.ap_id, other_user.ap_id]
50
51 data =
52 File.read!("test/fixtures/mastodon-post-activity.json")
53 |> Jason.decode!()
54 |> Map.put("to", to)
55 |> Map.put("cc", [])
56
57 object =
58 data["object"]
59 |> Map.put("to", to)
60 |> Map.put("cc", [])
61
62 data = Map.put(data, "object", object)
63
64 {:ok, %Activity{data: data, local: false} = activity} = Transmogrifier.handle_incoming(data)
65
66 assert data["to"] == []
67 assert data["cc"] == to
68
69 object_data = Object.normalize(activity, fetch: false).data
70
71 assert object_data["to"] == []
72 assert object_data["cc"] == to
73 end
74
75 test "it ignores an incoming notice if we already have it" do
76 activity = insert(:note_activity)
77
78 data =
79 File.read!("test/fixtures/mastodon-post-activity.json")
80 |> Jason.decode!()
81 |> Map.put("object", Object.normalize(activity, fetch: false).data)
82
83 {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
84
85 assert activity == returned_activity
86 end
87
88 @tag capture_log: true
89 test "it fetches reply-to activities if we don't have them" do
90 data =
91 File.read!("test/fixtures/mastodon-post-activity.json")
92 |> Jason.decode!()
93
94 object =
95 data["object"]
96 |> Map.put("inReplyTo", "https://mstdn.io/users/mayuutann/statuses/99568293732299394")
97
98 data = Map.put(data, "object", object)
99 {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
100 returned_object = Object.normalize(returned_activity, fetch: false)
101
102 assert %Activity{} =
103 Activity.get_create_by_object_ap_id(
104 "https://mstdn.io/users/mayuutann/statuses/99568293732299394"
105 )
106
107 assert returned_object.data["inReplyTo"] ==
108 "https://mstdn.io/users/mayuutann/statuses/99568293732299394"
109 end
110
111 test "it does not fetch reply-to activities beyond max replies depth limit" do
112 data =
113 File.read!("test/fixtures/mastodon-post-activity.json")
114 |> Jason.decode!()
115
116 object =
117 data["object"]
118 |> Map.put("inReplyTo", "https://shitposter.club/notice/2827873")
119
120 data = Map.put(data, "object", object)
121
122 with_mock Pleroma.Web.Federator,
123 allowed_thread_distance?: fn _ -> false end do
124 {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
125
126 returned_object = Object.normalize(returned_activity, fetch: false)
127
128 refute Activity.get_create_by_object_ap_id(
129 "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
130 )
131
132 assert returned_object.data["inReplyTo"] == "https://shitposter.club/notice/2827873"
133 end
134 end
135
136 test "it does not crash if the object in inReplyTo can't be fetched" do
137 data =
138 File.read!("test/fixtures/mastodon-post-activity.json")
139 |> Jason.decode!()
140
141 object =
142 data["object"]
143 |> Map.put("inReplyTo", "https://404.site/whatever")
144
145 data =
146 data
147 |> Map.put("object", object)
148
149 assert capture_log(fn ->
150 {:ok, _returned_activity} = Transmogrifier.handle_incoming(data)
151 end) =~ "[warn] Couldn't fetch \"https://404.site/whatever\", error: nil"
152 end
153
154 test "it does not work for deactivated users" do
155 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
156
157 insert(:user, ap_id: data["actor"], is_active: false)
158
159 assert {:error, _} = Transmogrifier.handle_incoming(data)
160 end
161
162 test "it works for incoming notices" do
163 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Jason.decode!()
164
165 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
166
167 assert data["id"] ==
168 "http://mastodon.example.org/users/admin/statuses/99512778738411822/activity"
169
170 assert data["context"] ==
171 "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation"
172
173 assert data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
174
175 assert data["cc"] == [
176 "http://mastodon.example.org/users/admin/followers",
177 "http://localtesting.pleroma.lol/users/lain"
178 ]
179
180 assert data["actor"] == "http://mastodon.example.org/users/admin"
181
182 object_data = Object.normalize(data["object"], fetch: false).data
183
184 assert object_data["id"] ==
185 "http://mastodon.example.org/users/admin/statuses/99512778738411822"
186
187 assert object_data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
188
189 assert object_data["cc"] == [
190 "http://mastodon.example.org/users/admin/followers",
191 "http://localtesting.pleroma.lol/users/lain"
192 ]
193
194 assert object_data["actor"] == "http://mastodon.example.org/users/admin"
195 assert object_data["attributedTo"] == "http://mastodon.example.org/users/admin"
196
197 assert object_data["context"] ==
198 "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation"
199
200 assert object_data["sensitive"] == true
201
202 user = User.get_cached_by_ap_id(object_data["actor"])
203
204 assert user.note_count == 1
205 end
206
207 test "it works for incoming notices without the sensitive property but an nsfw hashtag" do
208 data = File.read!("test/fixtures/mastodon-post-activity-nsfw.json") |> Jason.decode!()
209
210 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
211
212 object_data = Object.normalize(data["object"], fetch: false).data
213
214 assert object_data["sensitive"] == true
215 end
216
217 test "it works for incoming notices with hashtags" do
218 data = File.read!("test/fixtures/mastodon-post-activity-hashtag.json") |> Jason.decode!()
219
220 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
221 object = Object.normalize(data["object"], fetch: false)
222
223 assert Enum.at(object.data["tag"], 2) == "moo"
224 end
225
226 test "it works for incoming notices with contentMap" do
227 data = File.read!("test/fixtures/mastodon-post-activity-contentmap.json") |> Jason.decode!()
228
229 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
230 object = Object.normalize(data["object"], fetch: false)
231
232 assert object.data["content"] ==
233 "<p><span class=\"h-card\"><a href=\"http://localtesting.pleroma.lol/users/lain\" class=\"u-url mention\">@<span>lain</span></a></span></p>"
234 end
235
236 test "it works for incoming notices with to/cc not being an array (kroeg)" do
237 data = File.read!("test/fixtures/kroeg-post-activity.json") |> Jason.decode!()
238
239 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
240 object = Object.normalize(data["object"], fetch: false)
241
242 assert object.data["content"] ==
243 "<p>henlo from my Psion netBook</p><p>message sent from my Psion netBook</p>"
244 end
245
246 test "it ensures that as:Public activities make it to their followers collection" do
247 user = insert(:user)
248
249 data =
250 File.read!("test/fixtures/mastodon-post-activity.json")
251 |> Jason.decode!()
252 |> Map.put("actor", user.ap_id)
253 |> Map.put("to", ["https://www.w3.org/ns/activitystreams#Public"])
254 |> Map.put("cc", [])
255
256 object =
257 data["object"]
258 |> Map.put("attributedTo", user.ap_id)
259 |> Map.put("to", ["https://www.w3.org/ns/activitystreams#Public"])
260 |> Map.put("cc", [])
261 |> Map.put("id", user.ap_id <> "/activities/12345678")
262
263 data = Map.put(data, "object", object)
264
265 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
266
267 assert data["cc"] == [User.ap_followers(user)]
268 end
269
270 test "it ensures that address fields become lists" do
271 user = insert(:user)
272
273 data =
274 File.read!("test/fixtures/mastodon-post-activity.json")
275 |> Jason.decode!()
276 |> Map.put("actor", user.ap_id)
277 |> Map.put("to", nil)
278 |> Map.put("cc", nil)
279
280 object =
281 data["object"]
282 |> Map.put("attributedTo", user.ap_id)
283 |> Map.put("to", nil)
284 |> Map.put("cc", nil)
285 |> Map.put("id", user.ap_id <> "/activities/12345678")
286
287 data = Map.put(data, "object", object)
288
289 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
290
291 assert !is_nil(data["to"])
292 assert !is_nil(data["cc"])
293 end
294
295 test "it strips internal likes" do
296 data =
297 File.read!("test/fixtures/mastodon-post-activity.json")
298 |> Jason.decode!()
299
300 likes = %{
301 "first" =>
302 "http://mastodon.example.org/objects/dbdbc507-52c8-490d-9b7c-1e1d52e5c132/likes?page=1",
303 "id" => "http://mastodon.example.org/objects/dbdbc507-52c8-490d-9b7c-1e1d52e5c132/likes",
304 "totalItems" => 3,
305 "type" => "OrderedCollection"
306 }
307
308 object = Map.put(data["object"], "likes", likes)
309 data = Map.put(data, "object", object)
310
311 {:ok, %Activity{object: object}} = Transmogrifier.handle_incoming(data)
312
313 refute Map.has_key?(object.data, "likes")
314 end
315
316 test "it strips internal reactions" do
317 user = insert(:user)
318 {:ok, activity} = CommonAPI.post(user, %{status: "#cofe"})
319 {:ok, _} = CommonAPI.react_with_emoji(activity.id, user, "📢")
320
321 %{object: object} = Activity.get_by_id_with_object(activity.id)
322 assert Map.has_key?(object.data, "reactions")
323 assert Map.has_key?(object.data, "reaction_count")
324
325 object_data = Transmogrifier.strip_internal_fields(object.data)
326 refute Map.has_key?(object_data, "reactions")
327 refute Map.has_key?(object_data, "reaction_count")
328 end
329
330 test "it correctly processes messages with non-array to field" do
331 user = insert(:user)
332
333 message = %{
334 "@context" => "https://www.w3.org/ns/activitystreams",
335 "to" => "https://www.w3.org/ns/activitystreams#Public",
336 "type" => "Create",
337 "object" => %{
338 "content" => "blah blah blah",
339 "type" => "Note",
340 "attributedTo" => user.ap_id,
341 "inReplyTo" => nil
342 },
343 "actor" => user.ap_id
344 }
345
346 assert {:ok, activity} = Transmogrifier.handle_incoming(message)
347
348 assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["to"]
349 end
350
351 test "it correctly processes messages with non-array cc field" do
352 user = insert(:user)
353
354 message = %{
355 "@context" => "https://www.w3.org/ns/activitystreams",
356 "to" => user.follower_address,
357 "cc" => "https://www.w3.org/ns/activitystreams#Public",
358 "type" => "Create",
359 "object" => %{
360 "content" => "blah blah blah",
361 "type" => "Note",
362 "attributedTo" => user.ap_id,
363 "inReplyTo" => nil
364 },
365 "actor" => user.ap_id
366 }
367
368 assert {:ok, activity} = Transmogrifier.handle_incoming(message)
369
370 assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"]
371 assert [user.follower_address] == activity.data["to"]
372 end
373
374 test "it correctly processes messages with weirdness in address fields" do
375 user = insert(:user)
376
377 message = %{
378 "@context" => "https://www.w3.org/ns/activitystreams",
379 "to" => [nil, user.follower_address],
380 "cc" => ["https://www.w3.org/ns/activitystreams#Public", ["¿"]],
381 "type" => "Create",
382 "object" => %{
383 "content" => "…",
384 "type" => "Note",
385 "attributedTo" => user.ap_id,
386 "inReplyTo" => nil
387 },
388 "actor" => user.ap_id
389 }
390
391 assert {:ok, activity} = Transmogrifier.handle_incoming(message)
392
393 assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"]
394 assert [user.follower_address] == activity.data["to"]
395 end
396 end
397
398 describe "`handle_incoming/2`, Mastodon format `replies` handling" do
399 setup do: clear_config([:activitypub, :note_replies_output_limit], 5)
400 setup do: clear_config([:instance, :federation_incoming_replies_max_depth])
401
402 setup do
403 data =
404 "test/fixtures/mastodon-post-activity.json"
405 |> File.read!()
406 |> Jason.decode!()
407
408 items = get_in(data, ["object", "replies", "first", "items"])
409 assert length(items) > 0
410
411 %{data: data, items: items}
412 end
413
414 test "schedules background fetching of `replies` items if max thread depth limit allows", %{
415 data: data,
416 items: items
417 } do
418 clear_config([:instance, :federation_incoming_replies_max_depth], 10)
419
420 {:ok, _activity} = Transmogrifier.handle_incoming(data)
421
422 for id <- items do
423 job_args = %{"op" => "fetch_remote", "id" => id, "depth" => 1}
424 assert_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker, args: job_args)
425 end
426 end
427
428 test "does NOT schedule background fetching of `replies` beyond max thread depth limit allows",
429 %{data: data} do
430 clear_config([:instance, :federation_incoming_replies_max_depth], 0)
431
432 {:ok, _activity} = Transmogrifier.handle_incoming(data)
433
434 assert all_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker) == []
435 end
436 end
437
438 describe "`handle_incoming/2`, Pleroma format `replies` handling" do
439 setup do: clear_config([:activitypub, :note_replies_output_limit], 5)
440 setup do: clear_config([:instance, :federation_incoming_replies_max_depth])
441
442 setup do
443 user = insert(:user)
444
445 {:ok, activity} = CommonAPI.post(user, %{status: "post1"})
446
447 {:ok, reply1} =
448 CommonAPI.post(user, %{status: "reply1", in_reply_to_status_id: activity.id})
449
450 {:ok, reply2} =
451 CommonAPI.post(user, %{status: "reply2", in_reply_to_status_id: activity.id})
452
453 replies_uris = Enum.map([reply1, reply2], fn a -> a.object.data["id"] end)
454
455 {:ok, federation_output} = Transmogrifier.prepare_outgoing(activity.data)
456
457 Repo.delete(activity.object)
458 Repo.delete(activity)
459
460 %{federation_output: federation_output, replies_uris: replies_uris}
461 end
462
463 test "schedules background fetching of `replies` items if max thread depth limit allows", %{
464 federation_output: federation_output,
465 replies_uris: replies_uris
466 } do
467 clear_config([:instance, :federation_incoming_replies_max_depth], 1)
468
469 {:ok, _activity} = Transmogrifier.handle_incoming(federation_output)
470
471 for id <- replies_uris do
472 job_args = %{"op" => "fetch_remote", "id" => id, "depth" => 1}
473 assert_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker, args: job_args)
474 end
475 end
476
477 test "does NOT schedule background fetching of `replies` beyond max thread depth limit allows",
478 %{federation_output: federation_output} do
479 clear_config([:instance, :federation_incoming_replies_max_depth], 0)
480
481 {:ok, _activity} = Transmogrifier.handle_incoming(federation_output)
482
483 assert all_enqueued(worker: Pleroma.Workers.RemoteFetcherWorker) == []
484 end
485 end
486
487 describe "reserialization" do
488 test "successfully reserializes a message with inReplyTo == nil" do
489 user = insert(:user)
490
491 message = %{
492 "@context" => "https://www.w3.org/ns/activitystreams",
493 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
494 "cc" => [],
495 "type" => "Create",
496 "object" => %{
497 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
498 "cc" => [],
499 "type" => "Note",
500 "content" => "Hi",
501 "inReplyTo" => nil,
502 "attributedTo" => user.ap_id
503 },
504 "actor" => user.ap_id
505 }
506
507 {:ok, activity} = Transmogrifier.handle_incoming(message)
508
509 {:ok, _} = Transmogrifier.prepare_outgoing(activity.data)
510 end
511
512 test "successfully reserializes a message with AS2 objects in IR" do
513 user = insert(:user)
514
515 message = %{
516 "@context" => "https://www.w3.org/ns/activitystreams",
517 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
518 "cc" => [],
519 "type" => "Create",
520 "object" => %{
521 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
522 "cc" => [],
523 "type" => "Note",
524 "content" => "Hi",
525 "inReplyTo" => nil,
526 "attributedTo" => user.ap_id,
527 "tag" => [
528 %{"name" => "#2hu", "href" => "http://example.com/2hu", "type" => "Hashtag"},
529 %{"name" => "Bob", "href" => "http://example.com/bob", "type" => "Mention"}
530 ]
531 },
532 "actor" => user.ap_id
533 }
534
535 {:ok, activity} = Transmogrifier.handle_incoming(message)
536
537 {:ok, _} = Transmogrifier.prepare_outgoing(activity.data)
538 end
539 end
540
541 describe "fix_in_reply_to/2" do
542 setup do: clear_config([:instance, :federation_incoming_replies_max_depth])
543
544 setup do
545 data = Jason.decode!(File.read!("test/fixtures/mastodon-post-activity.json"))
546 [data: data]
547 end
548
549 test "returns not modified object when hasn't containts inReplyTo field", %{data: data} do
550 assert Transmogrifier.fix_in_reply_to(data) == data
551 end
552
553 test "returns object with inReplyTo when denied incoming reply", %{data: data} do
554 clear_config([:instance, :federation_incoming_replies_max_depth], 0)
555
556 object_with_reply =
557 Map.put(data["object"], "inReplyTo", "https://shitposter.club/notice/2827873")
558
559 modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
560 assert modified_object["inReplyTo"] == "https://shitposter.club/notice/2827873"
561
562 object_with_reply =
563 Map.put(data["object"], "inReplyTo", %{"id" => "https://shitposter.club/notice/2827873"})
564
565 modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
566 assert modified_object["inReplyTo"] == %{"id" => "https://shitposter.club/notice/2827873"}
567
568 object_with_reply =
569 Map.put(data["object"], "inReplyTo", ["https://shitposter.club/notice/2827873"])
570
571 modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
572 assert modified_object["inReplyTo"] == ["https://shitposter.club/notice/2827873"]
573
574 object_with_reply = Map.put(data["object"], "inReplyTo", [])
575 modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
576 assert modified_object["inReplyTo"] == []
577 end
578
579 @tag capture_log: true
580 test "returns modified object when allowed incoming reply", %{data: data} do
581 object_with_reply =
582 Map.put(
583 data["object"],
584 "inReplyTo",
585 "https://mstdn.io/users/mayuutann/statuses/99568293732299394"
586 )
587
588 clear_config([:instance, :federation_incoming_replies_max_depth], 5)
589 modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
590
591 assert modified_object["inReplyTo"] ==
592 "https://mstdn.io/users/mayuutann/statuses/99568293732299394"
593
594 assert modified_object["context"] ==
595 "tag:shitposter.club,2018-02-22:objectType=thread:nonce=e5a7c72d60a9c0e4"
596 end
597 end
598
599 describe "fix_attachments/1" do
600 test "returns not modified object" do
601 data = Jason.decode!(File.read!("test/fixtures/mastodon-post-activity.json"))
602 assert Transmogrifier.fix_attachments(data) == data
603 end
604
605 test "returns modified object when attachment is map" do
606 assert Transmogrifier.fix_attachments(%{
607 "attachment" => %{
608 "mediaType" => "video/mp4",
609 "url" => "https://peertube.moe/stat-480.mp4"
610 }
611 }) == %{
612 "attachment" => [
613 %{
614 "mediaType" => "video/mp4",
615 "type" => "Document",
616 "url" => [
617 %{
618 "href" => "https://peertube.moe/stat-480.mp4",
619 "mediaType" => "video/mp4",
620 "type" => "Link"
621 }
622 ]
623 }
624 ]
625 }
626 end
627
628 test "returns modified object when attachment is list" do
629 assert Transmogrifier.fix_attachments(%{
630 "attachment" => [
631 %{"mediaType" => "video/mp4", "url" => "https://pe.er/stat-480.mp4"},
632 %{"mimeType" => "video/mp4", "href" => "https://pe.er/stat-480.mp4"}
633 ]
634 }) == %{
635 "attachment" => [
636 %{
637 "mediaType" => "video/mp4",
638 "type" => "Document",
639 "url" => [
640 %{
641 "href" => "https://pe.er/stat-480.mp4",
642 "mediaType" => "video/mp4",
643 "type" => "Link"
644 }
645 ]
646 },
647 %{
648 "mediaType" => "video/mp4",
649 "type" => "Document",
650 "url" => [
651 %{
652 "href" => "https://pe.er/stat-480.mp4",
653 "mediaType" => "video/mp4",
654 "type" => "Link"
655 }
656 ]
657 }
658 ]
659 }
660 end
661 end
662
663 describe "fix_emoji/1" do
664 test "returns not modified object when object not contains tags" do
665 data = Jason.decode!(File.read!("test/fixtures/mastodon-post-activity.json"))
666 assert Transmogrifier.fix_emoji(data) == data
667 end
668
669 test "returns object with emoji when object contains list tags" do
670 assert Transmogrifier.fix_emoji(%{
671 "tag" => [
672 %{"type" => "Emoji", "name" => ":bib:", "icon" => %{"url" => "/test"}},
673 %{"type" => "Hashtag"}
674 ]
675 }) == %{
676 "emoji" => %{"bib" => "/test"},
677 "tag" => [
678 %{"icon" => %{"url" => "/test"}, "name" => ":bib:", "type" => "Emoji"},
679 %{"type" => "Hashtag"}
680 ]
681 }
682 end
683
684 test "returns object with emoji when object contains map tag" do
685 assert Transmogrifier.fix_emoji(%{
686 "tag" => %{"type" => "Emoji", "name" => ":bib:", "icon" => %{"url" => "/test"}}
687 }) == %{
688 "emoji" => %{"bib" => "/test"},
689 "tag" => %{"icon" => %{"url" => "/test"}, "name" => ":bib:", "type" => "Emoji"}
690 }
691 end
692 end
693
694 describe "set_replies/1" do
695 setup do: clear_config([:activitypub, :note_replies_output_limit], 2)
696
697 test "returns unmodified object if activity doesn't have self-replies" do
698 data = Jason.decode!(File.read!("test/fixtures/mastodon-post-activity.json"))
699 assert Transmogrifier.set_replies(data) == data
700 end
701
702 test "sets `replies` collection with a limited number of self-replies" do
703 [user, another_user] = insert_list(2, :user)
704
705 {:ok, %{id: id1} = activity} = CommonAPI.post(user, %{status: "1"})
706
707 {:ok, %{id: id2} = self_reply1} =
708 CommonAPI.post(user, %{status: "self-reply 1", in_reply_to_status_id: id1})
709
710 {:ok, self_reply2} =
711 CommonAPI.post(user, %{status: "self-reply 2", in_reply_to_status_id: id1})
712
713 # Assuming to _not_ be present in `replies` due to :note_replies_output_limit is set to 2
714 {:ok, _} = CommonAPI.post(user, %{status: "self-reply 3", in_reply_to_status_id: id1})
715
716 {:ok, _} =
717 CommonAPI.post(user, %{
718 status: "self-reply to self-reply",
719 in_reply_to_status_id: id2
720 })
721
722 {:ok, _} =
723 CommonAPI.post(another_user, %{
724 status: "another user's reply",
725 in_reply_to_status_id: id1
726 })
727
728 object = Object.normalize(activity, fetch: false)
729 replies_uris = Enum.map([self_reply1, self_reply2], fn a -> a.object.data["id"] end)
730
731 assert %{"type" => "Collection", "items" => ^replies_uris} =
732 Transmogrifier.set_replies(object.data)["replies"]
733 end
734 end
735
736 test "take_emoji_tags/1" do
737 user = insert(:user, %{emoji: %{"firefox" => "https://example.org/firefox.png"}})
738
739 assert Transmogrifier.take_emoji_tags(user) == [
740 %{
741 "icon" => %{"type" => "Image", "url" => "https://example.org/firefox.png"},
742 "id" => "https://example.org/firefox.png",
743 "name" => ":firefox:",
744 "type" => "Emoji",
745 "updated" => "1970-01-01T00:00:00Z"
746 }
747 ]
748 end
749 end