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