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