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