Merge branch 'feature/store-statuses-data-inside-flag' into 'develop'
[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 follows to locked account" do
809 pending_follower = insert(:user, ap_id: "http://mastodon.example.org/users/admin")
810 user = insert(:user, locked: true)
811
812 data =
813 File.read!("test/fixtures/mastodon-follow-activity.json")
814 |> Poison.decode!()
815 |> Map.put("object", user.ap_id)
816
817 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
818
819 assert data["type"] == "Follow"
820 assert data["object"] == user.ap_id
821 assert data["state"] == "pending"
822 assert data["actor"] == "http://mastodon.example.org/users/admin"
823
824 assert [^pending_follower] = User.get_follow_requests(user)
825 end
826
827 test "it works for incoming blocks" do
828 user = insert(:user)
829
830 data =
831 File.read!("test/fixtures/mastodon-block-activity.json")
832 |> Poison.decode!()
833 |> Map.put("object", user.ap_id)
834
835 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
836
837 assert data["type"] == "Block"
838 assert data["object"] == user.ap_id
839 assert data["actor"] == "http://mastodon.example.org/users/admin"
840
841 blocker = User.get_cached_by_ap_id(data["actor"])
842
843 assert User.blocks?(blocker, user)
844 end
845
846 test "incoming blocks successfully tear down any follow relationship" do
847 blocker = insert(:user)
848 blocked = insert(:user)
849
850 data =
851 File.read!("test/fixtures/mastodon-block-activity.json")
852 |> Poison.decode!()
853 |> Map.put("object", blocked.ap_id)
854 |> Map.put("actor", blocker.ap_id)
855
856 {:ok, blocker} = User.follow(blocker, blocked)
857 {:ok, blocked} = User.follow(blocked, blocker)
858
859 assert User.following?(blocker, blocked)
860 assert User.following?(blocked, blocker)
861
862 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
863
864 assert data["type"] == "Block"
865 assert data["object"] == blocked.ap_id
866 assert data["actor"] == blocker.ap_id
867
868 blocker = User.get_cached_by_ap_id(data["actor"])
869 blocked = User.get_cached_by_ap_id(data["object"])
870
871 assert User.blocks?(blocker, blocked)
872
873 refute User.following?(blocker, blocked)
874 refute User.following?(blocked, blocker)
875 end
876
877 test "it works for incoming unblocks with an existing block" do
878 user = insert(:user)
879
880 block_data =
881 File.read!("test/fixtures/mastodon-block-activity.json")
882 |> Poison.decode!()
883 |> Map.put("object", user.ap_id)
884
885 {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(block_data)
886
887 data =
888 File.read!("test/fixtures/mastodon-unblock-activity.json")
889 |> Poison.decode!()
890 |> Map.put("object", block_data)
891
892 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
893 assert data["type"] == "Undo"
894 assert data["object"]["type"] == "Block"
895 assert data["object"]["object"] == user.ap_id
896 assert data["actor"] == "http://mastodon.example.org/users/admin"
897
898 blocker = User.get_cached_by_ap_id(data["actor"])
899
900 refute User.blocks?(blocker, user)
901 end
902
903 test "it works for incoming accepts which were pre-accepted" do
904 follower = insert(:user)
905 followed = insert(:user)
906
907 {:ok, follower} = User.follow(follower, followed)
908 assert User.following?(follower, followed) == true
909
910 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
911
912 accept_data =
913 File.read!("test/fixtures/mastodon-accept-activity.json")
914 |> Poison.decode!()
915 |> Map.put("actor", followed.ap_id)
916
917 object =
918 accept_data["object"]
919 |> Map.put("actor", follower.ap_id)
920 |> Map.put("id", follow_activity.data["id"])
921
922 accept_data = Map.put(accept_data, "object", object)
923
924 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
925 refute activity.local
926
927 assert activity.data["object"] == follow_activity.data["id"]
928
929 assert activity.data["id"] == accept_data["id"]
930
931 follower = User.get_cached_by_id(follower.id)
932
933 assert User.following?(follower, followed) == true
934 end
935
936 test "it works for incoming accepts which were orphaned" do
937 follower = insert(:user)
938 followed = insert(:user, locked: true)
939
940 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
941
942 accept_data =
943 File.read!("test/fixtures/mastodon-accept-activity.json")
944 |> Poison.decode!()
945 |> Map.put("actor", followed.ap_id)
946
947 accept_data =
948 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
949
950 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
951 assert activity.data["object"] == follow_activity.data["id"]
952
953 follower = User.get_cached_by_id(follower.id)
954
955 assert User.following?(follower, followed) == true
956 end
957
958 test "it works for incoming accepts which are referenced by IRI only" do
959 follower = insert(:user)
960 followed = insert(:user, locked: true)
961
962 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
963
964 accept_data =
965 File.read!("test/fixtures/mastodon-accept-activity.json")
966 |> Poison.decode!()
967 |> Map.put("actor", followed.ap_id)
968 |> Map.put("object", follow_activity.data["id"])
969
970 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
971 assert activity.data["object"] == follow_activity.data["id"]
972
973 follower = User.get_cached_by_id(follower.id)
974
975 assert User.following?(follower, followed) == true
976 end
977
978 test "it fails for incoming accepts 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-accept-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 fails for incoming rejects which cannot be correlated" do
998 follower = insert(:user)
999 followed = insert(:user, locked: true)
1000
1001 accept_data =
1002 File.read!("test/fixtures/mastodon-reject-activity.json")
1003 |> Poison.decode!()
1004 |> Map.put("actor", followed.ap_id)
1005
1006 accept_data =
1007 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
1008
1009 :error = Transmogrifier.handle_incoming(accept_data)
1010
1011 follower = User.get_cached_by_id(follower.id)
1012
1013 refute User.following?(follower, followed) == true
1014 end
1015
1016 test "it works for incoming rejects which are orphaned" do
1017 follower = insert(:user)
1018 followed = insert(:user, locked: true)
1019
1020 {:ok, follower} = User.follow(follower, followed)
1021 {:ok, _follow_activity} = ActivityPub.follow(follower, followed)
1022
1023 assert User.following?(follower, followed) == true
1024
1025 reject_data =
1026 File.read!("test/fixtures/mastodon-reject-activity.json")
1027 |> Poison.decode!()
1028 |> Map.put("actor", followed.ap_id)
1029
1030 reject_data =
1031 Map.put(reject_data, "object", Map.put(reject_data["object"], "actor", follower.ap_id))
1032
1033 {:ok, activity} = Transmogrifier.handle_incoming(reject_data)
1034 refute activity.local
1035 assert activity.data["id"] == reject_data["id"]
1036
1037 follower = User.get_cached_by_id(follower.id)
1038
1039 assert User.following?(follower, followed) == false
1040 end
1041
1042 test "it works for incoming rejects which are referenced by IRI only" do
1043 follower = insert(:user)
1044 followed = insert(:user, locked: true)
1045
1046 {:ok, follower} = User.follow(follower, followed)
1047 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
1048
1049 assert User.following?(follower, followed) == true
1050
1051 reject_data =
1052 File.read!("test/fixtures/mastodon-reject-activity.json")
1053 |> Poison.decode!()
1054 |> Map.put("actor", followed.ap_id)
1055 |> Map.put("object", follow_activity.data["id"])
1056
1057 {:ok, %Activity{data: _}} = Transmogrifier.handle_incoming(reject_data)
1058
1059 follower = User.get_cached_by_id(follower.id)
1060
1061 assert User.following?(follower, followed) == false
1062 end
1063
1064 test "it rejects activities without a valid ID" do
1065 user = insert(:user)
1066
1067 data =
1068 File.read!("test/fixtures/mastodon-follow-activity.json")
1069 |> Poison.decode!()
1070 |> Map.put("object", user.ap_id)
1071 |> Map.put("id", "")
1072
1073 :error = Transmogrifier.handle_incoming(data)
1074 end
1075
1076 test "it remaps video URLs as attachments if necessary" do
1077 {:ok, object} =
1078 Fetcher.fetch_object_from_id(
1079 "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
1080 )
1081
1082 attachment = %{
1083 "type" => "Link",
1084 "mediaType" => "video/mp4",
1085 "href" =>
1086 "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
1087 "mimeType" => "video/mp4",
1088 "size" => 5_015_880,
1089 "url" => [
1090 %{
1091 "href" =>
1092 "https://peertube.moe/static/webseed/df5f464b-be8d-46fb-ad81-2d4c2d1630e3-480.mp4",
1093 "mediaType" => "video/mp4",
1094 "type" => "Link"
1095 }
1096 ],
1097 "width" => 480
1098 }
1099
1100 assert object.data["url"] ==
1101 "https://peertube.moe/videos/watch/df5f464b-be8d-46fb-ad81-2d4c2d1630e3"
1102
1103 assert object.data["attachment"] == [attachment]
1104 end
1105
1106 test "it accepts Flag activities" do
1107 user = insert(:user)
1108 other_user = insert(:user)
1109
1110 {:ok, activity} = CommonAPI.post(user, %{"status" => "test post"})
1111 object = Object.normalize(activity)
1112
1113 note_obj = %{
1114 "type" => "Note",
1115 "id" => activity.data["id"],
1116 "content" => "test post",
1117 "published" => object.data["published"],
1118 "actor" => AccountView.render("show.json", %{user: user})
1119 }
1120
1121 message = %{
1122 "@context" => "https://www.w3.org/ns/activitystreams",
1123 "cc" => [user.ap_id],
1124 "object" => [user.ap_id, activity.data["id"]],
1125 "type" => "Flag",
1126 "content" => "blocked AND reported!!!",
1127 "actor" => other_user.ap_id
1128 }
1129
1130 assert {:ok, activity} = Transmogrifier.handle_incoming(message)
1131
1132 assert activity.data["object"] == [user.ap_id, note_obj]
1133 assert activity.data["content"] == "blocked AND reported!!!"
1134 assert activity.data["actor"] == other_user.ap_id
1135 assert activity.data["cc"] == [user.ap_id]
1136 end
1137
1138 test "it correctly processes messages with non-array to field" do
1139 user = insert(:user)
1140
1141 message = %{
1142 "@context" => "https://www.w3.org/ns/activitystreams",
1143 "to" => "https://www.w3.org/ns/activitystreams#Public",
1144 "type" => "Create",
1145 "object" => %{
1146 "content" => "blah blah blah",
1147 "type" => "Note",
1148 "attributedTo" => user.ap_id,
1149 "inReplyTo" => nil
1150 },
1151 "actor" => user.ap_id
1152 }
1153
1154 assert {:ok, activity} = Transmogrifier.handle_incoming(message)
1155
1156 assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["to"]
1157 end
1158
1159 test "it correctly processes messages with non-array cc field" do
1160 user = insert(:user)
1161
1162 message = %{
1163 "@context" => "https://www.w3.org/ns/activitystreams",
1164 "to" => user.follower_address,
1165 "cc" => "https://www.w3.org/ns/activitystreams#Public",
1166 "type" => "Create",
1167 "object" => %{
1168 "content" => "blah blah blah",
1169 "type" => "Note",
1170 "attributedTo" => user.ap_id,
1171 "inReplyTo" => nil
1172 },
1173 "actor" => user.ap_id
1174 }
1175
1176 assert {:ok, activity} = Transmogrifier.handle_incoming(message)
1177
1178 assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"]
1179 assert [user.follower_address] == activity.data["to"]
1180 end
1181 end
1182
1183 describe "prepare outgoing" do
1184 test "it inlines private announced objects" do
1185 user = insert(:user)
1186
1187 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey", "visibility" => "private"})
1188
1189 {:ok, announce_activity, _} = CommonAPI.repeat(activity.id, user)
1190
1191 {:ok, modified} = Transmogrifier.prepare_outgoing(announce_activity.data)
1192
1193 assert modified["object"]["content"] == "hey"
1194 assert modified["object"]["actor"] == modified["object"]["attributedTo"]
1195 end
1196
1197 test "it turns mentions into tags" do
1198 user = insert(:user)
1199 other_user = insert(:user)
1200
1201 {:ok, activity} =
1202 CommonAPI.post(user, %{"status" => "hey, @#{other_user.nickname}, how are ya? #2hu"})
1203
1204 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1205 object = modified["object"]
1206
1207 expected_mention = %{
1208 "href" => other_user.ap_id,
1209 "name" => "@#{other_user.nickname}",
1210 "type" => "Mention"
1211 }
1212
1213 expected_tag = %{
1214 "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
1215 "type" => "Hashtag",
1216 "name" => "#2hu"
1217 }
1218
1219 assert Enum.member?(object["tag"], expected_tag)
1220 assert Enum.member?(object["tag"], expected_mention)
1221 end
1222
1223 test "it adds the sensitive property" do
1224 user = insert(:user)
1225
1226 {:ok, activity} = CommonAPI.post(user, %{"status" => "#nsfw hey"})
1227 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1228
1229 assert modified["object"]["sensitive"]
1230 end
1231
1232 test "it adds the json-ld context and the conversation property" do
1233 user = insert(:user)
1234
1235 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
1236 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1237
1238 assert modified["@context"] ==
1239 Pleroma.Web.ActivityPub.Utils.make_json_ld_header()["@context"]
1240
1241 assert modified["object"]["conversation"] == modified["context"]
1242 end
1243
1244 test "it sets the 'attributedTo' property to the actor of the object if it doesn't have one" do
1245 user = insert(:user)
1246
1247 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
1248 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1249
1250 assert modified["object"]["actor"] == modified["object"]["attributedTo"]
1251 end
1252
1253 test "it strips internal hashtag data" do
1254 user = insert(:user)
1255
1256 {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu"})
1257
1258 expected_tag = %{
1259 "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
1260 "type" => "Hashtag",
1261 "name" => "#2hu"
1262 }
1263
1264 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1265
1266 assert modified["object"]["tag"] == [expected_tag]
1267 end
1268
1269 test "it strips internal fields" do
1270 user = insert(:user)
1271
1272 {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu :firefox:"})
1273
1274 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1275
1276 assert length(modified["object"]["tag"]) == 2
1277
1278 assert is_nil(modified["object"]["emoji"])
1279 assert is_nil(modified["object"]["like_count"])
1280 assert is_nil(modified["object"]["announcements"])
1281 assert is_nil(modified["object"]["announcement_count"])
1282 assert is_nil(modified["object"]["context_id"])
1283 end
1284
1285 test "it strips internal fields of article" do
1286 activity = insert(:article_activity)
1287
1288 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1289
1290 assert length(modified["object"]["tag"]) == 2
1291
1292 assert is_nil(modified["object"]["emoji"])
1293 assert is_nil(modified["object"]["like_count"])
1294 assert is_nil(modified["object"]["announcements"])
1295 assert is_nil(modified["object"]["announcement_count"])
1296 assert is_nil(modified["object"]["context_id"])
1297 assert is_nil(modified["object"]["likes"])
1298 end
1299
1300 test "the directMessage flag is present" do
1301 user = insert(:user)
1302 other_user = insert(:user)
1303
1304 {:ok, activity} = CommonAPI.post(user, %{"status" => "2hu :moominmamma:"})
1305
1306 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1307
1308 assert modified["directMessage"] == false
1309
1310 {:ok, activity} =
1311 CommonAPI.post(user, %{"status" => "@#{other_user.nickname} :moominmamma:"})
1312
1313 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1314
1315 assert modified["directMessage"] == false
1316
1317 {:ok, activity} =
1318 CommonAPI.post(user, %{
1319 "status" => "@#{other_user.nickname} :moominmamma:",
1320 "visibility" => "direct"
1321 })
1322
1323 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1324
1325 assert modified["directMessage"] == true
1326 end
1327
1328 test "it strips BCC field" do
1329 user = insert(:user)
1330 {:ok, list} = Pleroma.List.create("foo", user)
1331
1332 {:ok, activity} =
1333 CommonAPI.post(user, %{"status" => "foobar", "visibility" => "list:#{list.id}"})
1334
1335 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
1336
1337 assert is_nil(modified["bcc"])
1338 end
1339
1340 test "it can handle Listen activities" do
1341 listen_activity = insert(:listen)
1342
1343 {:ok, modified} = Transmogrifier.prepare_outgoing(listen_activity.data)
1344
1345 assert modified["type"] == "Listen"
1346
1347 user = insert(:user)
1348
1349 {:ok, activity} = CommonAPI.listen(user, %{"title" => "lain radio episode 1"})
1350
1351 {:ok, _modified} = Transmogrifier.prepare_outgoing(activity.data)
1352 end
1353 end
1354
1355 describe "user upgrade" do
1356 test "it upgrades a user to activitypub" do
1357 user =
1358 insert(:user, %{
1359 nickname: "rye@niu.moe",
1360 local: false,
1361 ap_id: "https://niu.moe/users/rye",
1362 follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"})
1363 })
1364
1365 user_two = insert(:user)
1366 Pleroma.FollowingRelationship.follow(user_two, user, "accept")
1367
1368 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
1369 {:ok, unrelated_activity} = CommonAPI.post(user_two, %{"status" => "test"})
1370 assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients
1371
1372 user = User.get_cached_by_id(user.id)
1373 assert user.note_count == 1
1374
1375 {:ok, user} = Transmogrifier.upgrade_user_from_ap_id("https://niu.moe/users/rye")
1376 ObanHelpers.perform_all()
1377
1378 assert user.ap_enabled
1379 assert user.note_count == 1
1380 assert user.follower_address == "https://niu.moe/users/rye/followers"
1381 assert user.following_address == "https://niu.moe/users/rye/following"
1382
1383 user = User.get_cached_by_id(user.id)
1384 assert user.note_count == 1
1385
1386 activity = Activity.get_by_id(activity.id)
1387 assert user.follower_address in activity.recipients
1388
1389 assert %{
1390 "url" => [
1391 %{
1392 "href" =>
1393 "https://cdn.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
1394 }
1395 ]
1396 } = user.avatar
1397
1398 assert %{
1399 "url" => [
1400 %{
1401 "href" =>
1402 "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
1403 }
1404 ]
1405 } = user.banner
1406
1407 refute "..." in activity.recipients
1408
1409 unrelated_activity = Activity.get_by_id(unrelated_activity.id)
1410 refute user.follower_address in unrelated_activity.recipients
1411
1412 user_two = User.get_cached_by_id(user_two.id)
1413 assert User.following?(user_two, user)
1414 refute "..." in User.following(user_two)
1415 end
1416 end
1417
1418 describe "actor rewriting" do
1419 test "it fixes the actor URL property to be a proper URI" do
1420 data = %{
1421 "url" => %{"href" => "http://example.com"}
1422 }
1423
1424 rewritten = Transmogrifier.maybe_fix_user_object(data)
1425 assert rewritten["url"] == "http://example.com"
1426 end
1427 end
1428
1429 describe "actor origin containment" do
1430 test "it rejects activities which reference objects with bogus origins" do
1431 data = %{
1432 "@context" => "https://www.w3.org/ns/activitystreams",
1433 "id" => "http://mastodon.example.org/users/admin/activities/1234",
1434 "actor" => "http://mastodon.example.org/users/admin",
1435 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1436 "object" => "https://info.pleroma.site/activity.json",
1437 "type" => "Announce"
1438 }
1439
1440 :error = Transmogrifier.handle_incoming(data)
1441 end
1442
1443 test "it rejects activities which reference objects that have an incorrect attribution (variant 1)" do
1444 data = %{
1445 "@context" => "https://www.w3.org/ns/activitystreams",
1446 "id" => "http://mastodon.example.org/users/admin/activities/1234",
1447 "actor" => "http://mastodon.example.org/users/admin",
1448 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1449 "object" => "https://info.pleroma.site/activity2.json",
1450 "type" => "Announce"
1451 }
1452
1453 :error = Transmogrifier.handle_incoming(data)
1454 end
1455
1456 test "it rejects activities which reference objects that have an incorrect attribution (variant 2)" do
1457 data = %{
1458 "@context" => "https://www.w3.org/ns/activitystreams",
1459 "id" => "http://mastodon.example.org/users/admin/activities/1234",
1460 "actor" => "http://mastodon.example.org/users/admin",
1461 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1462 "object" => "https://info.pleroma.site/activity3.json",
1463 "type" => "Announce"
1464 }
1465
1466 :error = Transmogrifier.handle_incoming(data)
1467 end
1468 end
1469
1470 describe "reserialization" do
1471 test "successfully reserializes a message with inReplyTo == nil" do
1472 user = insert(:user)
1473
1474 message = %{
1475 "@context" => "https://www.w3.org/ns/activitystreams",
1476 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1477 "cc" => [],
1478 "type" => "Create",
1479 "object" => %{
1480 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1481 "cc" => [],
1482 "type" => "Note",
1483 "content" => "Hi",
1484 "inReplyTo" => nil,
1485 "attributedTo" => user.ap_id
1486 },
1487 "actor" => user.ap_id
1488 }
1489
1490 {:ok, activity} = Transmogrifier.handle_incoming(message)
1491
1492 {:ok, _} = Transmogrifier.prepare_outgoing(activity.data)
1493 end
1494
1495 test "successfully reserializes a message with AS2 objects in IR" do
1496 user = insert(:user)
1497
1498 message = %{
1499 "@context" => "https://www.w3.org/ns/activitystreams",
1500 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1501 "cc" => [],
1502 "type" => "Create",
1503 "object" => %{
1504 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
1505 "cc" => [],
1506 "type" => "Note",
1507 "content" => "Hi",
1508 "inReplyTo" => nil,
1509 "attributedTo" => user.ap_id,
1510 "tag" => [
1511 %{"name" => "#2hu", "href" => "http://example.com/2hu", "type" => "Hashtag"},
1512 %{"name" => "Bob", "href" => "http://example.com/bob", "type" => "Mention"}
1513 ]
1514 },
1515 "actor" => user.ap_id
1516 }
1517
1518 {:ok, activity} = Transmogrifier.handle_incoming(message)
1519
1520 {:ok, _} = Transmogrifier.prepare_outgoing(activity.data)
1521 end
1522 end
1523
1524 test "Rewrites Answers to Notes" do
1525 user = insert(:user)
1526
1527 {:ok, poll_activity} =
1528 CommonAPI.post(user, %{
1529 "status" => "suya...",
1530 "poll" => %{"options" => ["suya", "suya.", "suya.."], "expires_in" => 10}
1531 })
1532
1533 poll_object = Object.normalize(poll_activity)
1534 # TODO: Replace with CommonAPI vote creation when implemented
1535 data =
1536 File.read!("test/fixtures/mastodon-vote.json")
1537 |> Poison.decode!()
1538 |> Kernel.put_in(["to"], user.ap_id)
1539 |> Kernel.put_in(["object", "inReplyTo"], poll_object.data["id"])
1540 |> Kernel.put_in(["object", "to"], user.ap_id)
1541
1542 {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data)
1543 {:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
1544
1545 assert data["object"]["type"] == "Note"
1546 end
1547
1548 describe "fix_explicit_addressing" do
1549 setup do
1550 user = insert(:user)
1551 [user: user]
1552 end
1553
1554 test "moves non-explicitly mentioned actors to cc", %{user: user} do
1555 explicitly_mentioned_actors = [
1556 "https://pleroma.gold/users/user1",
1557 "https://pleroma.gold/user2"
1558 ]
1559
1560 object = %{
1561 "actor" => user.ap_id,
1562 "to" => explicitly_mentioned_actors ++ ["https://social.beepboop.ga/users/dirb"],
1563 "cc" => [],
1564 "tag" =>
1565 Enum.map(explicitly_mentioned_actors, fn href ->
1566 %{"type" => "Mention", "href" => href}
1567 end)
1568 }
1569
1570 fixed_object = Transmogrifier.fix_explicit_addressing(object)
1571 assert Enum.all?(explicitly_mentioned_actors, &(&1 in fixed_object["to"]))
1572 refute "https://social.beepboop.ga/users/dirb" in fixed_object["to"]
1573 assert "https://social.beepboop.ga/users/dirb" in fixed_object["cc"]
1574 end
1575
1576 test "does not move actor's follower collection to cc", %{user: user} do
1577 object = %{
1578 "actor" => user.ap_id,
1579 "to" => [user.follower_address],
1580 "cc" => []
1581 }
1582
1583 fixed_object = Transmogrifier.fix_explicit_addressing(object)
1584 assert user.follower_address in fixed_object["to"]
1585 refute user.follower_address in fixed_object["cc"]
1586 end
1587
1588 test "removes recipient's follower collection from cc", %{user: user} do
1589 recipient = insert(:user)
1590
1591 object = %{
1592 "actor" => user.ap_id,
1593 "to" => [recipient.ap_id, "https://www.w3.org/ns/activitystreams#Public"],
1594 "cc" => [user.follower_address, recipient.follower_address]
1595 }
1596
1597 fixed_object = Transmogrifier.fix_explicit_addressing(object)
1598
1599 assert user.follower_address in fixed_object["cc"]
1600 refute recipient.follower_address in fixed_object["cc"]
1601 refute recipient.follower_address in fixed_object["to"]
1602 end
1603 end
1604
1605 describe "fix_summary/1" do
1606 test "returns fixed object" do
1607 assert Transmogrifier.fix_summary(%{"summary" => nil}) == %{"summary" => ""}
1608 assert Transmogrifier.fix_summary(%{"summary" => "ok"}) == %{"summary" => "ok"}
1609 assert Transmogrifier.fix_summary(%{}) == %{"summary" => ""}
1610 end
1611 end
1612
1613 describe "fix_in_reply_to/2" do
1614 clear_config([:instance, :federation_incoming_replies_max_depth])
1615
1616 setup do
1617 data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json"))
1618 [data: data]
1619 end
1620
1621 test "returns not modified object when hasn't containts inReplyTo field", %{data: data} do
1622 assert Transmogrifier.fix_in_reply_to(data) == data
1623 end
1624
1625 test "returns object with inReplyToAtomUri when denied incoming reply", %{data: data} do
1626 Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 0)
1627
1628 object_with_reply =
1629 Map.put(data["object"], "inReplyTo", "https://shitposter.club/notice/2827873")
1630
1631 modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
1632 assert modified_object["inReplyTo"] == "https://shitposter.club/notice/2827873"
1633 assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
1634
1635 object_with_reply =
1636 Map.put(data["object"], "inReplyTo", %{"id" => "https://shitposter.club/notice/2827873"})
1637
1638 modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
1639 assert modified_object["inReplyTo"] == %{"id" => "https://shitposter.club/notice/2827873"}
1640 assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
1641
1642 object_with_reply =
1643 Map.put(data["object"], "inReplyTo", ["https://shitposter.club/notice/2827873"])
1644
1645 modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
1646 assert modified_object["inReplyTo"] == ["https://shitposter.club/notice/2827873"]
1647 assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
1648
1649 object_with_reply = Map.put(data["object"], "inReplyTo", [])
1650 modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
1651 assert modified_object["inReplyTo"] == []
1652 assert modified_object["inReplyToAtomUri"] == ""
1653 end
1654
1655 test "returns modified object when allowed incoming reply", %{data: data} do
1656 object_with_reply =
1657 Map.put(
1658 data["object"],
1659 "inReplyTo",
1660 "https://shitposter.club/notice/2827873"
1661 )
1662
1663 Pleroma.Config.put([:instance, :federation_incoming_replies_max_depth], 5)
1664 modified_object = Transmogrifier.fix_in_reply_to(object_with_reply)
1665
1666 assert modified_object["inReplyTo"] ==
1667 "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
1668
1669 assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
1670
1671 assert modified_object["conversation"] ==
1672 "tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26"
1673
1674 assert modified_object["context"] ==
1675 "tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26"
1676 end
1677 end
1678
1679 describe "fix_url/1" do
1680 test "fixes data for object when url is map" do
1681 object = %{
1682 "url" => %{
1683 "type" => "Link",
1684 "mimeType" => "video/mp4",
1685 "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4"
1686 }
1687 }
1688
1689 assert Transmogrifier.fix_url(object) == %{
1690 "url" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4"
1691 }
1692 end
1693
1694 test "fixes data for video object" do
1695 object = %{
1696 "type" => "Video",
1697 "url" => [
1698 %{
1699 "type" => "Link",
1700 "mimeType" => "video/mp4",
1701 "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4"
1702 },
1703 %{
1704 "type" => "Link",
1705 "mimeType" => "video/mp4",
1706 "href" => "https://peertube46fb-ad81-2d4c2d1630e3-240.mp4"
1707 },
1708 %{
1709 "type" => "Link",
1710 "mimeType" => "text/html",
1711 "href" => "https://peertube.-2d4c2d1630e3"
1712 },
1713 %{
1714 "type" => "Link",
1715 "mimeType" => "text/html",
1716 "href" => "https://peertube.-2d4c2d16377-42"
1717 }
1718 ]
1719 }
1720
1721 assert Transmogrifier.fix_url(object) == %{
1722 "attachment" => [
1723 %{
1724 "href" => "https://peede8d-46fb-ad81-2d4c2d1630e3-480.mp4",
1725 "mimeType" => "video/mp4",
1726 "type" => "Link"
1727 }
1728 ],
1729 "type" => "Video",
1730 "url" => "https://peertube.-2d4c2d1630e3"
1731 }
1732 end
1733
1734 test "fixes url for not Video object" do
1735 object = %{
1736 "type" => "Text",
1737 "url" => [
1738 %{
1739 "type" => "Link",
1740 "mimeType" => "text/html",
1741 "href" => "https://peertube.-2d4c2d1630e3"
1742 },
1743 %{
1744 "type" => "Link",
1745 "mimeType" => "text/html",
1746 "href" => "https://peertube.-2d4c2d16377-42"
1747 }
1748 ]
1749 }
1750
1751 assert Transmogrifier.fix_url(object) == %{
1752 "type" => "Text",
1753 "url" => "https://peertube.-2d4c2d1630e3"
1754 }
1755
1756 assert Transmogrifier.fix_url(%{"type" => "Text", "url" => []}) == %{
1757 "type" => "Text",
1758 "url" => ""
1759 }
1760 end
1761
1762 test "retunrs not modified object" do
1763 assert Transmogrifier.fix_url(%{"type" => "Text"}) == %{"type" => "Text"}
1764 end
1765 end
1766
1767 describe "get_obj_helper/2" do
1768 test "returns nil when cannot normalize object" do
1769 refute Transmogrifier.get_obj_helper("test-obj-id")
1770 end
1771
1772 test "returns {:ok, %Object{}} for success case" do
1773 assert {:ok, %Object{}} =
1774 Transmogrifier.get_obj_helper("https://shitposter.club/notice/2827873")
1775 end
1776 end
1777
1778 describe "fix_attachments/1" do
1779 test "returns not modified object" do
1780 data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json"))
1781 assert Transmogrifier.fix_attachments(data) == data
1782 end
1783
1784 test "returns modified object when attachment is map" do
1785 assert Transmogrifier.fix_attachments(%{
1786 "attachment" => %{
1787 "mediaType" => "video/mp4",
1788 "url" => "https://peertube.moe/stat-480.mp4"
1789 }
1790 }) == %{
1791 "attachment" => [
1792 %{
1793 "mediaType" => "video/mp4",
1794 "url" => [
1795 %{
1796 "href" => "https://peertube.moe/stat-480.mp4",
1797 "mediaType" => "video/mp4",
1798 "type" => "Link"
1799 }
1800 ]
1801 }
1802 ]
1803 }
1804 end
1805
1806 test "returns modified object when attachment is list" do
1807 assert Transmogrifier.fix_attachments(%{
1808 "attachment" => [
1809 %{"mediaType" => "video/mp4", "url" => "https://pe.er/stat-480.mp4"},
1810 %{"mimeType" => "video/mp4", "href" => "https://pe.er/stat-480.mp4"}
1811 ]
1812 }) == %{
1813 "attachment" => [
1814 %{
1815 "mediaType" => "video/mp4",
1816 "url" => [
1817 %{
1818 "href" => "https://pe.er/stat-480.mp4",
1819 "mediaType" => "video/mp4",
1820 "type" => "Link"
1821 }
1822 ]
1823 },
1824 %{
1825 "href" => "https://pe.er/stat-480.mp4",
1826 "mediaType" => "video/mp4",
1827 "mimeType" => "video/mp4",
1828 "url" => [
1829 %{
1830 "href" => "https://pe.er/stat-480.mp4",
1831 "mediaType" => "video/mp4",
1832 "type" => "Link"
1833 }
1834 ]
1835 }
1836 ]
1837 }
1838 end
1839 end
1840
1841 describe "fix_emoji/1" do
1842 test "returns not modified object when object not contains tags" do
1843 data = Poison.decode!(File.read!("test/fixtures/mastodon-post-activity.json"))
1844 assert Transmogrifier.fix_emoji(data) == data
1845 end
1846
1847 test "returns object with emoji when object contains list tags" do
1848 assert Transmogrifier.fix_emoji(%{
1849 "tag" => [
1850 %{"type" => "Emoji", "name" => ":bib:", "icon" => %{"url" => "/test"}},
1851 %{"type" => "Hashtag"}
1852 ]
1853 }) == %{
1854 "emoji" => %{"bib" => "/test"},
1855 "tag" => [
1856 %{"icon" => %{"url" => "/test"}, "name" => ":bib:", "type" => "Emoji"},
1857 %{"type" => "Hashtag"}
1858 ]
1859 }
1860 end
1861
1862 test "returns object with emoji when object contains map tag" do
1863 assert Transmogrifier.fix_emoji(%{
1864 "tag" => %{"type" => "Emoji", "name" => ":bib:", "icon" => %{"url" => "/test"}}
1865 }) == %{
1866 "emoji" => %{"bib" => "/test"},
1867 "tag" => %{"icon" => %{"url" => "/test"}, "name" => ":bib:", "type" => "Emoji"}
1868 }
1869 end
1870 end
1871 end