6ab71e2ea218541e104b11ff464069e5a837e111
[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
345 test "accept follow activity", %{conn: conn} do
346 Pleroma.Config.put([:instance, :federating], true)
347 relay = Relay.get_actor()
348
349 assert {:ok, %Activity{} = activity} = Relay.follow("https://relay.mastodon.host/actor")
350
351 followed_relay = Pleroma.User.get_by_ap_id("https://relay.mastodon.host/actor")
352 relay = refresh_record(relay)
353
354 accept =
355 File.read!("test/fixtures/relay/accept-follow.json")
356 |> String.replace("{{ap_id}}", relay.ap_id)
357 |> String.replace("{{activity_id}}", activity.data["id"])
358
359 assert "ok" ==
360 conn
361 |> assign(:valid_signature, true)
362 |> put_req_header("content-type", "application/activity+json")
363 |> post("/inbox", accept)
364 |> json_response(200)
365
366 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
367
368 assert Pleroma.FollowingRelationship.following?(
369 relay,
370 followed_relay
371 )
372
373 Mix.shell(Mix.Shell.Process)
374
375 on_exit(fn ->
376 Mix.shell(Mix.Shell.IO)
377 end)
378
379 :ok = Mix.Tasks.Pleroma.Relay.run(["list"])
380 assert_receive {:mix_shell, :info, ["relay.mastodon.host"]}
381 end
382 end
383
384 describe "/users/:nickname/inbox" do
385 setup do
386 data =
387 File.read!("test/fixtures/mastodon-post-activity.json")
388 |> Poison.decode!()
389
390 [data: data]
391 end
392
393 test "it inserts an incoming activity into the database", %{conn: conn, data: data} do
394 user = insert(:user)
395 data = Map.put(data, "bcc", [user.ap_id])
396
397 conn =
398 conn
399 |> assign(:valid_signature, true)
400 |> put_req_header("content-type", "application/activity+json")
401 |> post("/users/#{user.nickname}/inbox", data)
402
403 assert "ok" == json_response(conn, 200)
404 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
405 assert Activity.get_by_ap_id(data["id"])
406 end
407
408 test "it accepts messages with to as string instead of array", %{conn: conn, data: data} do
409 user = insert(:user)
410
411 data =
412 Map.put(data, "to", user.ap_id)
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 messages with cc as string instead of array", %{conn: conn, data: data} do
427 user = insert(:user)
428
429 data =
430 Map.put(data, "cc", user.ap_id)
431 |> Map.delete("to")
432
433 conn =
434 conn
435 |> assign(:valid_signature, true)
436 |> put_req_header("content-type", "application/activity+json")
437 |> post("/users/#{user.nickname}/inbox", data)
438
439 assert "ok" == json_response(conn, 200)
440 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
441 %Activity{} = activity = Activity.get_by_ap_id(data["id"])
442 assert user.ap_id in activity.recipients
443 end
444
445 test "it accepts messages with bcc as string instead of array", %{conn: conn, data: data} do
446 user = insert(:user)
447
448 data =
449 Map.put(data, "bcc", user.ap_id)
450 |> Map.delete("to")
451 |> Map.delete("cc")
452
453 conn =
454 conn
455 |> assign(:valid_signature, true)
456 |> put_req_header("content-type", "application/activity+json")
457 |> post("/users/#{user.nickname}/inbox", data)
458
459 assert "ok" == json_response(conn, 200)
460 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
461 assert Activity.get_by_ap_id(data["id"])
462 end
463
464 test "it accepts announces with to as string instead of array", %{conn: conn} do
465 user = insert(:user)
466
467 data = %{
468 "@context" => "https://www.w3.org/ns/activitystreams",
469 "actor" => "http://mastodon.example.org/users/admin",
470 "id" => "http://mastodon.example.org/users/admin/statuses/19512778738411822/activity",
471 "object" => "https://mastodon.social/users/emelie/statuses/101849165031453009",
472 "to" => "https://www.w3.org/ns/activitystreams#Public",
473 "cc" => [user.ap_id],
474 "type" => "Announce"
475 }
476
477 conn =
478 conn
479 |> assign(:valid_signature, true)
480 |> put_req_header("content-type", "application/activity+json")
481 |> post("/users/#{user.nickname}/inbox", data)
482
483 assert "ok" == json_response(conn, 200)
484 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
485 %Activity{} = activity = Activity.get_by_ap_id(data["id"])
486 assert "https://www.w3.org/ns/activitystreams#Public" in activity.recipients
487 end
488
489 test "it accepts messages from actors that are followed by the user", %{
490 conn: conn,
491 data: data
492 } do
493 recipient = insert(:user)
494 actor = insert(:user, %{ap_id: "http://mastodon.example.org/users/actor"})
495
496 {:ok, recipient} = User.follow(recipient, actor)
497
498 object =
499 data["object"]
500 |> Map.put("attributedTo", actor.ap_id)
501
502 data =
503 data
504 |> Map.put("actor", actor.ap_id)
505 |> Map.put("object", object)
506
507 conn =
508 conn
509 |> assign(:valid_signature, true)
510 |> put_req_header("content-type", "application/activity+json")
511 |> post("/users/#{recipient.nickname}/inbox", data)
512
513 assert "ok" == json_response(conn, 200)
514 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
515 assert Activity.get_by_ap_id(data["id"])
516 end
517
518 test "it rejects reads from other users", %{conn: conn} do
519 user = insert(:user)
520 otheruser = insert(:user)
521
522 conn =
523 conn
524 |> assign(:user, otheruser)
525 |> put_req_header("accept", "application/activity+json")
526 |> get("/users/#{user.nickname}/inbox")
527
528 assert json_response(conn, 403)
529 end
530
531 test "it doesn't crash without an authenticated user", %{conn: conn} do
532 user = insert(:user)
533
534 conn =
535 conn
536 |> put_req_header("accept", "application/activity+json")
537 |> get("/users/#{user.nickname}/inbox")
538
539 assert json_response(conn, 403)
540 end
541
542 test "it returns a note activity in a collection", %{conn: conn} do
543 note_activity = insert(:direct_note_activity)
544 note_object = Object.normalize(note_activity)
545 user = User.get_cached_by_ap_id(hd(note_activity.data["to"]))
546
547 conn =
548 conn
549 |> assign(:user, user)
550 |> put_req_header("accept", "application/activity+json")
551 |> get("/users/#{user.nickname}/inbox?page=true")
552
553 assert response(conn, 200) =~ note_object.data["content"]
554 end
555
556 test "it clears `unreachable` federation status of the sender", %{conn: conn, data: data} do
557 user = insert(:user)
558 data = Map.put(data, "bcc", [user.ap_id])
559
560 sender_host = URI.parse(data["actor"]).host
561 Instances.set_consistently_unreachable(sender_host)
562 refute Instances.reachable?(sender_host)
563
564 conn =
565 conn
566 |> assign(:valid_signature, true)
567 |> put_req_header("content-type", "application/activity+json")
568 |> post("/users/#{user.nickname}/inbox", data)
569
570 assert "ok" == json_response(conn, 200)
571 assert Instances.reachable?(sender_host)
572 end
573
574 test "it removes all follower collections but actor's", %{conn: conn} do
575 [actor, recipient] = insert_pair(:user)
576
577 data =
578 File.read!("test/fixtures/activitypub-client-post-activity.json")
579 |> Poison.decode!()
580
581 object = Map.put(data["object"], "attributedTo", actor.ap_id)
582
583 data =
584 data
585 |> Map.put("id", Utils.generate_object_id())
586 |> Map.put("actor", actor.ap_id)
587 |> Map.put("object", object)
588 |> Map.put("cc", [
589 recipient.follower_address,
590 actor.follower_address
591 ])
592 |> Map.put("to", [
593 recipient.ap_id,
594 recipient.follower_address,
595 "https://www.w3.org/ns/activitystreams#Public"
596 ])
597
598 conn
599 |> assign(:valid_signature, true)
600 |> put_req_header("content-type", "application/activity+json")
601 |> post("/users/#{recipient.nickname}/inbox", data)
602 |> json_response(200)
603
604 ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
605
606 activity = Activity.get_by_ap_id(data["id"])
607
608 assert activity.id
609 assert actor.follower_address in activity.recipients
610 assert actor.follower_address in activity.data["cc"]
611
612 refute recipient.follower_address in activity.recipients
613 refute recipient.follower_address in activity.data["cc"]
614 refute recipient.follower_address in activity.data["to"]
615 end
616 end
617
618 describe "/users/:nickname/outbox" do
619 test "it will not bomb when there is no activity", %{conn: conn} do
620 user = insert(:user)
621
622 conn =
623 conn
624 |> put_req_header("accept", "application/activity+json")
625 |> get("/users/#{user.nickname}/outbox")
626
627 result = json_response(conn, 200)
628 assert user.ap_id <> "/outbox" == result["id"]
629 end
630
631 test "it returns a note activity in a collection", %{conn: conn} do
632 note_activity = insert(:note_activity)
633 note_object = Object.normalize(note_activity)
634 user = User.get_cached_by_ap_id(note_activity.data["actor"])
635
636 conn =
637 conn
638 |> put_req_header("accept", "application/activity+json")
639 |> get("/users/#{user.nickname}/outbox?page=true")
640
641 assert response(conn, 200) =~ note_object.data["content"]
642 end
643
644 test "it returns an announce activity in a collection", %{conn: conn} do
645 announce_activity = insert(:announce_activity)
646 user = User.get_cached_by_ap_id(announce_activity.data["actor"])
647
648 conn =
649 conn
650 |> put_req_header("accept", "application/activity+json")
651 |> get("/users/#{user.nickname}/outbox?page=true")
652
653 assert response(conn, 200) =~ announce_activity.data["object"]
654 end
655 end
656
657 describe "POST /users/:nickname/outbox (C2S)" do
658 setup do
659 [
660 activity: %{
661 "@context" => "https://www.w3.org/ns/activitystreams",
662 "type" => "Create",
663 "object" => %{"type" => "Note", "content" => "AP C2S test"},
664 "to" => "https://www.w3.org/ns/activitystreams#Public",
665 "cc" => []
666 }
667 ]
668 end
669
670 test "it rejects posts from other users / unauthenticated users", %{
671 conn: conn,
672 activity: activity
673 } do
674 user = insert(:user)
675 otheruser = insert(:user)
676
677 conn =
678 conn
679 |> assign(:user, otheruser)
680 |> put_req_header("content-type", "application/activity+json")
681 |> post("/users/#{user.nickname}/outbox", activity)
682
683 assert json_response(conn, 403)
684 end
685
686 test "it inserts an incoming create activity into the database", %{
687 conn: conn,
688 activity: activity
689 } do
690 user = insert(:user)
691
692 result =
693 conn
694 |> assign(:user, user)
695 |> put_req_header("content-type", "application/activity+json")
696 |> post("/users/#{user.nickname}/outbox", activity)
697 |> json_response(201)
698
699 assert Activity.get_by_ap_id(result["id"])
700 assert result["object"]
701 assert %Object{data: object} = Object.normalize(result["object"])
702 assert object["content"] == activity["object"]["content"]
703 end
704
705 test "it inserts an incoming sensitive activity into the database", %{
706 conn: conn,
707 activity: activity
708 } do
709 user = insert(:user)
710 object = Map.put(activity["object"], "sensitive", true)
711 activity = Map.put(activity, "object", object)
712
713 result =
714 conn
715 |> assign(:user, user)
716 |> put_req_header("content-type", "application/activity+json")
717 |> post("/users/#{user.nickname}/outbox", activity)
718 |> json_response(201)
719
720 assert Activity.get_by_ap_id(result["id"])
721 assert result["object"]
722 assert %Object{data: object} = Object.normalize(result["object"])
723 assert object["sensitive"] == activity["object"]["sensitive"]
724 assert object["content"] == activity["object"]["content"]
725 end
726
727 test "it rejects an incoming activity with bogus type", %{conn: conn, activity: activity} do
728 user = insert(:user)
729 activity = Map.put(activity, "type", "BadType")
730
731 conn =
732 conn
733 |> assign(:user, user)
734 |> put_req_header("content-type", "application/activity+json")
735 |> post("/users/#{user.nickname}/outbox", activity)
736
737 assert json_response(conn, 400)
738 end
739
740 test "it erects a tombstone when receiving a delete activity", %{conn: conn} do
741 note_activity = insert(:note_activity)
742 note_object = Object.normalize(note_activity)
743 user = User.get_cached_by_ap_id(note_activity.data["actor"])
744
745 data = %{
746 type: "Delete",
747 object: %{
748 id: note_object.data["id"]
749 }
750 }
751
752 conn =
753 conn
754 |> assign(:user, user)
755 |> put_req_header("content-type", "application/activity+json")
756 |> post("/users/#{user.nickname}/outbox", data)
757
758 result = json_response(conn, 201)
759 assert Activity.get_by_ap_id(result["id"])
760
761 assert object = Object.get_by_ap_id(note_object.data["id"])
762 assert object.data["type"] == "Tombstone"
763 end
764
765 test "it rejects delete activity of object from other actor", %{conn: conn} do
766 note_activity = insert(:note_activity)
767 note_object = Object.normalize(note_activity)
768 user = insert(:user)
769
770 data = %{
771 type: "Delete",
772 object: %{
773 id: note_object.data["id"]
774 }
775 }
776
777 conn =
778 conn
779 |> assign(:user, user)
780 |> put_req_header("content-type", "application/activity+json")
781 |> post("/users/#{user.nickname}/outbox", data)
782
783 assert json_response(conn, 400)
784 end
785
786 test "it increases like count when receiving a like action", %{conn: conn} do
787 note_activity = insert(:note_activity)
788 note_object = Object.normalize(note_activity)
789 user = User.get_cached_by_ap_id(note_activity.data["actor"])
790
791 data = %{
792 type: "Like",
793 object: %{
794 id: note_object.data["id"]
795 }
796 }
797
798 conn =
799 conn
800 |> assign(:user, user)
801 |> put_req_header("content-type", "application/activity+json")
802 |> post("/users/#{user.nickname}/outbox", data)
803
804 result = json_response(conn, 201)
805 assert Activity.get_by_ap_id(result["id"])
806
807 assert object = Object.get_by_ap_id(note_object.data["id"])
808 assert object.data["like_count"] == 1
809 end
810 end
811
812 describe "/relay/followers" do
813 test "it returns relay followers", %{conn: conn} do
814 relay_actor = Relay.get_actor()
815 user = insert(:user)
816 User.follow(user, relay_actor)
817
818 result =
819 conn
820 |> assign(:relay, true)
821 |> get("/relay/followers")
822 |> json_response(200)
823
824 assert result["first"]["orderedItems"] == [user.ap_id]
825 end
826 end
827
828 describe "/relay/following" do
829 test "it returns relay following", %{conn: conn} do
830 result =
831 conn
832 |> assign(:relay, true)
833 |> get("/relay/following")
834 |> json_response(200)
835
836 assert result["first"]["orderedItems"] == []
837 end
838 end
839
840 describe "/users/:nickname/followers" do
841 test "it returns the followers in a collection", %{conn: conn} do
842 user = insert(:user)
843 user_two = insert(:user)
844 User.follow(user, user_two)
845
846 result =
847 conn
848 |> get("/users/#{user_two.nickname}/followers")
849 |> json_response(200)
850
851 assert result["first"]["orderedItems"] == [user.ap_id]
852 end
853
854 test "it returns returns a uri if the user has 'hide_followers' set", %{conn: conn} do
855 user = insert(:user)
856 user_two = insert(:user, hide_followers: true)
857 User.follow(user, user_two)
858
859 result =
860 conn
861 |> get("/users/#{user_two.nickname}/followers")
862 |> json_response(200)
863
864 assert is_binary(result["first"])
865 end
866
867 test "it returns a 403 error on pages, if the user has 'hide_followers' set and the request is not authenticated",
868 %{conn: conn} do
869 user = insert(:user, hide_followers: true)
870
871 result =
872 conn
873 |> get("/users/#{user.nickname}/followers?page=1")
874
875 assert result.status == 403
876 assert result.resp_body == ""
877 end
878
879 test "it renders the page, if the user has 'hide_followers' set and the request is authenticated with the same user",
880 %{conn: conn} do
881 user = insert(:user, hide_followers: true)
882 other_user = insert(:user)
883 {:ok, _other_user, user, _activity} = CommonAPI.follow(other_user, user)
884
885 result =
886 conn
887 |> assign(:user, user)
888 |> get("/users/#{user.nickname}/followers?page=1")
889 |> json_response(200)
890
891 assert result["totalItems"] == 1
892 assert result["orderedItems"] == [other_user.ap_id]
893 end
894
895 test "it works for more than 10 users", %{conn: conn} do
896 user = insert(:user)
897
898 Enum.each(1..15, fn _ ->
899 other_user = insert(:user)
900 User.follow(other_user, user)
901 end)
902
903 result =
904 conn
905 |> get("/users/#{user.nickname}/followers")
906 |> json_response(200)
907
908 assert length(result["first"]["orderedItems"]) == 10
909 assert result["first"]["totalItems"] == 15
910 assert result["totalItems"] == 15
911
912 result =
913 conn
914 |> get("/users/#{user.nickname}/followers?page=2")
915 |> json_response(200)
916
917 assert length(result["orderedItems"]) == 5
918 assert result["totalItems"] == 15
919 end
920 end
921
922 describe "/users/:nickname/following" do
923 test "it returns the following in a collection", %{conn: conn} do
924 user = insert(:user)
925 user_two = insert(:user)
926 User.follow(user, user_two)
927
928 result =
929 conn
930 |> get("/users/#{user.nickname}/following")
931 |> json_response(200)
932
933 assert result["first"]["orderedItems"] == [user_two.ap_id]
934 end
935
936 test "it returns a uri if the user has 'hide_follows' set", %{conn: conn} do
937 user = insert(:user, hide_follows: true)
938 user_two = insert(:user)
939 User.follow(user, user_two)
940
941 result =
942 conn
943 |> get("/users/#{user.nickname}/following")
944 |> json_response(200)
945
946 assert is_binary(result["first"])
947 end
948
949 test "it returns a 403 error on pages, if the user has 'hide_follows' set and the request is not authenticated",
950 %{conn: conn} do
951 user = insert(:user, hide_follows: true)
952
953 result =
954 conn
955 |> get("/users/#{user.nickname}/following?page=1")
956
957 assert result.status == 403
958 assert result.resp_body == ""
959 end
960
961 test "it renders the page, if the user has 'hide_follows' set and the request is authenticated with the same user",
962 %{conn: conn} do
963 user = insert(:user, hide_follows: true)
964 other_user = insert(:user)
965 {:ok, user, _other_user, _activity} = CommonAPI.follow(user, other_user)
966
967 result =
968 conn
969 |> assign(:user, user)
970 |> get("/users/#{user.nickname}/following?page=1")
971 |> json_response(200)
972
973 assert result["totalItems"] == 1
974 assert result["orderedItems"] == [other_user.ap_id]
975 end
976
977 test "it works for more than 10 users", %{conn: conn} do
978 user = insert(:user)
979
980 Enum.each(1..15, fn _ ->
981 user = User.get_cached_by_id(user.id)
982 other_user = insert(:user)
983 User.follow(user, other_user)
984 end)
985
986 result =
987 conn
988 |> get("/users/#{user.nickname}/following")
989 |> json_response(200)
990
991 assert length(result["first"]["orderedItems"]) == 10
992 assert result["first"]["totalItems"] == 15
993 assert result["totalItems"] == 15
994
995 result =
996 conn
997 |> get("/users/#{user.nickname}/following?page=2")
998 |> json_response(200)
999
1000 assert length(result["orderedItems"]) == 5
1001 assert result["totalItems"] == 15
1002 end
1003 end
1004
1005 describe "delivery tracking" do
1006 test "it tracks a signed object fetch", %{conn: conn} do
1007 user = insert(:user, local: false)
1008 activity = insert(:note_activity)
1009 object = Object.normalize(activity)
1010
1011 object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
1012
1013 conn
1014 |> put_req_header("accept", "application/activity+json")
1015 |> assign(:user, user)
1016 |> get(object_path)
1017 |> json_response(200)
1018
1019 assert Delivery.get(object.id, user.id)
1020 end
1021
1022 test "it tracks a signed activity fetch", %{conn: conn} do
1023 user = insert(:user, local: false)
1024 activity = insert(:note_activity)
1025 object = Object.normalize(activity)
1026
1027 activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url())
1028
1029 conn
1030 |> put_req_header("accept", "application/activity+json")
1031 |> assign(:user, user)
1032 |> get(activity_path)
1033 |> json_response(200)
1034
1035 assert Delivery.get(object.id, user.id)
1036 end
1037
1038 test "it tracks a signed object fetch when the json is cached", %{conn: conn} do
1039 user = insert(:user, local: false)
1040 other_user = insert(:user, local: false)
1041 activity = insert(:note_activity)
1042 object = Object.normalize(activity)
1043
1044 object_path = String.trim_leading(object.data["id"], Pleroma.Web.Endpoint.url())
1045
1046 conn
1047 |> put_req_header("accept", "application/activity+json")
1048 |> assign(:user, user)
1049 |> get(object_path)
1050 |> json_response(200)
1051
1052 build_conn()
1053 |> put_req_header("accept", "application/activity+json")
1054 |> assign(:user, other_user)
1055 |> get(object_path)
1056 |> json_response(200)
1057
1058 assert Delivery.get(object.id, user.id)
1059 assert Delivery.get(object.id, other_user.id)
1060 end
1061
1062 test "it tracks a signed activity fetch when the json is cached", %{conn: conn} do
1063 user = insert(:user, local: false)
1064 other_user = insert(:user, local: false)
1065 activity = insert(:note_activity)
1066 object = Object.normalize(activity)
1067
1068 activity_path = String.trim_leading(activity.data["id"], Pleroma.Web.Endpoint.url())
1069
1070 conn
1071 |> put_req_header("accept", "application/activity+json")
1072 |> assign(:user, user)
1073 |> get(activity_path)
1074 |> json_response(200)
1075
1076 build_conn()
1077 |> put_req_header("accept", "application/activity+json")
1078 |> assign(:user, other_user)
1079 |> get(activity_path)
1080 |> json_response(200)
1081
1082 assert Delivery.get(object.id, user.id)
1083 assert Delivery.get(object.id, other_user.id)
1084 end
1085 end
1086
1087 describe "Additionnal ActivityPub C2S endpoints" do
1088 test "/api/ap/whoami", %{conn: conn} do
1089 user = insert(:user)
1090
1091 conn =
1092 conn
1093 |> assign(:user, user)
1094 |> get("/api/ap/whoami")
1095
1096 user = User.get_cached_by_id(user.id)
1097
1098 assert UserView.render("user.json", %{user: user}) == json_response(conn, 200)
1099 end
1100
1101 clear_config([:media_proxy])
1102 clear_config([Pleroma.Upload])
1103
1104 test "uploadMedia", %{conn: conn} do
1105 user = insert(:user)
1106
1107 desc = "Description of the image"
1108
1109 image = %Plug.Upload{
1110 content_type: "image/jpg",
1111 path: Path.absname("test/fixtures/image.jpg"),
1112 filename: "an_image.jpg"
1113 }
1114
1115 conn =
1116 conn
1117 |> assign(:user, user)
1118 |> post("/api/ap/upload_media", %{"file" => image, "description" => desc})
1119
1120 assert object = json_response(conn, :created)
1121 assert object["name"] == desc
1122 assert object["type"] == "Document"
1123 assert object["actor"] == user.ap_id
1124 end
1125 end
1126 end