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