transmogrifier: Add support for array-less hashtags, add broken announce, harden...
[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 # Broken ;/
125 #
126 # test "it works for incoming announces with actor being inlined (kroeg)" do
127 # data = File.read!("test/fixtures/kroeg-announce-with-inline-actor.json") |> Poison.decode!()
128 #
129 # {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
130 #
131 # assert data["object"]["actor"] == "https://puckipedia.com/"
132 # end
133
134 test "it works for incoming notices with tag not being an array (kroeg)" do
135 data = File.read!("test/fixtures/kroeg-array-less-emoji.json") |> Poison.decode!()
136
137 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
138
139 assert data["object"]["emoji"] == %{
140 "icon_e_smile" => "https://puckipedia.com/forum/images/smilies/icon_e_smile.png"
141 }
142
143 data = File.read!("test/fixtures/kroeg-array-less-hashtag.json") |> Poison.decode!()
144
145 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
146
147 assert "test" in data["object"]["tag"]
148 end
149
150 test "it works for incoming follow requests" do
151 user = insert(:user)
152
153 data =
154 File.read!("test/fixtures/mastodon-follow-activity.json")
155 |> Poison.decode!()
156 |> Map.put("object", user.ap_id)
157
158 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
159
160 assert data["actor"] == "http://mastodon.example.org/users/admin"
161 assert data["type"] == "Follow"
162 assert data["id"] == "http://mastodon.example.org/users/admin#follows/2"
163 assert User.following?(User.get_by_ap_id(data["actor"]), user)
164 end
165
166 test "it works for incoming follow requests from hubzilla" do
167 user = insert(:user)
168
169 data =
170 File.read!("test/fixtures/hubzilla-follow-activity.json")
171 |> Poison.decode!()
172 |> Map.put("object", user.ap_id)
173 |> Utils.normalize_params()
174
175 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
176
177 assert data["actor"] == "https://hubzilla.example.org/channel/kaniini"
178 assert data["type"] == "Follow"
179 assert data["id"] == "https://hubzilla.example.org/channel/kaniini#follows/2"
180 assert User.following?(User.get_by_ap_id(data["actor"]), user)
181 end
182
183 test "it works for incoming likes" do
184 user = insert(:user)
185 {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
186
187 data =
188 File.read!("test/fixtures/mastodon-like.json")
189 |> Poison.decode!()
190 |> Map.put("object", activity.data["object"]["id"])
191
192 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
193
194 assert data["actor"] == "http://mastodon.example.org/users/admin"
195 assert data["type"] == "Like"
196 assert data["id"] == "http://mastodon.example.org/users/admin#likes/2"
197 assert data["object"] == activity.data["object"]["id"]
198 end
199
200 test "it returns an error for incoming unlikes wihout a like activity" do
201 user = insert(:user)
202 {:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"})
203
204 data =
205 File.read!("test/fixtures/mastodon-undo-like.json")
206 |> Poison.decode!()
207 |> Map.put("object", activity.data["object"]["id"])
208
209 assert Transmogrifier.handle_incoming(data) == :error
210 end
211
212 test "it works for incoming unlikes with an existing like activity" do
213 user = insert(:user)
214 {:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"})
215
216 like_data =
217 File.read!("test/fixtures/mastodon-like.json")
218 |> Poison.decode!()
219 |> Map.put("object", activity.data["object"]["id"])
220
221 {:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data)
222
223 data =
224 File.read!("test/fixtures/mastodon-undo-like.json")
225 |> Poison.decode!()
226 |> Map.put("object", like_data)
227 |> Map.put("actor", like_data["actor"])
228
229 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
230
231 assert data["actor"] == "http://mastodon.example.org/users/admin"
232 assert data["type"] == "Undo"
233 assert data["id"] == "http://mastodon.example.org/users/admin#likes/2/undo"
234 assert data["object"]["id"] == "http://mastodon.example.org/users/admin#likes/2"
235 end
236
237 test "it works for incoming announces" do
238 data = File.read!("test/fixtures/mastodon-announce.json") |> Poison.decode!()
239
240 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
241
242 assert data["actor"] == "http://mastodon.example.org/users/admin"
243 assert data["type"] == "Announce"
244
245 assert data["id"] ==
246 "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
247
248 assert data["object"] ==
249 "http://mastodon.example.org/users/admin/statuses/99541947525187367"
250
251 assert Activity.get_create_activity_by_object_ap_id(data["object"])
252 end
253
254 test "it works for incoming announces with an existing activity" do
255 user = insert(:user)
256 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
257
258 data =
259 File.read!("test/fixtures/mastodon-announce.json")
260 |> Poison.decode!()
261 |> Map.put("object", activity.data["object"]["id"])
262
263 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
264
265 assert data["actor"] == "http://mastodon.example.org/users/admin"
266 assert data["type"] == "Announce"
267
268 assert data["id"] ==
269 "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
270
271 assert data["object"] == activity.data["object"]["id"]
272
273 assert Activity.get_create_activity_by_object_ap_id(data["object"]).id == activity.id
274 end
275
276 test "it works for incoming update activities" do
277 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
278
279 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
280 update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
281
282 object =
283 update_data["object"]
284 |> Map.put("actor", data["actor"])
285 |> Map.put("id", data["actor"])
286
287 update_data =
288 update_data
289 |> Map.put("actor", data["actor"])
290 |> Map.put("object", object)
291
292 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
293
294 user = User.get_cached_by_ap_id(data["actor"])
295 assert user.name == "gargle"
296
297 assert user.avatar["url"] == [
298 %{
299 "href" =>
300 "https://cd.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
301 }
302 ]
303
304 assert user.info["banner"]["url"] == [
305 %{
306 "href" =>
307 "https://cd.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
308 }
309 ]
310
311 assert user.bio == "<p>Some bio</p>"
312 end
313
314 test "it works for incoming update activities which lock the account" do
315 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
316
317 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
318 update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
319
320 object =
321 update_data["object"]
322 |> Map.put("actor", data["actor"])
323 |> Map.put("id", data["actor"])
324 |> Map.put("manuallyApprovesFollowers", true)
325
326 update_data =
327 update_data
328 |> Map.put("actor", data["actor"])
329 |> Map.put("object", object)
330
331 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data)
332
333 user = User.get_cached_by_ap_id(data["actor"])
334 assert user.info["locked"] == true
335 end
336
337 test "it works for incoming deletes" do
338 activity = insert(:note_activity)
339
340 data =
341 File.read!("test/fixtures/mastodon-delete.json")
342 |> Poison.decode!()
343
344 object =
345 data["object"]
346 |> Map.put("id", activity.data["object"]["id"])
347
348 data =
349 data
350 |> Map.put("object", object)
351 |> Map.put("actor", activity.data["actor"])
352
353 {:ok, %Activity{local: false}} = Transmogrifier.handle_incoming(data)
354
355 refute Repo.get(Activity, activity.id)
356 end
357
358 test "it works for incoming unannounces with an existing notice" do
359 user = insert(:user)
360 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
361
362 announce_data =
363 File.read!("test/fixtures/mastodon-announce.json")
364 |> Poison.decode!()
365 |> Map.put("object", activity.data["object"]["id"])
366
367 {:ok, %Activity{data: announce_data, local: false}} =
368 Transmogrifier.handle_incoming(announce_data)
369
370 data =
371 File.read!("test/fixtures/mastodon-undo-announce.json")
372 |> Poison.decode!()
373 |> Map.put("object", announce_data)
374 |> Map.put("actor", announce_data["actor"])
375
376 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
377
378 assert data["type"] == "Undo"
379 assert data["object"]["type"] == "Announce"
380 assert data["object"]["object"] == activity.data["object"]["id"]
381
382 assert data["object"]["id"] ==
383 "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
384 end
385
386 test "it works for incomming unfollows with an existing follow" do
387 user = insert(:user)
388
389 follow_data =
390 File.read!("test/fixtures/mastodon-follow-activity.json")
391 |> Poison.decode!()
392 |> Map.put("object", user.ap_id)
393
394 {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(follow_data)
395
396 data =
397 File.read!("test/fixtures/mastodon-unfollow-activity.json")
398 |> Poison.decode!()
399 |> Map.put("object", follow_data)
400
401 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
402
403 assert data["type"] == "Undo"
404 assert data["object"]["type"] == "Follow"
405 assert data["object"]["object"] == user.ap_id
406 assert data["actor"] == "http://mastodon.example.org/users/admin"
407
408 refute User.following?(User.get_by_ap_id(data["actor"]), user)
409 end
410
411 test "it works for incoming blocks" do
412 user = insert(:user)
413
414 data =
415 File.read!("test/fixtures/mastodon-block-activity.json")
416 |> Poison.decode!()
417 |> Map.put("object", user.ap_id)
418
419 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
420
421 assert data["type"] == "Block"
422 assert data["object"] == user.ap_id
423 assert data["actor"] == "http://mastodon.example.org/users/admin"
424
425 blocker = User.get_by_ap_id(data["actor"])
426
427 assert User.blocks?(blocker, user)
428 end
429
430 test "incoming blocks successfully tear down any follow relationship" do
431 blocker = insert(:user)
432 blocked = insert(:user)
433
434 data =
435 File.read!("test/fixtures/mastodon-block-activity.json")
436 |> Poison.decode!()
437 |> Map.put("object", blocked.ap_id)
438 |> Map.put("actor", blocker.ap_id)
439
440 {:ok, blocker} = User.follow(blocker, blocked)
441 {:ok, blocked} = User.follow(blocked, blocker)
442
443 assert User.following?(blocker, blocked)
444 assert User.following?(blocked, blocker)
445
446 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
447
448 assert data["type"] == "Block"
449 assert data["object"] == blocked.ap_id
450 assert data["actor"] == blocker.ap_id
451
452 blocker = User.get_by_ap_id(data["actor"])
453 blocked = User.get_by_ap_id(data["object"])
454
455 assert User.blocks?(blocker, blocked)
456
457 refute User.following?(blocker, blocked)
458 refute User.following?(blocked, blocker)
459 end
460
461 test "it works for incoming unblocks with an existing block" do
462 user = insert(:user)
463
464 block_data =
465 File.read!("test/fixtures/mastodon-block-activity.json")
466 |> Poison.decode!()
467 |> Map.put("object", user.ap_id)
468
469 {:ok, %Activity{data: _, local: false}} = Transmogrifier.handle_incoming(block_data)
470
471 data =
472 File.read!("test/fixtures/mastodon-unblock-activity.json")
473 |> Poison.decode!()
474 |> Map.put("object", block_data)
475
476 {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
477 assert data["type"] == "Undo"
478 assert data["object"]["type"] == "Block"
479 assert data["object"]["object"] == user.ap_id
480 assert data["actor"] == "http://mastodon.example.org/users/admin"
481
482 blocker = User.get_by_ap_id(data["actor"])
483
484 refute User.blocks?(blocker, user)
485 end
486
487 test "it works for incoming accepts which were pre-accepted" do
488 follower = insert(:user)
489 followed = insert(:user)
490
491 {:ok, follower} = User.follow(follower, followed)
492 assert User.following?(follower, followed) == true
493
494 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
495
496 accept_data =
497 File.read!("test/fixtures/mastodon-accept-activity.json")
498 |> Poison.decode!()
499 |> Map.put("actor", followed.ap_id)
500
501 object =
502 accept_data["object"]
503 |> Map.put("actor", follower.ap_id)
504 |> Map.put("id", follow_activity.data["id"])
505
506 accept_data = Map.put(accept_data, "object", object)
507
508 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
509 refute activity.local
510
511 assert activity.data["object"] == follow_activity.data["id"]
512
513 follower = Repo.get(User, follower.id)
514
515 assert User.following?(follower, followed) == true
516 end
517
518 test "it works for incoming accepts which were orphaned" do
519 follower = insert(:user)
520 followed = insert(:user, %{info: %{"locked" => true}})
521
522 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
523
524 accept_data =
525 File.read!("test/fixtures/mastodon-accept-activity.json")
526 |> Poison.decode!()
527 |> Map.put("actor", followed.ap_id)
528
529 accept_data =
530 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
531
532 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
533 assert activity.data["object"] == follow_activity.data["id"]
534
535 follower = Repo.get(User, follower.id)
536
537 assert User.following?(follower, followed) == true
538 end
539
540 test "it works for incoming accepts which are referenced by IRI only" do
541 follower = insert(:user)
542 followed = insert(:user, %{info: %{"locked" => true}})
543
544 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
545
546 accept_data =
547 File.read!("test/fixtures/mastodon-accept-activity.json")
548 |> Poison.decode!()
549 |> Map.put("actor", followed.ap_id)
550 |> Map.put("object", follow_activity.data["id"])
551
552 {:ok, activity} = Transmogrifier.handle_incoming(accept_data)
553 assert activity.data["object"] == follow_activity.data["id"]
554
555 follower = Repo.get(User, follower.id)
556
557 assert User.following?(follower, followed) == true
558 end
559
560 test "it fails for incoming accepts which cannot be correlated" do
561 follower = insert(:user)
562 followed = insert(:user, %{info: %{"locked" => true}})
563
564 accept_data =
565 File.read!("test/fixtures/mastodon-accept-activity.json")
566 |> Poison.decode!()
567 |> Map.put("actor", followed.ap_id)
568
569 accept_data =
570 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
571
572 :error = Transmogrifier.handle_incoming(accept_data)
573
574 follower = Repo.get(User, follower.id)
575
576 refute User.following?(follower, followed) == true
577 end
578
579 test "it fails for incoming rejects which cannot be correlated" do
580 follower = insert(:user)
581 followed = insert(:user, %{info: %{"locked" => true}})
582
583 accept_data =
584 File.read!("test/fixtures/mastodon-reject-activity.json")
585 |> Poison.decode!()
586 |> Map.put("actor", followed.ap_id)
587
588 accept_data =
589 Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id))
590
591 :error = Transmogrifier.handle_incoming(accept_data)
592
593 follower = Repo.get(User, follower.id)
594
595 refute User.following?(follower, followed) == true
596 end
597
598 test "it works for incoming rejects which are orphaned" do
599 follower = insert(:user)
600 followed = insert(:user, %{info: %{"locked" => true}})
601
602 {:ok, follower} = User.follow(follower, followed)
603 {:ok, _follow_activity} = ActivityPub.follow(follower, followed)
604
605 assert User.following?(follower, followed) == true
606
607 reject_data =
608 File.read!("test/fixtures/mastodon-reject-activity.json")
609 |> Poison.decode!()
610 |> Map.put("actor", followed.ap_id)
611
612 reject_data =
613 Map.put(reject_data, "object", Map.put(reject_data["object"], "actor", follower.ap_id))
614
615 {:ok, activity} = Transmogrifier.handle_incoming(reject_data)
616 refute activity.local
617
618 follower = Repo.get(User, follower.id)
619
620 assert User.following?(follower, followed) == false
621 end
622
623 test "it works for incoming rejects which are referenced by IRI only" do
624 follower = insert(:user)
625 followed = insert(:user, %{info: %{"locked" => true}})
626
627 {:ok, follower} = User.follow(follower, followed)
628 {:ok, follow_activity} = ActivityPub.follow(follower, followed)
629
630 assert User.following?(follower, followed) == true
631
632 reject_data =
633 File.read!("test/fixtures/mastodon-reject-activity.json")
634 |> Poison.decode!()
635 |> Map.put("actor", followed.ap_id)
636 |> Map.put("object", follow_activity.data["id"])
637
638 {:ok, %Activity{data: _}} = Transmogrifier.handle_incoming(reject_data)
639
640 follower = Repo.get(User, follower.id)
641
642 assert User.following?(follower, followed) == false
643 end
644
645 test "it rejects activities without a valid ID" do
646 user = insert(:user)
647
648 data =
649 File.read!("test/fixtures/mastodon-follow-activity.json")
650 |> Poison.decode!()
651 |> Map.put("object", user.ap_id)
652 |> Map.put("id", "")
653
654 :error = Transmogrifier.handle_incoming(data)
655 end
656 end
657
658 describe "prepare outgoing" do
659 test "it turns mentions into tags" do
660 user = insert(:user)
661 other_user = insert(:user)
662
663 {:ok, activity} =
664 CommonAPI.post(user, %{"status" => "hey, @#{other_user.nickname}, how are ya? #2hu"})
665
666 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
667 object = modified["object"]
668
669 expected_mention = %{
670 "href" => other_user.ap_id,
671 "name" => "@#{other_user.nickname}",
672 "type" => "Mention"
673 }
674
675 expected_tag = %{
676 "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
677 "type" => "Hashtag",
678 "name" => "#2hu"
679 }
680
681 assert Enum.member?(object["tag"], expected_tag)
682 assert Enum.member?(object["tag"], expected_mention)
683 end
684
685 test "it adds the sensitive property" do
686 user = insert(:user)
687
688 {:ok, activity} = CommonAPI.post(user, %{"status" => "#nsfw hey"})
689 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
690
691 assert modified["object"]["sensitive"]
692 end
693
694 test "it adds the json-ld context and the conversation property" do
695 user = insert(:user)
696
697 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
698 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
699
700 assert modified["@context"] == "https://www.w3.org/ns/activitystreams"
701 assert modified["object"]["conversation"] == modified["context"]
702 end
703
704 test "it sets the 'attributedTo' property to the actor of the object if it doesn't have one" do
705 user = insert(:user)
706
707 {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
708 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
709
710 assert modified["object"]["actor"] == modified["object"]["attributedTo"]
711 end
712
713 test "it translates ostatus IDs to external URLs" do
714 incoming = File.read!("test/fixtures/incoming_note_activity.xml")
715 {:ok, [referent_activity]} = OStatus.handle_incoming(incoming)
716
717 user = insert(:user)
718
719 {:ok, activity, _} = CommonAPI.favorite(referent_activity.id, user)
720 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
721
722 assert modified["object"] == "http://gs.example.org:4040/index.php/notice/29"
723 end
724
725 test "it translates ostatus reply_to IDs to external URLs" do
726 incoming = File.read!("test/fixtures/incoming_note_activity.xml")
727 {:ok, [referred_activity]} = OStatus.handle_incoming(incoming)
728
729 user = insert(:user)
730
731 {:ok, activity} =
732 CommonAPI.post(user, %{"status" => "HI!", "in_reply_to_status_id" => referred_activity.id})
733
734 {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
735
736 assert modified["object"]["inReplyTo"] == "http://gs.example.org:4040/index.php/notice/29"
737 end
738 end
739
740 describe "user upgrade" do
741 test "it upgrades a user to activitypub" do
742 user =
743 insert(:user, %{
744 nickname: "rye@niu.moe",
745 local: false,
746 ap_id: "https://niu.moe/users/rye",
747 follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"})
748 })
749
750 user_two = insert(:user, %{following: [user.follower_address]})
751
752 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
753 {:ok, unrelated_activity} = CommonAPI.post(user_two, %{"status" => "test"})
754 assert "http://localhost:4001/users/rye@niu.moe/followers" in activity.recipients
755
756 user = Repo.get(User, user.id)
757 assert user.info["note_count"] == 1
758
759 {:ok, user} = Transmogrifier.upgrade_user_from_ap_id("https://niu.moe/users/rye")
760 assert user.info["ap_enabled"]
761 assert user.info["note_count"] == 1
762 assert user.follower_address == "https://niu.moe/users/rye/followers"
763
764 # Wait for the background task
765 :timer.sleep(1000)
766
767 user = Repo.get(User, user.id)
768 assert user.info["note_count"] == 1
769
770 activity = Repo.get(Activity, activity.id)
771 assert user.follower_address in activity.recipients
772
773 assert %{
774 "url" => [
775 %{
776 "href" =>
777 "https://cdn.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"
778 }
779 ]
780 } = user.avatar
781
782 assert %{
783 "url" => [
784 %{
785 "href" =>
786 "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
787 }
788 ]
789 } = user.info["banner"]
790
791 refute "..." in activity.recipients
792
793 unrelated_activity = Repo.get(Activity, unrelated_activity.id)
794 refute user.follower_address in unrelated_activity.recipients
795
796 user_two = Repo.get(User, user_two.id)
797 assert user.follower_address in user_two.following
798 refute "..." in user_two.following
799 end
800 end
801
802 describe "maybe_retire_websub" do
803 test "it deletes all websub client subscripitions with the user as topic" do
804 subscription = %WebsubClientSubscription{topic: "https://niu.moe/users/rye.atom"}
805 {:ok, ws} = Repo.insert(subscription)
806
807 subscription = %WebsubClientSubscription{topic: "https://niu.moe/users/pasty.atom"}
808 {:ok, ws2} = Repo.insert(subscription)
809
810 Transmogrifier.maybe_retire_websub("https://niu.moe/users/rye")
811
812 refute Repo.get(WebsubClientSubscription, ws.id)
813 assert Repo.get(WebsubClientSubscription, ws2.id)
814 end
815 end
816
817 describe "actor rewriting" do
818 test "it fixes the actor URL property to be a proper URI" do
819 data = %{
820 "url" => %{"href" => "http://example.com"}
821 }
822
823 rewritten = Transmogrifier.maybe_fix_user_object(data)
824 assert rewritten["url"] == "http://example.com"
825 end
826 end
827
828 describe "actor origin containment" do
829 test "it rejects objects with a bogus origin" do
830 {:error, _} = ActivityPub.fetch_object_from_id("https://info.pleroma.site/activity.json")
831 end
832
833 test "it rejects activities which reference objects with bogus origins" do
834 user = insert(:user, %{local: false})
835
836 data = %{
837 "@context" => "https://www.w3.org/ns/activitystreams",
838 "id" => user.ap_id <> "/activities/1234",
839 "actor" => user.ap_id,
840 "to" => ["https://www.w3.org/ns/activitystreams#Public"],
841 "object" => "https://info.pleroma.site/activity.json",
842 "type" => "Announce"
843 }
844
845 :error = Transmogrifier.handle_incoming(data)
846 end
847 end
848 end