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