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