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