[#1427] Reworked admin scopes support.
[akkoma] / test / web / activity_pub / activity_pub_controller_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.ActivityPubControllerTest do
6 use Pleroma.Web.ConnCase
7 use Oban.Testing, repo: Pleroma.Repo
8
9 import Pleroma.Factory
10 alias Pleroma.Activity
11 alias Pleroma.Delivery
12 alias Pleroma.Instances
13 alias Pleroma.Object
14 alias Pleroma.Tests.ObanHelpers
15 alias Pleroma.User
16 alias Pleroma.Web.ActivityPub.ObjectView
17 alias Pleroma.Web.ActivityPub.Relay
18 alias Pleroma.Web.ActivityPub.UserView
19 alias Pleroma.Web.ActivityPub.Utils
20 alias Pleroma.Web.CommonAPI
21 alias Pleroma.Workers.ReceiverWorker
22
23 setup_all do
24 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
25 :ok
26 end
27
28 clear_config_all([:instance, :federating],
29 do: Pleroma.Config.put([:instance, :federating], true)
30 )
31
32 describe "/relay" do
33 clear_config([:instance, :allow_relay])
34
35 test "with the relay active, it returns the relay user", %{conn: conn} do
36 res =
37 conn
38 |> get(activity_pub_path(conn, :relay))
39 |> json_response(200)
40
41 assert res["id"] =~ "/relay"
42 end
43
44 test "with the relay disabled, it returns 404", %{conn: conn} do
45 Pleroma.Config.put([:instance, :allow_relay], false)
46
47 conn
48 |> get(activity_pub_path(conn, :relay))
49 |> json_response(404)
50 |> assert
51 end
52 end
53
54 describe "/internal/fetch" do
55 test "it returns the internal fetch user", %{conn: conn} do
56 res =
57 conn
58 |> get(activity_pub_path(conn, :internal_fetch))
59 |> json_response(200)
60
61 assert res["id"] =~ "/fetch"
62 end
63 end
64
65 describe "/users/:nickname" do
66 test "it returns a json representation of the user with accept application/json", %{
67 conn: conn
68 } do
69 user = insert(:user)
70
71 conn =
72 conn
73 |> put_req_header("accept", "application/json")
74 |> get("/users/#{user.nickname}")
75
76 user = User.get_cached_by_id(user.id)
77
78 assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
79 end
80
81 test "it returns a json representation of the user with accept application/activity+json", %{
82 conn: conn
83 } do
84 user = insert(:user)
85
86 conn =
87 conn
88 |> put_req_header("accept", "application/activity+json")
89 |> get("/users/#{user.nickname}")
90
91 user = User.get_cached_by_id(user.id)
92
93 assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
94 end
95
96 test "it returns a json representation of the user with accept application/ld+json", %{
97 conn: conn
98 } do
99 user = insert(:user)
100
101 conn =
102 conn
103 |> put_req_header(
104 "accept",
105 "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
106 )
107 |> get("/users/#{user.nickname}")
108
109 user = User.get_cached_by_id(user.id)
110
111 assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
112 end
113 end
114
115 describe "/object/:uuid" do
116 test "it returns a json representation of the object with accept application/json", %{
117 conn: conn
118 } do
119 note = insert(:note)
120 uuid = String.split(note.data["id"], "/") |> List.last()
121
122 conn =
123 conn
124 |> put_req_header("accept", "application/json")
125 |> get("/objects/#{uuid}")
126
127 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note})
128 end
129
130 test "it returns a json representation of the object with accept application/activity+json",
131 %{conn: conn} do
132 note = insert(:note)
133 uuid = String.split(note.data["id"], "/") |> List.last()
134
135 conn =
136 conn
137 |> put_req_header("accept", "application/activity+json")
138 |> get("/objects/#{uuid}")
139
140 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note})
141 end
142
143 test "it returns a json representation of the object with accept application/ld+json", %{
144 conn: conn
145 } do
146 note = insert(:note)
147 uuid = String.split(note.data["id"], "/") |> List.last()
148
149 conn =
150 conn
151 |> put_req_header(
152 "accept",
153 "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
154 )
155 |> get("/objects/#{uuid}")
156
157 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note})
158 end
159
160 test "it returns 404 for non-public messages", %{conn: conn} do
161 note = insert(:direct_note)
162 uuid = String.split(note.data["id"], "/") |> List.last()
163
164 conn =
165 conn
166 |> put_req_header("accept", "application/activity+json")
167 |> get("/objects/#{uuid}")
168
169 assert json_response(conn, 404)
170 end
171
172 test "it returns 404 for tombstone objects", %{conn: conn} do
173 tombstone = insert(:tombstone)
174 uuid = String.split(tombstone.data["id"], "/") |> List.last()
175
176 conn =
177 conn
178 |> put_req_header("accept", "application/activity+json")
179 |> get("/objects/#{uuid}")
180
181 assert json_response(conn, 404)
182 end
183
184 test "it caches a response", %{conn: conn} do
185 note = insert(:note)
186 uuid = String.split(note.data["id"], "/") |> List.last()
187
188 conn1 =
189 conn
190 |> put_req_header("accept", "application/activity+json")
191 |> get("/objects/#{uuid}")
192
193 assert json_response(conn1, :ok)
194 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
195
196 conn2 =
197 conn
198 |> put_req_header("accept", "application/activity+json")
199 |> get("/objects/#{uuid}")
200
201 assert json_response(conn1, :ok) == json_response(conn2, :ok)
202 assert Enum.any?(conn2.resp_headers, &(&1 == {"x-cache", "HIT from Pleroma"}))
203 end
204
205 test "cached purged after object deletion", %{conn: conn} do
206 note = insert(:note)
207 uuid = String.split(note.data["id"], "/") |> List.last()
208
209 conn1 =
210 conn
211 |> put_req_header("accept", "application/activity+json")
212 |> get("/objects/#{uuid}")
213
214 assert json_response(conn1, :ok)
215 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
216
217 Object.delete(note)
218
219 conn2 =
220 conn
221 |> put_req_header("accept", "application/activity+json")
222 |> get("/objects/#{uuid}")
223
224 assert "Not found" == json_response(conn2, :not_found)
225 end
226 end
227
228 describe "/activities/:uuid" do
229 test "it returns a json representation of the activity", %{conn: conn} do
230 activity = insert(:note_activity)
231 uuid = String.split(activity.data["id"], "/") |> List.last()
232
233 conn =
234 conn
235 |> put_req_header("accept", "application/activity+json")
236 |> get("/activities/#{uuid}")
237
238 assert json_response(conn, 200) == ObjectView.render("object.json", %{object: activity})
239 end
240
241 test "it returns 404 for non-public activities", %{conn: conn} do
242 activity = insert(:direct_note_activity)
243 uuid = String.split(activity.data["id"], "/") |> List.last()
244
245 conn =
246 conn
247 |> put_req_header("accept", "application/activity+json")
248 |> get("/activities/#{uuid}")
249
250 assert json_response(conn, 404)
251 end
252
253 test "it caches a response", %{conn: conn} do
254 activity = insert(:note_activity)
255 uuid = String.split(activity.data["id"], "/") |> List.last()
256
257 conn1 =
258 conn
259 |> put_req_header("accept", "application/activity+json")
260 |> get("/activities/#{uuid}")
261
262 assert json_response(conn1, :ok)
263 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
264
265 conn2 =
266 conn
267 |> put_req_header("accept", "application/activity+json")
268 |> get("/activities/#{uuid}")
269
270 assert json_response(conn1, :ok) == json_response(conn2, :ok)
271 assert Enum.any?(conn2.resp_headers, &(&1 == {"x-cache", "HIT from Pleroma"}))
272 end
273
274 test "cached purged after activity deletion", %{conn: conn} do
275 user = insert(:user)
276 {:ok, activity} = CommonAPI.post(user, %{"status" => "cofe"})
277
278 uuid = String.split(activity.data["id"], "/") |> List.last()
279
280 conn1 =
281 conn
282 |> put_req_header("accept", "application/activity+json")
283 |> get("/activities/#{uuid}")
284
285 assert json_response(conn1, :ok)
286 assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
287
288 Activity.delete_by_ap_id(activity.object.data["id"])
289
290 conn2 =
291 conn
292 |> put_req_header("accept", "application/activity+json")
293 |> get("/activities/#{uuid}")
294
295 assert "Not found" == json_response(conn2, :not_found)
296 end
297 end
298
299 describe "/inbox" do
300 test "it inserts an incoming activity into the database", %{conn: conn} do
301 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
302
303 conn =
304 conn
305 |> assign(:valid_signature, true)
306 |> put_req_header("content-type", "application/activity+json")
307 |> post("/inbox", data)
308
309 assert "ok" == json_response(conn, 200)
310
311 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
312 assert Activity.get_by_ap_id(data["id"])
313 end
314
315 test "it clears `unreachable` federation status of the sender", %{conn: conn} do
316 data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()
317
318 sender_url = data["actor"]
319 Instances.set_consistently_unreachable(sender_url)
320 refute Instances.reachable?(sender_url)
321
322 conn =
323 conn
324 |> assign(:valid_signature, true)
325 |> put_req_header("content-type", "application/activity+json")
326 |> post("/inbox", data)
327
328 assert "ok" == json_response(conn, 200)
329 assert Instances.reachable?(sender_url)
330 end
331 end
332
333 describe "/users/:nickname/inbox" do
334 setup do
335 data =
336 File.read!("test/fixtures/mastodon-post-activity.json")
337 |> Poison.decode!()
338
339 [data: data]
340 end
341
342 test "it inserts an incoming activity into the database", %{conn: conn, data: data} do
343 user = insert(:user)
344 data = Map.put(data, "bcc", [user.ap_id])
345
346 conn =
347 conn
348 |> assign(:valid_signature, true)
349 |> put_req_header("content-type", "application/activity+json")
350 |> post("/users/#{user.nickname}/inbox", data)
351
352 assert "ok" == json_response(conn, 200)
353 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
354 assert Activity.get_by_ap_id(data["id"])
355 end
356
357 test "it accepts messages with to as string instead of array", %{conn: conn, data: data} do
358 user = insert(:user)
359
360 data =
361 Map.put(data, "to", user.ap_id)
362 |> Map.delete("cc")
363
364 conn =
365 conn
366 |> assign(:valid_signature, true)
367 |> put_req_header("content-type", "application/activity+json")
368 |> post("/users/#{user.nickname}/inbox", data)
369
370 assert "ok" == json_response(conn, 200)
371 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
372 assert Activity.get_by_ap_id(data["id"])
373 end
374
375 test "it accepts messages with cc as string instead of array", %{conn: conn, data: data} do
376 user = insert(:user)
377
378 data =
379 Map.put(data, "cc", user.ap_id)
380 |> Map.delete("to")
381
382 conn =
383 conn
384 |> assign(:valid_signature, true)
385 |> put_req_header("content-type", "application/activity+json")
386 |> post("/users/#{user.nickname}/inbox", data)
387
388 assert "ok" == json_response(conn, 200)
389 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
390 %Activity{} = activity = Activity.get_by_ap_id(data["id"])
391 assert user.ap_id in activity.recipients
392 end
393
394 test "it accepts messages with bcc as string instead of array", %{conn: conn, data: data} do
395 user = insert(:user)
396
397 data =
398 Map.put(data, "bcc", user.ap_id)
399 |> Map.delete("to")
400 |> Map.delete("cc")
401
402 conn =
403 conn
404 |> assign(:valid_signature, true)
405 |> put_req_header("content-type", "application/activity+json")
406 |> post("/users/#{user.nickname}/inbox", data)
407
408 assert "ok" == json_response(conn, 200)
409 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
410 assert Activity.get_by_ap_id(data["id"])
411 end
412
413 test "it accepts announces with to as string instead of array", %{conn: conn} do
414 user = insert(:user)
415
416 data = %{
417 "@context" => "https://www.w3.org/ns/activitystreams",
418 "actor" => "http://mastodon.example.org/users/admin",
419 "id" => "http://mastodon.example.org/users/admin/statuses/19512778738411822/activity",
420 "object" => "https://mastodon.social/users/emelie/statuses/101849165031453009",
421 "to" => "https://www.w3.org/ns/activitystreams#Public",
422 "cc" => [user.ap_id],
423 "type" => "Announce"
424 }
425
426 conn =
427 conn
428 |> assign(:valid_signature, true)
429 |> put_req_header("content-type", "application/activity+json")
430 |> post("/users/#{user.nickname}/inbox", data)
431
432 assert "ok" == json_response(conn, 200)
433 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
434 %Activity{} = activity = Activity.get_by_ap_id(data["id"])
435 assert "https://www.w3.org/ns/activitystreams#Public" in activity.recipients
436 end
437
438 test "it accepts messages from actors that are followed by the user", %{
439 conn: conn,
440 data: data
441 } do
442 recipient = insert(:user)
443 actor = insert(:user, %{ap_id: "http://mastodon.example.org/users/actor"})
444
445 {:ok, recipient} = User.follow(recipient, actor)
446
447 object =
448 data["object"]
449 |> Map.put("attributedTo", actor.ap_id)
450
451 data =
452 data
453 |> Map.put("actor", actor.ap_id)
454 |> Map.put("object", object)
455
456 conn =
457 conn
458 |> assign(:valid_signature, true)
459 |> put_req_header("content-type", "application/activity+json")
460 |> post("/users/#{recipient.nickname}/inbox", data)
461
462 assert "ok" == json_response(conn, 200)
463 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
464 assert Activity.get_by_ap_id(data["id"])
465 end
466
467 test "it rejects reads from other users", %{conn: conn} do
468 user = insert(:user)
469 otheruser = insert(:user)
470
471 conn =
472 conn
473 |> assign(:user, otheruser)
474 |> put_req_header("accept", "application/activity+json")
475 |> get("/users/#{user.nickname}/inbox")
476
477 assert json_response(conn, 403)
478 end
479
480 test "it doesn't crash without an authenticated user", %{conn: conn} do
481 user = insert(:user)
482
483 conn =
484 conn
485 |> put_req_header("accept", "application/activity+json")
486 |> get("/users/#{user.nickname}/inbox")
487
488 assert json_response(conn, 403)
489 end
490
491 test "it returns a note activity in a collection", %{conn: conn} do
492 note_activity = insert(:direct_note_activity)
493 note_object = Object.normalize(note_activity)
494 user = User.get_cached_by_ap_id(hd(note_activity.data["to"]))
495
496 conn =
497 conn
498 |> assign(:user, user)
499 |> put_req_header("accept", "application/activity+json")
500 |> get("/users/#{user.nickname}/inbox?page=true")
501
502 assert response(conn, 200) =~ note_object.data["content"]
503 end
504
505 test "it clears `unreachable` federation status of the sender", %{conn: conn, data: data} do
506 user = insert(:user)
507 data = Map.put(data, "bcc", [user.ap_id])
508
509 sender_host = URI.parse(data["actor"]).host
510 Instances.set_consistently_unreachable(sender_host)
511 refute Instances.reachable?(sender_host)
512
513 conn =
514 conn
515 |> assign(:valid_signature, true)
516 |> put_req_header("content-type", "application/activity+json")
517 |> post("/users/#{user.nickname}/inbox", data)
518
519 assert "ok" == json_response(conn, 200)
520 assert Instances.reachable?(sender_host)
521 end
522
523 test "it removes all follower collections but actor's", %{conn: conn} do
524 [actor, recipient] = insert_pair(:user)
525
526 data =
527 File.read!("test/fixtures/activitypub-client-post-activity.json")
528 |> Poison.decode!()
529
530 object = Map.put(data["object"], "attributedTo", actor.ap_id)
531
532 data =
533 data
534 |> Map.put("id", Utils.generate_object_id())
535 |> Map.put("actor", actor.ap_id)
536 |> Map.put("object", object)
537 |> Map.put("cc", [
538 recipient.follower_address,
539 actor.follower_address
540 ])
541 |> Map.put("to", [
542 recipient.ap_id,
543 recipient.follower_address,
544 "https://www.w3.org/ns/activitystreams#Public"
545 ])
546
547 conn
548 |> assign(:valid_signature, true)
549 |> put_req_header("content-type", "application/activity+json")
550 |> post("/users/#{recipient.nickname}/inbox", data)
551 |> json_response(200)
552
553 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
554
555 activity = Activity.get_by_ap_id(data["id"])
556
557 assert activity.id
558 assert actor.follower_address in activity.recipients
559 assert actor.follower_address in activity.data["cc"]
560
561 refute recipient.follower_address in activity.recipients
562 refute recipient.follower_address in activity.data["cc"]
563 refute recipient.follower_address in activity.data["to"]
564 end
565 end
566
567 describe "/users/:nickname/outbox" do
568 test "it will not bomb when there is no activity", %{conn: conn} do
569 user = insert(:user)
570
571 conn =
572 conn
573 |> put_req_header("accept", "application/activity+json")
574 |> get("/users/#{user.nickname}/outbox")
575
576 result = json_response(conn, 200)
577 assert user.ap_id <> "/outbox" == result["id"]
578 end
579
580 test "it returns a note activity in a collection", %{conn: conn} do
581 note_activity = insert(:note_activity)
582 note_object = Object.normalize(note_activity)
583 user = User.get_cached_by_ap_id(note_activity.data["actor"])
584
585 conn =
586 conn
587 |> put_req_header("accept", "application/activity+json")
588 |> get("/users/#{user.nickname}/outbox?page=true")
589
590 assert response(conn, 200) =~ note_object.data["content"]
591 end
592
593 test "it returns an announce activity in a collection", %{conn: conn} do
594 announce_activity = insert(:announce_activity)
595 user = User.get_cached_by_ap_id(announce_activity.data["actor"])
596
597 conn =
598 conn
599 |> put_req_header("accept", "application/activity+json")
600 |> get("/users/#{user.nickname}/outbox?page=true")
601
602 assert response(conn, 200) =~ announce_activity.data["object"]
603 end
604
605 test "it rejects posts from other users", %{conn: conn} do
606 data = File.read!("test/fixtures/activitypub-client-post-activity.json") |> Poison.decode!()
607 user = insert(:user)
608 otheruser = insert(:user)
609
610 conn =
611 conn
612 |> assign(:user, otheruser)
613 |> put_req_header("content-type", "application/activity+json")
614 |> post("/users/#{user.nickname}/outbox", data)
615
616 assert json_response(conn, 403)
617 end
618
619 test "it inserts an incoming create activity into the database", %{conn: conn} do
620 data = File.read!("test/fixtures/activitypub-client-post-activity.json") |> Poison.decode!()
621 user = insert(:user)
622
623 conn =
624 conn
625 |> assign(:user, user)
626 |> put_req_header("content-type", "application/activity+json")
627 |> post("/users/#{user.nickname}/outbox", data)
628
629 result = json_response(conn, 201)
630
631 assert Activity.get_by_ap_id(result["id"])
632 end
633
634 test "it rejects an incoming activity with bogus type", %{conn: conn} do
635 data = File.read!("test/fixtures/activitypub-client-post-activity.json") |> Poison.decode!()
636 user = insert(:user)
637
638 data =
639 data
640 |> Map.put("type", "BadType")
641
642 conn =
643 conn
644 |> assign(:user, user)
645 |> put_req_header("content-type", "application/activity+json")
646 |> post("/users/#{user.nickname}/outbox", data)
647
648 assert json_response(conn, 400)
649 end
650
651 test "it erects a tombstone when receiving a delete activity", %{conn: conn} do
652 note_activity = insert(:note_activity)
653 note_object = Object.normalize(note_activity)
654 user = User.get_cached_by_ap_id(note_activity.data["actor"])
655
656 data = %{
657 type: "Delete",
658 object: %{
659 id: note_object.data["id"]
660 }
661 }
662
663 conn =
664 conn
665 |> assign(:user, user)
666 |> put_req_header("content-type", "application/activity+json")
667 |> post("/users/#{user.nickname}/outbox", data)
668
669 result = json_response(conn, 201)
670 assert Activity.get_by_ap_id(result["id"])
671
672 assert object = Object.get_by_ap_id(note_object.data["id"])
673 assert object.data["type"] == "Tombstone"
674 end
675
676 test "it rejects delete activity of object from other actor", %{conn: conn} do
677 note_activity = insert(:note_activity)
678 note_object = Object.normalize(note_activity)
679 user = insert(:user)
680
681 data = %{
682 type: "Delete",
683 object: %{
684 id: note_object.data["id"]
685 }
686 }
687
688 conn =
689 conn
690 |> assign(:user, user)
691 |> put_req_header("content-type", "application/activity+json")
692 |> post("/users/#{user.nickname}/outbox", data)
693
694 assert json_response(conn, 400)
695 end
696
697 test "it increases like count when receiving a like action", %{conn: conn} do
698 note_activity = insert(:note_activity)
699 note_object = Object.normalize(note_activity)
700 user = User.get_cached_by_ap_id(note_activity.data["actor"])
701
702 data = %{
703 type: "Like",
704 object: %{
705 id: note_object.data["id"]
706 }
707 }
708
709 conn =
710 conn
711 |> assign(:user, user)
712 |> put_req_header("content-type", "application/activity+json")
713 |> post("/users/#{user.nickname}/outbox", data)
714
715 result = json_response(conn, 201)
716 assert Activity.get_by_ap_id(result["id"])
717
718 assert object = Object.get_by_ap_id(note_object.data["id"])
719 assert object.data["like_count"] == 1
720 end
721 end
722
723 describe "/relay/followers" do
724 test "it returns relay followers", %{conn: conn} do
725 relay_actor = Relay.get_actor()
726 user = insert(:user)
727 User.follow(user, relay_actor)
728
729 result =
730 conn
731 |> assign(:relay, true)
732 |> get("/relay/followers")
733 |> json_response(200)
734
735 assert result["first"]["orderedItems"] == [user.ap_id]
736 end
737 end
738
739 describe "/relay/following" do
740 test "it returns relay following", %{conn: conn} do
741 result =
742 conn
743 |> assign(:relay, true)
744 |> get("/relay/following")
745 |> json_response(200)
746
747 assert result["first"]["orderedItems"] == []
748 end
749 end
750
751 describe "/users/:nickname/followers" do
752 test "it returns the followers in a collection", %{conn: conn} do
753 user = insert(:user)
754 user_two = insert(:user)
755 User.follow(user, user_two)
756
757 result =
758 conn
759 |> get("/users/#{user_two.nickname}/followers")
760 |> json_response(200)
761
762 assert result["first"]["orderedItems"] == [user.ap_id]
763 end
764
765 test "it returns returns a uri if the user has 'hide_followers' set", %{conn: conn} do
766 user = insert(:user)
767 user_two = insert(:user, hide_followers: true)
768 User.follow(user, user_two)
769
770 result =
771 conn
772 |> get("/users/#{user_two.nickname}/followers")
773 |> json_response(200)
774
775 assert is_binary(result["first"])
776 end
777
778 test "it returns a 403 error on pages, if the user has 'hide_followers' set and the request is not authenticated",
779 %{conn: conn} do
780 user = insert(:user, hide_followers: true)
781
782 result =
783 conn
784 |> get("/users/#{user.nickname}/followers?page=1")
785
786 assert result.status == 403
787 assert result.resp_body == ""
788 end
789
790 test "it renders the page, if the user has 'hide_followers' set and the request is authenticated with the same user",
791 %{conn: conn} do
792 user = insert(:user, hide_followers: true)
793 other_user = insert(:user)
794 {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
795
796 result =
797 conn
798 |> assign(:user, user)
799 |> get("/users/#{user.nickname}/followers?page=1")
800 |> json_response(200)
801
802 assert result["totalItems"] == 1
803 assert result["orderedItems"] == [other_user.ap_id]
804 end
805
806 test "it works for more than 10 users", %{conn: conn} do
807 user = insert(:user)
808
809 Enum.each(1..15, fn _ ->
810 other_user = insert(:user)
811 User.follow(other_user, user)
812 end)
813
814 result =
815 conn
816 |> get("/users/#{user.nickname}/followers")
817 |> json_response(200)
818
819 assert length(result["first"]["orderedItems"]) == 10
820 assert result["first"]["totalItems"] == 15
821 assert result["totalItems"] == 15
822
823 result =
824 conn
825 |> get("/users/#{user.nickname}/followers?page=2")
826 |> json_response(200)
827
828 assert length(result["orderedItems"]) == 5
829 assert result["totalItems"] == 15
830 end
831 end
832
833 describe "/users/:nickname/following" do
834 test "it returns the following in a collection", %{conn: conn} do
835 user = insert(:user)
836 user_two = insert(:user)
837 User.follow(user, user_two)
838
839 result =
840 conn
841 |> get("/users/#{user.nickname}/following")
842 |> json_response(200)
843
844 assert result["first"]["orderedItems"] == [user_two.ap_id]
845 end
846
847 test "it returns a uri if the user has 'hide_follows' set", %{conn: conn} do
848 user = insert(:user, hide_follows: true)
849 user_two = insert(:user)
850 User.follow(user, user_two)
851
852 result =
853 conn
854 |> get("/users/#{user.nickname}/following")
855 |> json_response(200)
856
857 assert is_binary(result["first"])
858 end
859
860 test "it returns a 403 error on pages, if the user has 'hide_follows' set and the request is not authenticated",
861 %{conn: conn} do
862 user = insert(:user, hide_follows: true)
863
864 result =
865 conn
866 |> get("/users/#{user.nickname}/following?page=1")
867
868 assert result.status == 403
869 assert result.resp_body == ""
870 end
871
872 test "it renders the page, if the user has 'hide_follows' set and the request is authenticated with the same user",
873 %{conn: conn} do
874 user = insert(:user, hide_follows: true)
875 other_user = insert(:user)
876 {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user)
877
878 result =
879 conn
880 |> assign(:user, user)
881 |> get("/users/#{user.nickname}/following?page=1")
882 |> json_response(200)
883
884 assert result["totalItems"] == 1
885 assert result["orderedItems"] == [other_user.ap_id]
886 end
887
888 test "it works for more than 10 users", %{conn: conn} do
889 user = insert(:user)
890
891 Enum.each(1..15, fn _ ->
892 user = User.get_cached_by_id(user.id)
893 other_user = insert(:user)
894 User.follow(user, other_user)
895 end)
896
897 result =
898 conn
899 |> get("/users/#{user.nickname}/following")
900 |> json_response(200)
901
902 assert length(result["first"]["orderedItems"]) == 10
903 assert result["first"]["totalItems"] == 15
904 assert result["totalItems"] == 15
905
906 result =
907 conn
908 |> get("/users/#{user.nickname}/following?page=2")
909 |> json_response(200)
910
911 assert length(result["orderedItems"]) == 5
912 assert result["totalItems"] == 15
913 end
914 end
915
916 describe "delivery tracking" do
917 test "it tracks a signed object fetch", %{conn: conn} do
918 user = insert(:user, local: false)
919 activity = insert(:note_activity)
920 object = Object.normalize(activity)
921
922 object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
923
924 conn
925 |> put_req_header("accept", "application/activity+json")
926 |> assign(:user, user)
927 |> get(object_path)
928 |> json_response(200)
929
930 assert Delivery.get(object.id, user.id)
931 end
932
933 test "it tracks a signed activity fetch", %{conn: conn} do
934 user = insert(:user, local: false)
935 activity = insert(:note_activity)
936 object = Object.normalize(activity)
937
938 activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url())
939
940 conn
941 |> put_req_header("accept", "application/activity+json")
942 |> assign(:user, user)
943 |> get(activity_path)
944 |> json_response(200)
945
946 assert Delivery.get(object.id, user.id)
947 end
948
949 test "it tracks a signed object fetch when the json is cached", %{conn: conn} do
950 user = insert(:user, local: false)
951 other_user = insert(:user, local: false)
952 activity = insert(:note_activity)
953 object = Object.normalize(activity)
954
955 object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
956
957 conn
958 |> put_req_header("accept", "application/activity+json")
959 |> assign(:user, user)
960 |> get(object_path)
961 |> json_response(200)
962
963 build_conn()
964 |> put_req_header("accept", "application/activity+json")
965 |> assign(:user, other_user)
966 |> get(object_path)
967 |> json_response(200)
968
969 assert Delivery.get(object.id, user.id)
970 assert Delivery.get(object.id, other_user.id)
971 end
972
973 test "it tracks a signed activity fetch when the json is cached", %{conn: conn} do
974 user = insert(:user, local: false)
975 other_user = insert(:user, local: false)
976 activity = insert(:note_activity)
977 object = Object.normalize(activity)
978
979 activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url())
980
981 conn
982 |> put_req_header("accept", "application/activity+json")
983 |> assign(:user, user)
984 |> get(activity_path)
985 |> json_response(200)
986
987 build_conn()
988 |> put_req_header("accept", "application/activity+json")
989 |> assign(:user, other_user)
990 |> get(activity_path)
991 |> json_response(200)
992
993 assert Delivery.get(object.id, user.id)
994 assert Delivery.get(object.id, other_user.id)
995 end
996 end
997
998 describe "Additionnal ActivityPub C2S endpoints" do
999 test "/api/ap/whoami", %{conn: conn} do
1000 user = insert(:user)
1001
1002 conn =
1003 conn
1004 |> assign(:user, user)
1005 |> get("/api/ap/whoami")
1006
1007 user = User.get_cached_by_id(user.id)
1008
1009 assert UserView.render("user.json", %{user: user}) == json_response(conn, 200)
1010 end
1011
1012 clear_config([:media_proxy])
1013 clear_config([Pleroma.Upload])
1014
1015 test "uploadMedia", %{conn: conn} do
1016 user = insert(:user)
1017
1018 desc = "Description of the image"
1019
1020 image = %Plug.Upload{
1021 content_type: "image/jpg",
1022 path: Path.absname("test/fixtures/image.jpg"),
1023 filename: "an_image.jpg"
1024 }
1025
1026 conn =
1027 conn
1028 |> assign(:user, user)
1029 |> post("/api/ap/upload_media", %{"file" => image, "description" => desc})
1030
1031 assert object = json_response(conn, :created)
1032 assert object["name"] == desc
1033 assert object["type"] == "Document"
1034 assert object["actor"] == user.ap_id
1035 end
1036 end
1037 end