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