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