tests: add additional spoofing tests
[akkoma] / test / web / activity_pub / transmogrifier_test.exs
1 defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
2 use Pleroma.DataCase
3 alias Pleroma.Web.ActivityPub.Transmogrifier
4 alias Pleroma.Web.ActivityPub.Utils
5 alias Pleroma.Web.ActivityPub.ActivityPub
6 alias Pleroma.Web.OStatus
7 alias Pleroma.Activity
8 alias Pleroma.User
9 alias Pleroma.Repo
10 alias Pleroma.Web.Websub.WebsubClientSubscription
11
12 import Pleroma.Factory
13 alias Pleroma.Web.CommonAPI
14
15 describe "handle_incoming" do
16 test "it ignores an incoming notice if we already have it" do
17 activity = insert(:note_activity)
18
19 data =
20 File.read!("test/fixtures/mastodon-post-activity.json")
21 |> Poison.decode!()
22 |> Map.put("object", activity.data["object"])
23
24 {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
25
26 assert activity == returned_activity
27 end
28
29 test "it fetches replied-to activities if we don't have them" do
30 data =
31 File.read!("test/fixtures/mastodon-post-activity.json")
32 |> Poison.decode!()
33
34 object =
35 data["object"]
36 |> Map.put("inReplyTo", "https://shitposter.club/notice/2827873")
37
38 data =
39 data
40 |> Map.put("object", object)
41
42 {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
43
44 assert activity =
45 Activity.get_create_activity_by_object_ap_id(
46 "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
47 )
48
49 assert returned_activity.data["object"]["inReplyToAtomUri"] ==
50 "https://shitposter.club/notice/2827873"
51
52 assert returned_activity.data["object"]["inReplyToStatusId"] == activity.id
53 end
54
55 test "it works for incoming notices" do
56 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
57
58 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
59
60 assert data["id"] ==
61 "http://mastodon.example.org/users/admin/statuses/99512778738411822/activity"
62
63 assert data["context"] ==
64 "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation"
65
66 assert data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
67
68 assert data["cc"] == [
69 "http://mastodon.example.org/users/admin/followers",
70 "http://localtesting.pleroma.lol/users/lain"
71 ]
72
73 assert data["actor"] == "http://mastodon.example.org/users/admin"
74
75 object = data["object"]
76 assert object["id"] == "http://mastodon.example.org/users/admin/statuses/99512778738411822"
77
78 assert object["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
79
80 assert object["cc"] == [
81 "http://mastodon.example.org/users/admin/followers",
82 "http://localtesting.pleroma.lol/users/lain"
83 ]
84
85 assert object["actor"] == "http://mastodon.example.org/users/admin"
86 assert object["attributedTo"] == "http://mastodon.example.org/users/admin"
87
88 assert object["context"] ==
89 "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation"
90
91 assert object["sensitive"] == true
92
93 user = User.get_by_ap_id(object["actor"])
94
95 assert user.info["note_count"] == 1
96 end
97
98 test "it works for incoming notices with hashtags" do
99 data = File.read!("test/fixtures/mastodon-post-activity-hashtag.json") |> Poison.decode!()
100
101 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
102 assert Enum.at(data["object"]["tag"], 2) == "moo"
103 end
104
105 test "it works for incoming notices with contentMap" do
106 data =
107 File.read!("test/fixtures/mastodon-post-activity-contentmap.json") |> Poison.decode!()
108
109 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
110
111 assert data["object"]["content"] ==
112 "<p><span class=\"h-card\"><a href=\"http://localtesting.pleroma.lol/users/lain\" class=\"u-url mention\">@<span>lain</span></a></span></p>"
113 end
114
115 test "it works for incoming notices with to/cc not being an array (kroeg)" do
116 data = File.read!("test/fixtures/kroeg-post-activity.json") |> Poison.decode!()
117
118 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
119
120 assert data["object"]["content"] ==
121 "<p>henlo from my Psion netBook</p><p>message sent from my Psion netBook</p>"
122 end
123
124 test "it works for incoming announces with actor being inlined (kroeg)" do
125 data = File.read!("test/fixtures/kroeg-announce-with-inline-actor.json") |> Poison.decode!()
126
127 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
128
129 assert data["actor"] == "https://puckipedia.com/"
130 end
131
132 test "it works for incoming notices with tag not being an array (kroeg)" do
133 data = File.read!("test/fixtures/kroeg-array-less-emoji.json") |> Poison.decode!()
134
135 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
136
137 assert data["object"]["emoji"] == %{
138 "icon_e_smile" => "https://puckipedia.com/forum/images/smilies/icon_e_smile.png"
139 }
140
141 data = File.read!("test/fixtures/kroeg-array-less-hashtag.json") |> Poison.decode!()
142
143 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
144
145 assert "test" in data["object"]["tag"]
146 end
147
148 test "it works for incoming notices with url not being a string (prismo)" do
149 data = File.read!("test/fixtures/prismo-url-map.json") |> Poison.decode!()
150
151 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
152
153 assert data["object"]["url"] == "https://prismo.news/posts/83"
154 end
155
156 test "it works for incoming follow requests" do
157 user = insert(:user)
158
159 data =
160 File.read!("test/fixtures/mastodon-follow-activity.json")
161 |> Poison.decode!()
162 |> Map.put("object", user.ap_id)
163
164 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
165
166 assert data["actor"] == "http://mastodon.example.org/users/admin"
167 assert data["type"] == "Follow"
168 assert data["id"] == "http://mastodon.example.org/users/admin#follows/2"
169 assert User.following?(User.get_by_ap_id(data["actor"]), user)
170 end
171
172 test "it works for incoming follow requests from hubzilla" do
173 user = insert(:user)
174
175 data =
176 File.read!("test/fixtures/hubzilla-follow-activity.json")
177 |> Poison.decode!()
178 |> Map.put("object", user.ap_id)
179 |> Utils.normalize_params()
180
181 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
182
183 assert data["actor"] == "https://hubzilla.example.org/channel/kaniini"
184 assert data["type"] == "Follow"
185 assert data["id"] == "https://hubzilla.example.org/channel/kaniini#follows/2"
186 assert User.following?(User.get_by_ap_id(data["actor"]), user)
187 end
188
189 test "it works for incoming likes" do
190 user = insert(:user)
191 {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
192
193 data =
194 File.read!("test/fixtures/mastodon-like.json")
195 |> Poison.decode!()
196 |> Map.put("object", activity.data["object"]["id"])
197
198 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
199
200 assert data["actor"] == "http://mastodon.example.org/users/admin"
201 assert data["type"] == "Like"
202 assert data["id"] == "http://mastodon.example.org/users/admin#likes/2"
203 assert data["object"] == activity.data["object"]["id"]
204 end
205
206 test "it returns an error for incoming unlikes wihout a like activity" do
207 user = insert(:user)
208 {:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"})
209
210 data =
211 File.read!("test/fixtures/mastodon-undo-like.json")
212 |> Poison.decode!()
213 |> Map.put("object", activity.data["object"]["id"])
214
215 assert Transmogrifier.handle_incoming(data) == :error
216 end
217
218 test "it works for incoming unlikes with an existing like activity" do
219 user = insert(:user)
220 {:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"})
221
222 like_data =
223 File.read!("test/fixtures/mastodon-like.json")
224 |> Poison.decode!()
225 |> Map.put("object", activity.data["object"]["id"])
226
227 {:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data)
228
229 data =
230 File.read!("test/fixtures/mastodon-undo-like.json")
231 |> Poison.decode!()
232 |> Map.put("object", like_data)
233 |> Map.put("actor", like_data["actor"])
234
235 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
236
237 assert data["actor"] == "http://mastodon.example.org/users/admin"
238 assert data["type"] == "Undo"
239 assert data["id"] == "http://mastodon.example.org/users/admin#likes/2/undo"
240 assert data["object"]["id"] == "http://mastodon.example.org/users/admin#likes/2"
241 end
242
243 test "it works for incoming announces" do
244 data = File.read!("test/fixtures/mastodon-announce.json") |> Poison.decode!()
245
246 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
247
248 assert data["actor"] == "http://mastodon.example.org/users/admin"
249 assert data["type"] == "Announce"
250
251 assert data["id"] ==
252 "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
253
254 assert data["object"] ==
255 "http://mastodon.example.org/users/admin/statuses/99541947525187367"
256
257 assert Activity.get_create_activity_by_object_ap_id(data["object"])
258 end
259
260 test "it works for incoming announces with an existing activity" do
261 user = insert(:user)
262 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
263
264 data =
265 File.read!("test/fixtures/mastodon-announce.json")
266 |> Poison.decode!()
267 |> Map.put("object", activity.data["object"]["id"])
268
269 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
270
271 assert data["actor"] == "http://mastodon.example.org/users/admin"
272 assert data["type"] == "Announce"
273
274 assert data["id"] ==
275 "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
276
277 assert data["object"] == activity.data["object"]["id"]
278
279 assert Activity.get_create_activity_by_object_ap_id(data["object"]).id == activity.id
280 end
281
282 test "it works for incoming update activities" do
283 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
284
285 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
286 update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
287
288 object =
289 update_data["object"]
290 |> Map.put("actor", data["actor"])
291 |> Map.put("id", data["actor"])
292
293 update_data =
294 update_data
295 |> Map.put("actor", data["actor"])
296 |> Map.put("object", object)
297
298 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
299
300 user = User.get_cached_by_ap_id(data["actor"])
301 assert user.name == "gargle"
302
303 assert user.avatar["url"] == [
304 %{
305 "href" =>
306 "https://cd.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
307 }
308 ]
309
310 assert user.info["banner"]["url"] == [
311 %{
312 "href" =>
313 "https://cd.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
314 }
315 ]
316
317 assert user.bio == "<p>Some bio</p>"
318 end
319
320 test "it works for incoming update activities which lock the account" do
321 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
322
323 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
324 update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
325
326 object =
327 update_data["object"]
328 |> Map.put("actor", data["actor"])
329 |> Map.put("id", data["actor"])
330 |> Map.put("manuallyApprovesFollowers", true)
331
332 update_data =
333 update_data
334 |> Map.put("actor", data["actor"])
335 |> Map.put("object", object)
336
337 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
338
339 user = User.get_cached_by_ap_id(data["actor"])
340 assert user.info["locked"] == true
341 end
342
343 test "it works for incoming deletes" do
344 activity = insert(:note_activity)
345
346 data =
347 File.read!("test/fixtures/mastodon-delete.json")
348 |> Poison.decode!()
349
350 object =
351 data["object"]
352 |> Map.put("id", activity.data["object"]["id"])
353
354 data =
355 data
356 |> Map.put("object", object)
357 |> Map.put("actor", activity.data["actor"])
358
359 {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data)
360
361 refute Repo.get(Activity, activity.id)
362 end
363
364 test "it works for incoming unannounces with an existing notice" do
365 user = insert(:user)
366 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
367
368 announce_data =
369 File.read!("test/fixtures/mastodon-announce.json")
370 |> Poison.decode!()
371 |> Map.put("object", activity.data["object"]["id"])
372
373 {:ok, %Activity{data: announce_data, local: false}} =
374 Transmogrifier.handle_incoming(announce_data)
375
376 data =
377 File.read!("test/fixtures/mastodon-undo-announce.json")
378 |> Poison.decode!()
379 |> Map.put("object", announce_data)
380 |> Map.put("actor", announce_data["actor"])
381
382 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
383
384 assert data["type"] == "Undo"
385 assert data["object"]["type"] == "Announce"
386 assert data["object"]["object"] == activity.data["object"]["id"]
387
388 assert data["object"]["id"] ==
389 "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
390 end
391
392 test "it works for incomming unfollows with an existing follow" do
393 user = insert(:user)
394
395 follow_data =
396 File.read!("test/fixtures/mastodon-follow-activity.json")
397 |> Poison.decode!()
398 |> Map.put("object", user.ap_id)
399
400 {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(follow_data)
401
402 data =
403 File.read!("test/fixtures/mastodon-unfollow-activity.json")
404 |> Poison.decode!()
405 |> Map.put("object", follow_data)
406
407 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
408
409 assert data["type"] == "Undo"
410 assert data["object"]["type"] == "Follow"
411 assert data["object"]["object"] == user.ap_id
412 assert data["actor"] == "http://mastodon.example.org/users/admin"
413
414 refute User.following?(User.get_by_ap_id(data["actor"]), user)
415 end
416
417 test "it works for incoming blocks" do
418 user = insert(:user)
419
420 data =
421 File.read!("test/fixtures/mastodon-block-activity.json")
422 |> Poison.decode!()
423 |> Map.put("object", user.ap_id)
424
425 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
426
427 assert data["type"] == "Block"
428 assert data["object"] == user.ap_id
429 assert data["actor"] == "http://mastodon.example.org/users/admin"
430
431 blocker = User.get_by_ap_id(data["actor"])
432
433 assert User.blocks?(blocker, user)
434 end
435
436 test "incoming blocks successfully tear down any follow relationship" do
437 blocker = insert(:user)
438 blocked = insert(:user)
439
440 data =
441 File.read!("test/fixtures/mastodon-block-activity.json")
442 |> Poison.decode!()
443 |> Map.put("object", blocked.ap_id)
444 |> Map.put("actor", blocker.ap_id)
445
446 {:ok, blocker} = User.follow(blocker, blocked)
447 {:ok, blocked} = User.follow(blocked, blocker)
448
449 assert User.following?(blocker, blocked)
450 assert User.following?(blocked, blocker)
451
452 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
453
454 assert data["type"] == "Block"
455 assert data["object"] == blocked.ap_id
456 assert data["actor"] == blocker.ap_id
457
458 blocker = User.get_by_ap_id(data["actor"])
459 blocked = User.get_by_ap_id(data["object"])
460
461 assert User.blocks?(blocker, blocked)
462
463 refute User.following?(blocker, blocked)
464 refute User.following?(blocked, blocker)
465 end
466
467 test "it works for incoming unblocks with an existing block" do
468 user = insert(:user)
469
470 block_data =
471 File.read!("test/fixtures/mastodon-block-activity.json")
472 |> Poison.decode!()
473 |> Map.put("object", user.ap_id)
474
475 {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(block_data)
476
477 data =
478 File.read!("test/fixtures/mastodon-unblock-activity.json")
479 |> Poison.decode!()
480 |> Map.put("object", block_data)
481
482 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
483 assert data["type"] == "Undo"
484 assert data["object"]["type"] == "Block"
485 assert data["object"]["object"] == user.ap_id
486 assert data["actor"] == "http://mastodon.example.org/users/admin"
487
488 blocker = User.get_by_ap_id(data["actor"])
489
490 refute User.blocks?(blocker, user)
491 end
492
493 test "it works for incoming accepts which were pre-accepted" do
494 follower = insert(:user)
495 followed = insert(:user)
496
497 {:ok, follower} = User.follow(follower, followed)
498 assert User.following?(follower, followed) == true
499
500 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
501
502 accept_data =
503 File.read!("test/fixtures/mastodon-accept-activity.json")
504 |> Poison.decode!()
505 |> Map.put("actor", followed.ap_id)
506
507 object =
508 accept_data["object"]
509 |> Map.put("actor", follower.ap_id)
510 |> Map.put("id", follow_activity.data["id"])
511
512 accept_data = Map.put(accept_data, "object", object)
513
514 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
515 refute activity.local
516
517 assert activity.data["object"] == follow_activity.data["id"]
518
519 follower = Repo.get(User, follower.id)
520
521 assert User.following?(follower, followed) == true
522 end
523
524 test "it works for incoming accepts which were orphaned" do
525 follower = insert(:user)
526 followed = insert(:user, %{info: %{"locked" => true}})
527
528 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
529
530 accept_data =
531 File.read!("test/fixtures/mastodon-accept-activity.json")
532 |> Poison.decode!()
533 |> Map.put("actor", followed.ap_id)
534
535 accept_data =
536 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
537
538 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
539 assert activity.data["object"] == follow_activity.data["id"]
540
541 follower = Repo.get(User, follower.id)
542
543 assert User.following?(follower, followed) == true
544 end
545
546 test "it works for incoming accepts which are referenced by IRI only" do
547 follower = insert(:user)
548 followed = insert(:user, %{info: %{"locked" => true}})
549
550 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
551
552 accept_data =
553 File.read!("test/fixtures/mastodon-accept-activity.json")
554 |> Poison.decode!()
555 |> Map.put("actor", followed.ap_id)
556 |> Map.put("object", follow_activity.data["id"])
557
558 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
559 assert activity.data["object"] == follow_activity.data["id"]
560
561 follower = Repo.get(User, follower.id)
562
563 assert User.following?(follower, followed) == true
564 end
565
566 test "it fails for incoming accepts which cannot be correlated" do
567 follower = insert(:user)
568 followed = insert(:user, %{info: %{"locked" => true}})
569
570 accept_data =
571 File.read!("test/fixtures/mastodon-accept-activity.json")
572 |> Poison.decode!()
573 |> Map.put("actor", followed.ap_id)
574
575 accept_data =
576 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
577
578 :error = Transmogrifier.handle_incoming(accept_data)
579
580 follower = Repo.get(User, follower.id)
581
582 refute User.following?(follower, followed) == true
583 end
584
585 test "it fails for incoming rejects which cannot be correlated" do
586 follower = insert(:user)
587 followed = insert(:user, %{info: %{"locked" => true}})
588
589 accept_data =
590 File.read!("test/fixtures/mastodon-reject-activity.json")
591 |> Poison.decode!()
592 |> Map.put("actor", followed.ap_id)
593
594 accept_data =
595 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
596
597 :error = Transmogrifier.handle_incoming(accept_data)
598
599 follower = Repo.get(User, follower.id)
600
601 refute User.following?(follower, followed) == true
602 end
603
604 test "it works for incoming rejects which are orphaned" do
605 follower = insert(:user)
606 followed = insert(:user, %{info: %{"locked" => true}})
607
608 {:ok, follower} = User.follow(follower, followed)
609 {:ok, _follow_activity} = ActivityPub.follow(follower, followed)
610
611 assert User.following?(follower, followed) == true
612
613 reject_data =
614 File.read!("test/fixtures/mastodon-reject-activity.json")
615 |> Poison.decode!()
616 |> Map.put("actor", followed.ap_id)
617
618 reject_data =
619 Map.put(reject_data, "object", Map.put(reject_data["object"], "actor", follower.ap_id))
620
621 {:ok, activity} = Transmogrifier.handle_incoming(reject_data)
622 refute activity.local
623
624 follower = Repo.get(User, follower.id)
625
626 assert User.following?(follower, followed) == false
627 end
628
629 test "it works for incoming rejects which are referenced by IRI only" do
630 follower = insert(:user)
631 followed = insert(:user, %{info: %{"locked" => true}})
632
633 {:ok, follower} = User.follow(follower, followed)
634 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
635
636 assert User.following?(follower, followed) == true
637
638 reject_data =
639 File.read!("test/fixtures/mastodon-reject-activity.json")
640 |> Poison.decode!()
641 |> Map.put("actor", followed.ap_id)
642 |> Map.put("object", follow_activity.data["id"])
643
644 {:ok, %Activity{data: _}} = Transmogrifier.handle_incoming(reject_data)
645
646 follower = Repo.get(User, follower.id)
647
648 assert User.following?(follower, followed) == false
649 end
650
651 test "it rejects activities without a valid ID" do
652 user = insert(:user)
653
654 data =
655 File.read!("test/fixtures/mastodon-follow-activity.json")
656 |> Poison.decode!()
657 |> Map.put("object", user.ap_id)
658 |> Map.put("id", "")
659
660 :error = Transmogrifier.handle_incoming(data)
661 end
662 end
663
664 describe "prepare outgoing" do
665 test "it turns mentions into tags" do
666 user = insert(:user)
667 other_user = insert(:user)
668
669 {:ok, activity} =
670 CommonAPI.post(user, %{"status" => "hey, @#{other_user.nickname}, how are ya? #2hu"})
671
672 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
673 object = modified["object"]
674
675 expected_mention = %{
676 "href" => other_user.ap_id,
677 "name" => "@#{other_user.nickname}",
678 "type" => "Mention"
679 }
680
681 expected_tag = %{
682 "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
683 "type" => "Hashtag",
684 "name" => "#2hu"
685 }
686
687 assert Enum.member?(object["tag"], expected_tag)
688 assert Enum.member?(object["tag"], expected_mention)
689 end
690
691 test "it adds the sensitive property" do
692 user = insert(:user)
693
694 {:ok, activity} = CommonAPI.post(user, %{"status" => "#nsfw hey"})
695 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
696
697 assert modified["object"]["sensitive"]
698 end
699
700 test "it adds the json-ld context and the conversation property" do
701 user = insert(:user)
702
703 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
704 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
705
706 assert modified["@context"] ==
707 Pleroma.Web.ActivityPub.Utils.make_json_ld_header()["@context"]
708
709 assert modified["object"]["conversation"] == modified["context"]
710 end
711
712 test "it sets the 'attributedTo' property to the actor of the object if it doesn't have one" do
713 user = insert(:user)
714
715 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
716 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
717
718 assert modified["object"]["actor"] == modified["object"]["attributedTo"]
719 end
720
721 test "it translates ostatus IDs to external URLs" do
722 incoming = File.read!("test/fixtures/incoming_note_activity.xml")
723 {:ok, [referent_activity]} = OStatus.handle_incoming(incoming)
724
725 user = insert(:user)
726
727 {:ok, activity, _} = CommonAPI.favorite(referent_activity.id, user)
728 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
729
730 assert modified["object"] == "http://gs.example.org:4040/index.php/notice/29"
731 end
732
733 test "it translates ostatus reply_to IDs to external URLs" do
734 incoming = File.read!("test/fixtures/incoming_note_activity.xml")
735 {:ok, [referred_activity]} = OStatus.handle_incoming(incoming)
736
737 user = insert(:user)
738
739 {:ok, activity} =
740 CommonAPI.post(user, %{"status" => "HI!", "in_reply_to_status_id" => referred_activity.id})
741
742 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
743
744 assert modified["object"]["inReplyTo"] == "http://gs.example.org:4040/index.php/notice/29"
745 end
746
747 test "it strips internal hashtag data" do
748 user = insert(:user)
749
750 {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu"})
751
752 expected_tag = %{
753 "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
754 "type" => "Hashtag",
755 "name" => "#2hu"
756 }
757
758 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
759
760 assert modified["object"]["tag"] == [expected_tag]
761 end
762
763 test "it strips internal fields" do
764 user = insert(:user)
765
766 {:ok, activity} = CommonAPI.post(user, %{"status" => "#2hu :moominmamma:"})
767
768 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
769
770 assert length(modified["object"]["tag"]) == 2
771
772 assert is_nil(modified["object"]["emoji"])
773 assert is_nil(modified["object"]["likes"])
774 assert is_nil(modified["object"]["like_count"])
775 assert is_nil(modified["object"]["announcements"])
776 assert is_nil(modified["object"]["announcement_count"])
777 assert is_nil(modified["object"]["context_id"])
778 end
779 end
780
781 describe "user upgrade" do
782 test "it upgrades a user to activitypub" do
783 user =
784 insert(:user, %{
785 nickname: "rye@niu.moe",
786 local: false,
787 ap_id: "https://niu.moe/users/rye",
788 follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"})
789 })
790
791 user_two = insert(:user, %{following: [user.follower_address]})
792
793 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
794 {:ok, unrelated_activity} = CommonAPI.post(user_two, %{"status" => "test"})
795 assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients
796
797 user = Repo.get(User, user.id)
798 assert user.info["note_count"] == 1
799
800 {:ok, user} = Transmogrifier.upgrade_user_from_ap_id("https://niu.moe/users/rye")
801 assert user.info["ap_enabled"]
802 assert user.info["note_count"] == 1
803 assert user.follower_address == "https://niu.moe/users/rye/followers"
804
805 # Wait for the background task
806 :timer.sleep(1000)
807
808 user = Repo.get(User, user.id)
809 assert user.info["note_count"] == 1
810
811 activity = Repo.get(Activity, activity.id)
812 assert user.follower_address in activity.recipients
813
814 assert %{
815 "url" => [
816 %{
817 "href" =>
818 "https://cdn.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
819 }
820 ]
821 } = user.avatar
822
823 assert %{
824 "url" => [
825 %{
826 "href" =>
827 "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
828 }
829 ]
830 } = user.info["banner"]
831
832 refute "..." in activity.recipients
833
834 unrelated_activity = Repo.get(Activity, unrelated_activity.id)
835 refute user.follower_address in unrelated_activity.recipients
836
837 user_two = Repo.get(User, user_two.id)
838 assert user.follower_address in user_two.following
839 refute "..." in user_two.following
840 end
841 end
842
843 describe "maybe_retire_websub" do
844 test "it deletes all websub client subscripitions with the user as topic" do
845 subscription = %WebsubClientSubscription{topic: "https://niu.moe/users/rye.atom"}
846 {:ok, ws} = Repo.insert(subscription)
847
848 subscription = %WebsubClientSubscription{topic: "https://niu.moe/users/pasty.atom"}
849 {:ok, ws2} = Repo.insert(subscription)
850
851 Transmogrifier.maybe_retire_websub("https://niu.moe/users/rye")
852
853 refute Repo.get(WebsubClientSubscription, ws.id)
854 assert Repo.get(WebsubClientSubscription, ws2.id)
855 end
856 end
857
858 describe "actor rewriting" do
859 test "it fixes the actor URL property to be a proper URI" do
860 data = %{
861 "url" => %{"href" => "http://example.com"}
862 }
863
864 rewritten = Transmogrifier.maybe_fix_user_object(data)
865 assert rewritten["url"] == "http://example.com"
866 end
867 end
868
869 describe "actor origin containment" do
870 test "it rejects objects with a bogus origin" do
871 {:error, _} = ActivityPub.fetch_object_from_id("https://info.pleroma.site/activity.json")
872 end
873
874 test "it rejects activities which reference objects with bogus origins" do
875 data = %{
876 "@context" => "https://www.w3.org/ns/activitystreams",
877 "id" => "https://mastodon.example.org/users/admin/activities/1234",
878 "actor" => "https://mastodon.example.org/users/admin",
879 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
880 "object" => "https://info.pleroma.site/activity.json",
881 "type" => "Announce"
882 }
883
884 :error = Transmogrifier.handle_incoming(data)
885 end
886
887 test "it rejects objects when the ID does not match the fetched URI" do
888 {:error, _} = ActivityPub.fetch_object_from_id("https://info.pleroma.site/activity2.json")
889 end
890
891 test "it rejects activities which reference objects by mismatched URI" do
892 data = %{
893 "@context" => "https://www.w3.org/ns/activitystreams",
894 "id" => "http://mastodon.example.org/users/admin/activities/1234",
895 "actor" => "http://mastodon.example.org/users/admin",
896 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
897 "object" => "https://info.pleroma.site/activity2.json",
898 "type" => "Announce"
899 }
900
901 :error = Transmogrifier.handle_incoming(data)
902 end
903 end
904 end