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