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