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