Separate Subscription Notifications from regular Notifications
[akkoma] / test / web / mastodon_api / mastodon_api_controller_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
6 use Pleroma.Web.ConnCase
7
8 alias Ecto.Changeset
9 alias Pleroma.Activity
10 alias Pleroma.ActivityExpiration
11 alias Pleroma.Config
12 alias Pleroma.Notification
13 alias Pleroma.Object
14 alias Pleroma.Repo
15 alias Pleroma.ScheduledActivity
16 alias Pleroma.SubscriptionNotification
17 alias Pleroma.User
18 alias Pleroma.Web.ActivityPub.ActivityPub
19 alias Pleroma.Web.CommonAPI
20 alias Pleroma.Web.MastodonAPI.FilterView
21 alias Pleroma.Web.OAuth.App
22 alias Pleroma.Web.OAuth.Token
23 alias Pleroma.Web.OStatus
24 alias Pleroma.Web.Push
25 import Pleroma.Factory
26 import ExUnit.CaptureLog
27 import Tesla.Mock
28 import Swoosh.TestAssertions
29
30 @image "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
31
32 setup do
33 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
34 :ok
35 end
36
37 clear_config([:instance, :public])
38 clear_config([:rich_media, :enabled])
39
40 test "the home timeline", %{conn: conn} do
41 user = insert(:user)
42 following = insert(:user)
43
44 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
45
46 conn =
47 conn
48 |> assign(:user, user)
49 |> get("/api/v1/timelines/home")
50
51 assert Enum.empty?(json_response(conn, 200))
52
53 {:ok, user} = User.follow(user, following)
54
55 conn =
56 build_conn()
57 |> assign(:user, user)
58 |> get("/api/v1/timelines/home")
59
60 assert [%{"content" => "test"}] = json_response(conn, 200)
61 end
62
63 test "the public timeline", %{conn: conn} do
64 following = insert(:user)
65
66 capture_log(fn ->
67 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
68
69 {:ok, [_activity]} =
70 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
71
72 conn =
73 conn
74 |> get("/api/v1/timelines/public", %{"local" => "False"})
75
76 assert length(json_response(conn, 200)) == 2
77
78 conn =
79 build_conn()
80 |> get("/api/v1/timelines/public", %{"local" => "True"})
81
82 assert [%{"content" => "test"}] = json_response(conn, 200)
83
84 conn =
85 build_conn()
86 |> get("/api/v1/timelines/public", %{"local" => "1"})
87
88 assert [%{"content" => "test"}] = json_response(conn, 200)
89 end)
90 end
91
92 test "the public timeline when public is set to false", %{conn: conn} do
93 Config.put([:instance, :public], false)
94
95 assert conn
96 |> get("/api/v1/timelines/public", %{"local" => "False"})
97 |> json_response(403) == %{"error" => "This resource requires authentication."}
98 end
99
100 describe "posting statuses" do
101 setup do
102 user = insert(:user)
103
104 conn =
105 build_conn()
106 |> assign(:user, user)
107
108 [conn: conn]
109 end
110
111 test "posting a status", %{conn: conn} do
112 idempotency_key = "Pikachu rocks!"
113
114 conn_one =
115 conn
116 |> put_req_header("idempotency-key", idempotency_key)
117 |> post("/api/v1/statuses", %{
118 "status" => "cofe",
119 "spoiler_text" => "2hu",
120 "sensitive" => "false"
121 })
122
123 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
124 # Six hours
125 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
126
127 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
128 json_response(conn_one, 200)
129
130 assert Activity.get_by_id(id)
131
132 conn_two =
133 conn
134 |> put_req_header("idempotency-key", idempotency_key)
135 |> post("/api/v1/statuses", %{
136 "status" => "cofe",
137 "spoiler_text" => "2hu",
138 "sensitive" => "false"
139 })
140
141 assert %{"id" => second_id} = json_response(conn_two, 200)
142 assert id == second_id
143
144 conn_three =
145 conn
146 |> post("/api/v1/statuses", %{
147 "status" => "cofe",
148 "spoiler_text" => "2hu",
149 "sensitive" => "false"
150 })
151
152 assert %{"id" => third_id} = json_response(conn_three, 200)
153 refute id == third_id
154
155 # An activity that will expire:
156 # 2 hours
157 expires_in = 120 * 60
158
159 conn_four =
160 conn
161 |> post("api/v1/statuses", %{
162 "status" => "oolong",
163 "expires_in" => expires_in
164 })
165
166 assert fourth_response = %{"id" => fourth_id} = json_response(conn_four, 200)
167 assert activity = Activity.get_by_id(fourth_id)
168 assert expiration = ActivityExpiration.get_by_activity_id(fourth_id)
169
170 estimated_expires_at =
171 NaiveDateTime.utc_now()
172 |> NaiveDateTime.add(expires_in)
173 |> NaiveDateTime.truncate(:second)
174
175 # This assert will fail if the test takes longer than a minute. I sure hope it never does:
176 assert abs(NaiveDateTime.diff(expiration.scheduled_at, estimated_expires_at, :second)) < 60
177
178 assert fourth_response["pleroma"]["expires_at"] ==
179 NaiveDateTime.to_iso8601(expiration.scheduled_at)
180 end
181
182 test "replying to a status", %{conn: conn} do
183 user = insert(:user)
184 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "cofe"})
185
186 conn =
187 conn
188 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
189
190 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
191
192 activity = Activity.get_by_id(id)
193
194 assert activity.data["context"] == replied_to.data["context"]
195 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
196 end
197
198 test "replying to a direct message with visibility other than direct", %{conn: conn} do
199 user = insert(:user)
200 {:ok, replied_to} = CommonAPI.post(user, %{"status" => "suya..", "visibility" => "direct"})
201
202 Enum.each(["public", "private", "unlisted"], fn visibility ->
203 conn =
204 conn
205 |> post("/api/v1/statuses", %{
206 "status" => "@#{user.nickname} hey",
207 "in_reply_to_id" => replied_to.id,
208 "visibility" => visibility
209 })
210
211 assert json_response(conn, 422) == %{"error" => "The message visibility must be direct"}
212 end)
213 end
214
215 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
216 conn =
217 conn
218 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
219
220 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
221 assert Activity.get_by_id(id)
222 end
223
224 test "posting a sensitive status", %{conn: conn} do
225 conn =
226 conn
227 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
228
229 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
230 assert Activity.get_by_id(id)
231 end
232
233 test "posting a fake status", %{conn: conn} do
234 real_conn =
235 conn
236 |> post("/api/v1/statuses", %{
237 "status" =>
238 "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it"
239 })
240
241 real_status = json_response(real_conn, 200)
242
243 assert real_status
244 assert Object.get_by_ap_id(real_status["uri"])
245
246 real_status =
247 real_status
248 |> Map.put("id", nil)
249 |> Map.put("url", nil)
250 |> Map.put("uri", nil)
251 |> Map.put("created_at", nil)
252 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
253
254 fake_conn =
255 conn
256 |> post("/api/v1/statuses", %{
257 "status" =>
258 "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it",
259 "preview" => true
260 })
261
262 fake_status = json_response(fake_conn, 200)
263
264 assert fake_status
265 refute Object.get_by_ap_id(fake_status["uri"])
266
267 fake_status =
268 fake_status
269 |> Map.put("id", nil)
270 |> Map.put("url", nil)
271 |> Map.put("uri", nil)
272 |> Map.put("created_at", nil)
273 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
274
275 assert real_status == fake_status
276 end
277
278 test "posting a status with OGP link preview", %{conn: conn} do
279 Config.put([:rich_media, :enabled], true)
280
281 conn =
282 conn
283 |> post("/api/v1/statuses", %{
284 "status" => "https://example.com/ogp"
285 })
286
287 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
288 assert Activity.get_by_id(id)
289 end
290
291 test "posting a direct status", %{conn: conn} do
292 user2 = insert(:user)
293 content = "direct cofe @#{user2.nickname}"
294
295 conn =
296 conn
297 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
298
299 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
300 assert activity = Activity.get_by_id(id)
301 assert activity.recipients == [user2.ap_id, conn.assigns[:user].ap_id]
302 assert activity.data["to"] == [user2.ap_id]
303 assert activity.data["cc"] == []
304 end
305 end
306
307 describe "posting polls" do
308 test "posting a poll", %{conn: conn} do
309 user = insert(:user)
310 time = NaiveDateTime.utc_now()
311
312 conn =
313 conn
314 |> assign(:user, user)
315 |> post("/api/v1/statuses", %{
316 "status" => "Who is the #bestgrill?",
317 "poll" => %{"options" => ["Rei", "Asuka", "Misato"], "expires_in" => 420}
318 })
319
320 response = json_response(conn, 200)
321
322 assert Enum.all?(response["poll"]["options"], fn %{"title" => title} ->
323 title in ["Rei", "Asuka", "Misato"]
324 end)
325
326 assert NaiveDateTime.diff(NaiveDateTime.from_iso8601!(response["poll"]["expires_at"]), time) in 420..430
327 refute response["poll"]["expred"]
328 end
329
330 test "option limit is enforced", %{conn: conn} do
331 user = insert(:user)
332 limit = Config.get([:instance, :poll_limits, :max_options])
333
334 conn =
335 conn
336 |> assign(:user, user)
337 |> post("/api/v1/statuses", %{
338 "status" => "desu~",
339 "poll" => %{"options" => Enum.map(0..limit, fn _ -> "desu" end), "expires_in" => 1}
340 })
341
342 %{"error" => error} = json_response(conn, 422)
343 assert error == "Poll can't contain more than #{limit} options"
344 end
345
346 test "option character limit is enforced", %{conn: conn} do
347 user = insert(:user)
348 limit = Config.get([:instance, :poll_limits, :max_option_chars])
349
350 conn =
351 conn
352 |> assign(:user, user)
353 |> post("/api/v1/statuses", %{
354 "status" => "...",
355 "poll" => %{
356 "options" => [Enum.reduce(0..limit, "", fn _, acc -> acc <> "." end)],
357 "expires_in" => 1
358 }
359 })
360
361 %{"error" => error} = json_response(conn, 422)
362 assert error == "Poll options cannot be longer than #{limit} characters each"
363 end
364
365 test "minimal date limit is enforced", %{conn: conn} do
366 user = insert(:user)
367 limit = Config.get([:instance, :poll_limits, :min_expiration])
368
369 conn =
370 conn
371 |> assign(:user, user)
372 |> post("/api/v1/statuses", %{
373 "status" => "imagine arbitrary limits",
374 "poll" => %{
375 "options" => ["this post was made by pleroma gang"],
376 "expires_in" => limit - 1
377 }
378 })
379
380 %{"error" => error} = json_response(conn, 422)
381 assert error == "Expiration date is too soon"
382 end
383
384 test "maximum date limit is enforced", %{conn: conn} do
385 user = insert(:user)
386 limit = Config.get([:instance, :poll_limits, :max_expiration])
387
388 conn =
389 conn
390 |> assign(:user, user)
391 |> post("/api/v1/statuses", %{
392 "status" => "imagine arbitrary limits",
393 "poll" => %{
394 "options" => ["this post was made by pleroma gang"],
395 "expires_in" => limit + 1
396 }
397 })
398
399 %{"error" => error} = json_response(conn, 422)
400 assert error == "Expiration date is too far in the future"
401 end
402 end
403
404 test "direct timeline", %{conn: conn} do
405 user_one = insert(:user)
406 user_two = insert(:user)
407
408 {:ok, user_two} = User.follow(user_two, user_one)
409
410 {:ok, direct} =
411 CommonAPI.post(user_one, %{
412 "status" => "Hi @#{user_two.nickname}!",
413 "visibility" => "direct"
414 })
415
416 {:ok, _follower_only} =
417 CommonAPI.post(user_one, %{
418 "status" => "Hi @#{user_two.nickname}!",
419 "visibility" => "private"
420 })
421
422 # Only direct should be visible here
423 res_conn =
424 conn
425 |> assign(:user, user_two)
426 |> get("api/v1/timelines/direct")
427
428 [status] = json_response(res_conn, 200)
429
430 assert %{"visibility" => "direct"} = status
431 assert status["url"] != direct.data["id"]
432
433 # User should be able to see their own direct message
434 res_conn =
435 build_conn()
436 |> assign(:user, user_one)
437 |> get("api/v1/timelines/direct")
438
439 [status] = json_response(res_conn, 200)
440
441 assert %{"visibility" => "direct"} = status
442
443 # Both should be visible here
444 res_conn =
445 conn
446 |> assign(:user, user_two)
447 |> get("api/v1/timelines/home")
448
449 [_s1, _s2] = json_response(res_conn, 200)
450
451 # Test pagination
452 Enum.each(1..20, fn _ ->
453 {:ok, _} =
454 CommonAPI.post(user_one, %{
455 "status" => "Hi @#{user_two.nickname}!",
456 "visibility" => "direct"
457 })
458 end)
459
460 res_conn =
461 conn
462 |> assign(:user, user_two)
463 |> get("api/v1/timelines/direct")
464
465 statuses = json_response(res_conn, 200)
466 assert length(statuses) == 20
467
468 res_conn =
469 conn
470 |> assign(:user, user_two)
471 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
472
473 [status] = json_response(res_conn, 200)
474
475 assert status["url"] != direct.data["id"]
476 end
477
478 test "Conversations", %{conn: conn} do
479 user_one = insert(:user)
480 user_two = insert(:user)
481 user_three = insert(:user)
482
483 {:ok, user_two} = User.follow(user_two, user_one)
484
485 {:ok, direct} =
486 CommonAPI.post(user_one, %{
487 "status" => "Hi @#{user_two.nickname}, @#{user_three.nickname}!",
488 "visibility" => "direct"
489 })
490
491 {:ok, _follower_only} =
492 CommonAPI.post(user_one, %{
493 "status" => "Hi @#{user_two.nickname}!",
494 "visibility" => "private"
495 })
496
497 res_conn =
498 conn
499 |> assign(:user, user_one)
500 |> get("/api/v1/conversations")
501
502 assert response = json_response(res_conn, 200)
503
504 assert [
505 %{
506 "id" => res_id,
507 "accounts" => res_accounts,
508 "last_status" => res_last_status,
509 "unread" => unread
510 }
511 ] = response
512
513 account_ids = Enum.map(res_accounts, & &1["id"])
514 assert length(res_accounts) == 2
515 assert user_two.id in account_ids
516 assert user_three.id in account_ids
517 assert is_binary(res_id)
518 assert unread == true
519 assert res_last_status["id"] == direct.id
520
521 # Apparently undocumented API endpoint
522 res_conn =
523 conn
524 |> assign(:user, user_one)
525 |> post("/api/v1/conversations/#{res_id}/read")
526
527 assert response = json_response(res_conn, 200)
528 assert length(response["accounts"]) == 2
529 assert response["last_status"]["id"] == direct.id
530 assert response["unread"] == false
531
532 # (vanilla) Mastodon frontend behaviour
533 res_conn =
534 conn
535 |> assign(:user, user_one)
536 |> get("/api/v1/statuses/#{res_last_status["id"]}/context")
537
538 assert %{"ancestors" => [], "descendants" => []} == json_response(res_conn, 200)
539 end
540
541 test "doesn't include DMs from blocked users", %{conn: conn} do
542 blocker = insert(:user)
543 blocked = insert(:user)
544 user = insert(:user)
545 {:ok, blocker} = User.block(blocker, blocked)
546
547 {:ok, _blocked_direct} =
548 CommonAPI.post(blocked, %{
549 "status" => "Hi @#{blocker.nickname}!",
550 "visibility" => "direct"
551 })
552
553 {:ok, direct} =
554 CommonAPI.post(user, %{
555 "status" => "Hi @#{blocker.nickname}!",
556 "visibility" => "direct"
557 })
558
559 res_conn =
560 conn
561 |> assign(:user, user)
562 |> get("api/v1/timelines/direct")
563
564 [status] = json_response(res_conn, 200)
565 assert status["id"] == direct.id
566 end
567
568 test "verify_credentials", %{conn: conn} do
569 user = insert(:user)
570
571 conn =
572 conn
573 |> assign(:user, user)
574 |> get("/api/v1/accounts/verify_credentials")
575
576 response = json_response(conn, 200)
577
578 assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
579 assert response["pleroma"]["chat_token"]
580 assert id == to_string(user.id)
581 end
582
583 test "verify_credentials default scope unlisted", %{conn: conn} do
584 user = insert(:user, %{info: %User.Info{default_scope: "unlisted"}})
585
586 conn =
587 conn
588 |> assign(:user, user)
589 |> get("/api/v1/accounts/verify_credentials")
590
591 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
592 assert id == to_string(user.id)
593 end
594
595 test "apps/verify_credentials", %{conn: conn} do
596 token = insert(:oauth_token)
597
598 conn =
599 conn
600 |> assign(:user, token.user)
601 |> assign(:token, token)
602 |> get("/api/v1/apps/verify_credentials")
603
604 app = Repo.preload(token, :app).app
605
606 expected = %{
607 "name" => app.client_name,
608 "website" => app.website,
609 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
610 }
611
612 assert expected == json_response(conn, 200)
613 end
614
615 test "user avatar can be set", %{conn: conn} do
616 user = insert(:user)
617 avatar_image = File.read!("test/fixtures/avatar_data_uri")
618
619 conn =
620 conn
621 |> assign(:user, user)
622 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: avatar_image})
623
624 user = refresh_record(user)
625
626 assert %{
627 "name" => _,
628 "type" => _,
629 "url" => [
630 %{
631 "href" => _,
632 "mediaType" => _,
633 "type" => _
634 }
635 ]
636 } = user.avatar
637
638 assert %{"url" => _} = json_response(conn, 200)
639 end
640
641 test "user avatar can be reset", %{conn: conn} do
642 user = insert(:user)
643
644 conn =
645 conn
646 |> assign(:user, user)
647 |> patch("/api/v1/pleroma/accounts/update_avatar", %{img: ""})
648
649 user = User.get_cached_by_id(user.id)
650
651 assert user.avatar == nil
652
653 assert %{"url" => nil} = json_response(conn, 200)
654 end
655
656 test "can set profile banner", %{conn: conn} do
657 user = insert(:user)
658
659 conn =
660 conn
661 |> assign(:user, user)
662 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => @image})
663
664 user = refresh_record(user)
665 assert user.info.banner["type"] == "Image"
666
667 assert %{"url" => _} = json_response(conn, 200)
668 end
669
670 test "can reset profile banner", %{conn: conn} do
671 user = insert(:user)
672
673 conn =
674 conn
675 |> assign(:user, user)
676 |> patch("/api/v1/pleroma/accounts/update_banner", %{"banner" => ""})
677
678 user = refresh_record(user)
679 assert user.info.banner == %{}
680
681 assert %{"url" => nil} = json_response(conn, 200)
682 end
683
684 test "background image can be set", %{conn: conn} do
685 user = insert(:user)
686
687 conn =
688 conn
689 |> assign(:user, user)
690 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => @image})
691
692 user = refresh_record(user)
693 assert user.info.background["type"] == "Image"
694 assert %{"url" => _} = json_response(conn, 200)
695 end
696
697 test "background image can be reset", %{conn: conn} do
698 user = insert(:user)
699
700 conn =
701 conn
702 |> assign(:user, user)
703 |> patch("/api/v1/pleroma/accounts/update_background", %{"img" => ""})
704
705 user = refresh_record(user)
706 assert user.info.background == %{}
707 assert %{"url" => nil} = json_response(conn, 200)
708 end
709
710 test "creates an oauth app", %{conn: conn} do
711 user = insert(:user)
712 app_attrs = build(:oauth_app)
713
714 conn =
715 conn
716 |> assign(:user, user)
717 |> post("/api/v1/apps", %{
718 client_name: app_attrs.client_name,
719 redirect_uris: app_attrs.redirect_uris
720 })
721
722 [app] = Repo.all(App)
723
724 expected = %{
725 "name" => app.client_name,
726 "website" => app.website,
727 "client_id" => app.client_id,
728 "client_secret" => app.client_secret,
729 "id" => app.id |> to_string(),
730 "redirect_uri" => app.redirect_uris,
731 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
732 }
733
734 assert expected == json_response(conn, 200)
735 end
736
737 test "get a status", %{conn: conn} do
738 activity = insert(:note_activity)
739
740 conn =
741 conn
742 |> get("/api/v1/statuses/#{activity.id}")
743
744 assert %{"id" => id} = json_response(conn, 200)
745 assert id == to_string(activity.id)
746 end
747
748 test "get statuses by IDs", %{conn: conn} do
749 %{id: id1} = insert(:note_activity)
750 %{id: id2} = insert(:note_activity)
751
752 query_string = "ids[]=#{id1}&ids[]=#{id2}"
753 conn = get(conn, "/api/v1/statuses/?#{query_string}")
754
755 assert [%{"id" => ^id1}, %{"id" => ^id2}] = json_response(conn, :ok)
756 end
757
758 describe "deleting a status" do
759 test "when you created it", %{conn: conn} do
760 activity = insert(:note_activity)
761 author = User.get_cached_by_ap_id(activity.data["actor"])
762
763 conn =
764 conn
765 |> assign(:user, author)
766 |> delete("/api/v1/statuses/#{activity.id}")
767
768 assert %{} = json_response(conn, 200)
769
770 refute Activity.get_by_id(activity.id)
771 end
772
773 test "when you didn't create it", %{conn: conn} do
774 activity = insert(:note_activity)
775 user = insert(:user)
776
777 conn =
778 conn
779 |> assign(:user, user)
780 |> delete("/api/v1/statuses/#{activity.id}")
781
782 assert %{"error" => _} = json_response(conn, 403)
783
784 assert Activity.get_by_id(activity.id) == activity
785 end
786
787 test "when you're an admin or moderator", %{conn: conn} do
788 activity1 = insert(:note_activity)
789 activity2 = insert(:note_activity)
790 admin = insert(:user, info: %{is_admin: true})
791 moderator = insert(:user, info: %{is_moderator: true})
792
793 res_conn =
794 conn
795 |> assign(:user, admin)
796 |> delete("/api/v1/statuses/#{activity1.id}")
797
798 assert %{} = json_response(res_conn, 200)
799
800 res_conn =
801 conn
802 |> assign(:user, moderator)
803 |> delete("/api/v1/statuses/#{activity2.id}")
804
805 assert %{} = json_response(res_conn, 200)
806
807 refute Activity.get_by_id(activity1.id)
808 refute Activity.get_by_id(activity2.id)
809 end
810 end
811
812 describe "filters" do
813 test "creating a filter", %{conn: conn} do
814 user = insert(:user)
815
816 filter = %Pleroma.Filter{
817 phrase: "knights",
818 context: ["home"]
819 }
820
821 conn =
822 conn
823 |> assign(:user, user)
824 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
825
826 assert response = json_response(conn, 200)
827 assert response["phrase"] == filter.phrase
828 assert response["context"] == filter.context
829 assert response["irreversible"] == false
830 assert response["id"] != nil
831 assert response["id"] != ""
832 end
833
834 test "fetching a list of filters", %{conn: conn} do
835 user = insert(:user)
836
837 query_one = %Pleroma.Filter{
838 user_id: user.id,
839 filter_id: 1,
840 phrase: "knights",
841 context: ["home"]
842 }
843
844 query_two = %Pleroma.Filter{
845 user_id: user.id,
846 filter_id: 2,
847 phrase: "who",
848 context: ["home"]
849 }
850
851 {:ok, filter_one} = Pleroma.Filter.create(query_one)
852 {:ok, filter_two} = Pleroma.Filter.create(query_two)
853
854 response =
855 conn
856 |> assign(:user, user)
857 |> get("/api/v1/filters")
858 |> json_response(200)
859
860 assert response ==
861 render_json(
862 FilterView,
863 "filters.json",
864 filters: [filter_two, filter_one]
865 )
866 end
867
868 test "get a filter", %{conn: conn} do
869 user = insert(:user)
870
871 query = %Pleroma.Filter{
872 user_id: user.id,
873 filter_id: 2,
874 phrase: "knight",
875 context: ["home"]
876 }
877
878 {:ok, filter} = Pleroma.Filter.create(query)
879
880 conn =
881 conn
882 |> assign(:user, user)
883 |> get("/api/v1/filters/#{filter.filter_id}")
884
885 assert _response = json_response(conn, 200)
886 end
887
888 test "update a filter", %{conn: conn} do
889 user = insert(:user)
890
891 query = %Pleroma.Filter{
892 user_id: user.id,
893 filter_id: 2,
894 phrase: "knight",
895 context: ["home"]
896 }
897
898 {:ok, _filter} = Pleroma.Filter.create(query)
899
900 new = %Pleroma.Filter{
901 phrase: "nii",
902 context: ["home"]
903 }
904
905 conn =
906 conn
907 |> assign(:user, user)
908 |> put("/api/v1/filters/#{query.filter_id}", %{
909 phrase: new.phrase,
910 context: new.context
911 })
912
913 assert response = json_response(conn, 200)
914 assert response["phrase"] == new.phrase
915 assert response["context"] == new.context
916 end
917
918 test "delete a filter", %{conn: conn} do
919 user = insert(:user)
920
921 query = %Pleroma.Filter{
922 user_id: user.id,
923 filter_id: 2,
924 phrase: "knight",
925 context: ["home"]
926 }
927
928 {:ok, filter} = Pleroma.Filter.create(query)
929
930 conn =
931 conn
932 |> assign(:user, user)
933 |> delete("/api/v1/filters/#{filter.filter_id}")
934
935 assert response = json_response(conn, 200)
936 assert response == %{}
937 end
938 end
939
940 describe "list timelines" do
941 test "list timeline", %{conn: conn} do
942 user = insert(:user)
943 other_user = insert(:user)
944 {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."})
945 {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
946 {:ok, list} = Pleroma.List.create("name", user)
947 {:ok, list} = Pleroma.List.follow(list, other_user)
948
949 conn =
950 conn
951 |> assign(:user, user)
952 |> get("/api/v1/timelines/list/#{list.id}")
953
954 assert [%{"id" => id}] = json_response(conn, 200)
955
956 assert id == to_string(activity_two.id)
957 end
958
959 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
960 user = insert(:user)
961 other_user = insert(:user)
962 {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
963
964 {:ok, _activity_two} =
965 CommonAPI.post(other_user, %{
966 "status" => "Marisa is cute.",
967 "visibility" => "private"
968 })
969
970 {:ok, list} = Pleroma.List.create("name", user)
971 {:ok, list} = Pleroma.List.follow(list, other_user)
972
973 conn =
974 conn
975 |> assign(:user, user)
976 |> get("/api/v1/timelines/list/#{list.id}")
977
978 assert [%{"id" => id}] = json_response(conn, 200)
979
980 assert id == to_string(activity_one.id)
981 end
982 end
983
984 describe "notifications" do
985 test "list of notifications", %{conn: conn} do
986 user = insert(:user)
987 other_user = insert(:user)
988
989 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
990
991 {:ok, [_notification]} = Notification.create_notifications(activity)
992
993 conn =
994 conn
995 |> assign(:user, user)
996 |> get("/api/v1/notifications")
997
998 expected_response =
999 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1000 user.ap_id
1001 }\">@<span>#{user.nickname}</span></a></span>"
1002
1003 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
1004 assert response == expected_response
1005 end
1006
1007 test "getting a single notification", %{conn: conn} do
1008 user = insert(:user)
1009 other_user = insert(:user)
1010
1011 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1012
1013 {:ok, [notification]} = Notification.create_notifications(activity)
1014
1015 conn =
1016 conn
1017 |> assign(:user, user)
1018 |> get("/api/v1/notifications/#{notification.id}")
1019
1020 expected_response =
1021 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
1022 user.ap_id
1023 }\">@<span>#{user.nickname}</span></a></span>"
1024
1025 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
1026 assert response == expected_response
1027 end
1028
1029 test "dismissing a single notification", %{conn: conn} do
1030 user = insert(:user)
1031 other_user = insert(:user)
1032
1033 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1034
1035 {:ok, [notification]} = Notification.create_notifications(activity)
1036
1037 conn =
1038 conn
1039 |> assign(:user, user)
1040 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
1041
1042 assert %{} = json_response(conn, 200)
1043 end
1044
1045 test "clearing all notifications", %{conn: conn} do
1046 user = insert(:user)
1047 other_user = insert(:user)
1048
1049 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1050
1051 {:ok, [_notification]} = Notification.create_notifications(activity)
1052
1053 conn =
1054 conn
1055 |> assign(:user, user)
1056 |> post("/api/v1/notifications/clear")
1057
1058 assert %{} = json_response(conn, 200)
1059
1060 conn =
1061 build_conn()
1062 |> assign(:user, user)
1063 |> get("/api/v1/notifications")
1064
1065 assert all = json_response(conn, 200)
1066 assert all == []
1067 end
1068
1069 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
1070 user = insert(:user)
1071 other_user = insert(:user)
1072
1073 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1074 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1075 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1076 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1077
1078 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1079 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1080 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1081 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1082
1083 conn =
1084 conn
1085 |> assign(:user, user)
1086
1087 # min_id
1088 conn_res =
1089 conn
1090 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
1091
1092 result = json_response(conn_res, 200)
1093 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1094
1095 # since_id
1096 conn_res =
1097 conn
1098 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
1099
1100 result = json_response(conn_res, 200)
1101 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1102
1103 # max_id
1104 conn_res =
1105 conn
1106 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
1107
1108 result = json_response(conn_res, 200)
1109 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1110 end
1111
1112 test "filters notifications using exclude_types", %{conn: conn} do
1113 user = insert(:user)
1114 other_user = insert(:user)
1115
1116 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
1117 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
1118 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
1119 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
1120 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
1121
1122 mention_notification_id =
1123 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
1124
1125 favorite_notification_id =
1126 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
1127
1128 reblog_notification_id =
1129 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
1130
1131 follow_notification_id =
1132 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
1133
1134 conn =
1135 conn
1136 |> assign(:user, user)
1137
1138 conn_res =
1139 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
1140
1141 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
1142
1143 conn_res =
1144 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
1145
1146 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
1147
1148 conn_res =
1149 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
1150
1151 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
1152
1153 conn_res =
1154 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
1155
1156 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
1157 end
1158
1159 test "destroy multiple", %{conn: conn} do
1160 user = insert(:user)
1161 other_user = insert(:user)
1162
1163 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1164 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
1165 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1166 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
1167
1168 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
1169 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
1170 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
1171 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
1172
1173 conn =
1174 conn
1175 |> assign(:user, user)
1176
1177 conn_res =
1178 conn
1179 |> get("/api/v1/notifications")
1180
1181 result = json_response(conn_res, 200)
1182 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
1183
1184 conn2 =
1185 conn
1186 |> assign(:user, other_user)
1187
1188 conn_res =
1189 conn2
1190 |> get("/api/v1/notifications")
1191
1192 result = json_response(conn_res, 200)
1193 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1194
1195 conn_destroy =
1196 conn
1197 |> delete("/api/v1/notifications/destroy_multiple", %{
1198 "ids" => [notification1_id, notification2_id]
1199 })
1200
1201 assert json_response(conn_destroy, 200) == %{}
1202
1203 conn_res =
1204 conn2
1205 |> get("/api/v1/notifications")
1206
1207 result = json_response(conn_res, 200)
1208 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1209 end
1210
1211 test "doesn't see notifications after muting user with notifications", %{conn: conn} do
1212 user = insert(:user)
1213 user2 = insert(:user)
1214
1215 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1216 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1217
1218 conn = assign(conn, :user, user)
1219
1220 conn = get(conn, "/api/v1/notifications")
1221
1222 assert length(json_response(conn, 200)) == 1
1223
1224 {:ok, user} = User.mute(user, user2)
1225
1226 conn = assign(build_conn(), :user, user)
1227 conn = get(conn, "/api/v1/notifications")
1228
1229 assert json_response(conn, 200) == []
1230 end
1231
1232 test "see notifications after muting user without notifications", %{conn: conn} do
1233 user = insert(:user)
1234 user2 = insert(:user)
1235
1236 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1237 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1238
1239 conn = assign(conn, :user, user)
1240
1241 conn = get(conn, "/api/v1/notifications")
1242
1243 assert length(json_response(conn, 200)) == 1
1244
1245 {:ok, user} = User.mute(user, user2, false)
1246
1247 conn = assign(build_conn(), :user, user)
1248 conn = get(conn, "/api/v1/notifications")
1249
1250 assert length(json_response(conn, 200)) == 1
1251 end
1252
1253 test "see notifications after muting user with notifications and with_muted parameter", %{
1254 conn: conn
1255 } do
1256 user = insert(:user)
1257 user2 = insert(:user)
1258
1259 {:ok, _, _, _} = CommonAPI.follow(user, user2)
1260 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
1261
1262 conn = assign(conn, :user, user)
1263
1264 conn = get(conn, "/api/v1/notifications")
1265
1266 assert length(json_response(conn, 200)) == 1
1267
1268 {:ok, user} = User.mute(user, user2)
1269
1270 conn = assign(build_conn(), :user, user)
1271 conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
1272
1273 assert length(json_response(conn, 200)) == 1
1274 end
1275 end
1276
1277 describe "subscription_notifications" do
1278 setup do
1279 user = insert(:user)
1280 subscriber = insert(:user)
1281
1282 User.subscribe(subscriber, user)
1283
1284 {:ok, %{user: user, subscriber: subscriber}}
1285 end
1286
1287 test "list of notifications", %{conn: conn, user: user, subscriber: subscriber} do
1288 status_text = "Hello"
1289 {:ok, _activity} = CommonAPI.post(user, %{"status" => status_text})
1290
1291 conn =
1292 conn
1293 |> assign(:user, subscriber)
1294 |> get("/api/v1/notifications/subscription")
1295
1296 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
1297 assert response == status_text
1298 end
1299
1300 test "getting a single notification", %{conn: conn, user: user, subscriber: subscriber} do
1301 status_text = "Hello"
1302
1303 {:ok, _activity} = CommonAPI.post(user, %{"status" => status_text})
1304 [notification] = Repo.all(SubscriptionNotification)
1305
1306 conn =
1307 conn
1308 |> assign(:user, subscriber)
1309 |> get("/api/v1/notifications/subscription/#{notification.id}")
1310
1311 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
1312 assert response == status_text
1313 end
1314
1315 test "dismissing a single notification also deletes it", %{
1316 conn: conn,
1317 user: user,
1318 subscriber: subscriber
1319 } do
1320 status_text = "Hello"
1321 {:ok, _activity} = CommonAPI.post(user, %{"status" => status_text})
1322
1323 [notification] = Repo.all(SubscriptionNotification)
1324
1325 conn =
1326 conn
1327 |> assign(:user, subscriber)
1328 |> post("/api/v1/notifications/subscription/dismiss", %{"id" => notification.id})
1329
1330 assert %{} = json_response(conn, 200)
1331
1332 assert Repo.all(SubscriptionNotification) == []
1333 end
1334
1335 test "clearing all notifications also deletes them", %{
1336 conn: conn,
1337 user: user,
1338 subscriber: subscriber
1339 } do
1340 status_text1 = "Hello"
1341 status_text2 = "Hello again"
1342 {:ok, _activity1} = CommonAPI.post(user, %{"status" => status_text1})
1343 {:ok, _activity2} = CommonAPI.post(user, %{"status" => status_text2})
1344
1345 conn =
1346 conn
1347 |> assign(:user, subscriber)
1348 |> post("/api/v1/notifications/subscription/clear")
1349
1350 assert %{} = json_response(conn, 200)
1351
1352 conn =
1353 build_conn()
1354 |> assign(:user, subscriber)
1355 |> get("/api/v1/notifications/subscription")
1356
1357 assert json_response(conn, 200) == []
1358
1359 assert Repo.all(SubscriptionNotification) == []
1360 end
1361
1362 test "paginates notifications using min_id, since_id, max_id, and limit", %{
1363 conn: conn,
1364 user: user,
1365 subscriber: subscriber
1366 } do
1367 {:ok, activity1} = CommonAPI.post(user, %{"status" => "Hello 1"})
1368 {:ok, activity2} = CommonAPI.post(user, %{"status" => "Hello 2"})
1369 {:ok, activity3} = CommonAPI.post(user, %{"status" => "Hello 3"})
1370 {:ok, activity4} = CommonAPI.post(user, %{"status" => "Hello 4"})
1371
1372 notification1_id =
1373 Repo.get_by(SubscriptionNotification, activity_id: activity1.id).id |> to_string()
1374
1375 notification2_id =
1376 Repo.get_by(SubscriptionNotification, activity_id: activity2.id).id |> to_string()
1377
1378 notification3_id =
1379 Repo.get_by(SubscriptionNotification, activity_id: activity3.id).id |> to_string()
1380
1381 notification4_id =
1382 Repo.get_by(SubscriptionNotification, activity_id: activity4.id).id |> to_string()
1383
1384 conn = assign(conn, :user, subscriber)
1385
1386 # min_id
1387 conn_res =
1388 get(conn, "/api/v1/notifications/subscription?limit=2&min_id=#{notification1_id}")
1389
1390 result = json_response(conn_res, 200)
1391 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1392
1393 # since_id
1394 conn_res =
1395 get(conn, "/api/v1/notifications/subscription?limit=2&since_id=#{notification1_id}")
1396
1397 result = json_response(conn_res, 200)
1398 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
1399
1400 # max_id
1401 conn_res =
1402 get(conn, "/api/v1/notifications/subscription?limit=2&max_id=#{notification4_id}")
1403
1404 result = json_response(conn_res, 200)
1405 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
1406 end
1407
1408 test "destroy multiple", %{conn: conn, user: user1, subscriber: user2} do
1409 # mutual subscription
1410 User.subscribe(user1, user2)
1411
1412 {:ok, activity1} = CommonAPI.post(user1, %{"status" => "Hello 1"})
1413 {:ok, activity2} = CommonAPI.post(user1, %{"status" => "World 1"})
1414 {:ok, activity3} = CommonAPI.post(user2, %{"status" => "Hello 2"})
1415 {:ok, activity4} = CommonAPI.post(user2, %{"status" => "World 2"})
1416
1417 notification1_id =
1418 Repo.get_by(SubscriptionNotification, activity_id: activity1.id).id |> to_string()
1419
1420 notification2_id =
1421 Repo.get_by(SubscriptionNotification, activity_id: activity2.id).id |> to_string()
1422
1423 notification3_id =
1424 Repo.get_by(SubscriptionNotification, activity_id: activity3.id).id |> to_string()
1425
1426 notification4_id =
1427 Repo.get_by(SubscriptionNotification, activity_id: activity4.id).id |> to_string()
1428
1429 conn = assign(conn, :user, user1)
1430
1431 conn_res = get(conn, "/api/v1/notifications/subscription")
1432
1433 result = json_response(conn_res, 200)
1434
1435 Enum.each(result, fn %{"id" => id} ->
1436 assert id in [notification3_id, notification4_id]
1437 end)
1438
1439 conn2 = assign(conn, :user, user2)
1440
1441 conn_res = get(conn2, "/api/v1/notifications/subscription")
1442
1443 result = json_response(conn_res, 200)
1444
1445 Enum.each(result, fn %{"id" => id} ->
1446 assert id in [notification1_id, notification2_id]
1447 end)
1448
1449 conn_destroy =
1450 delete(conn, "/api/v1/notifications/subscription/destroy_multiple", %{
1451 "ids" => [notification3_id, notification4_id]
1452 })
1453
1454 assert json_response(conn_destroy, 200) == %{}
1455
1456 conn_res = get(conn2, "/api/v1/notifications/subscription")
1457
1458 result = json_response(conn_res, 200)
1459
1460 Enum.each(result, fn %{"id" => id} ->
1461 assert id in [notification1_id, notification2_id]
1462 end)
1463
1464 assert length(Repo.all(SubscriptionNotification)) == 2
1465 end
1466 end
1467
1468 describe "reblogging" do
1469 test "reblogs and returns the reblogged status", %{conn: conn} do
1470 activity = insert(:note_activity)
1471 user = insert(:user)
1472
1473 conn =
1474 conn
1475 |> assign(:user, user)
1476 |> post("/api/v1/statuses/#{activity.id}/reblog")
1477
1478 assert %{
1479 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1},
1480 "reblogged" => true
1481 } = json_response(conn, 200)
1482
1483 assert to_string(activity.id) == id
1484 end
1485
1486 test "reblogged status for another user", %{conn: conn} do
1487 activity = insert(:note_activity)
1488 user1 = insert(:user)
1489 user2 = insert(:user)
1490 user3 = insert(:user)
1491 CommonAPI.favorite(activity.id, user2)
1492 {:ok, _bookmark} = Pleroma.Bookmark.create(user2.id, activity.id)
1493 {:ok, reblog_activity1, _object} = CommonAPI.repeat(activity.id, user1)
1494 {:ok, _, _object} = CommonAPI.repeat(activity.id, user2)
1495
1496 conn_res =
1497 conn
1498 |> assign(:user, user3)
1499 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1500
1501 assert %{
1502 "reblog" => %{"id" => id, "reblogged" => false, "reblogs_count" => 2},
1503 "reblogged" => false,
1504 "favourited" => false,
1505 "bookmarked" => false
1506 } = json_response(conn_res, 200)
1507
1508 conn_res =
1509 conn
1510 |> assign(:user, user2)
1511 |> get("/api/v1/statuses/#{reblog_activity1.id}")
1512
1513 assert %{
1514 "reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 2},
1515 "reblogged" => true,
1516 "favourited" => true,
1517 "bookmarked" => true
1518 } = json_response(conn_res, 200)
1519
1520 assert to_string(activity.id) == id
1521 end
1522
1523 test "returns 400 error when activity is not exist", %{conn: conn} do
1524 user = insert(:user)
1525
1526 conn =
1527 conn
1528 |> assign(:user, user)
1529 |> post("/api/v1/statuses/foo/reblog")
1530
1531 assert json_response(conn, 400) == %{"error" => "Could not repeat"}
1532 end
1533 end
1534
1535 describe "unreblogging" do
1536 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1537 activity = insert(:note_activity)
1538 user = insert(:user)
1539
1540 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1541
1542 conn =
1543 conn
1544 |> assign(:user, user)
1545 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1546
1547 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1548
1549 assert to_string(activity.id) == id
1550 end
1551
1552 test "returns 400 error when activity is not exist", %{conn: conn} do
1553 user = insert(:user)
1554
1555 conn =
1556 conn
1557 |> assign(:user, user)
1558 |> post("/api/v1/statuses/foo/unreblog")
1559
1560 assert json_response(conn, 400) == %{"error" => "Could not unrepeat"}
1561 end
1562 end
1563
1564 describe "favoriting" do
1565 test "favs a status and returns it", %{conn: conn} do
1566 activity = insert(:note_activity)
1567 user = insert(:user)
1568
1569 conn =
1570 conn
1571 |> assign(:user, user)
1572 |> post("/api/v1/statuses/#{activity.id}/favourite")
1573
1574 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1575 json_response(conn, 200)
1576
1577 assert to_string(activity.id) == id
1578 end
1579
1580 test "returns 400 error for a wrong id", %{conn: conn} do
1581 user = insert(:user)
1582
1583 conn =
1584 conn
1585 |> assign(:user, user)
1586 |> post("/api/v1/statuses/1/favourite")
1587
1588 assert json_response(conn, 400) == %{"error" => "Could not favorite"}
1589 end
1590 end
1591
1592 describe "unfavoriting" do
1593 test "unfavorites a status and returns it", %{conn: conn} do
1594 activity = insert(:note_activity)
1595 user = insert(:user)
1596
1597 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1598
1599 conn =
1600 conn
1601 |> assign(:user, user)
1602 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1603
1604 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1605 json_response(conn, 200)
1606
1607 assert to_string(activity.id) == id
1608 end
1609
1610 test "returns 400 error for a wrong id", %{conn: conn} do
1611 user = insert(:user)
1612
1613 conn =
1614 conn
1615 |> assign(:user, user)
1616 |> post("/api/v1/statuses/1/unfavourite")
1617
1618 assert json_response(conn, 400) == %{"error" => "Could not unfavorite"}
1619 end
1620 end
1621
1622 describe "user timelines" do
1623 test "gets a users statuses", %{conn: conn} do
1624 user_one = insert(:user)
1625 user_two = insert(:user)
1626 user_three = insert(:user)
1627
1628 {:ok, user_three} = User.follow(user_three, user_one)
1629
1630 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1631
1632 {:ok, direct_activity} =
1633 CommonAPI.post(user_one, %{
1634 "status" => "Hi, @#{user_two.nickname}.",
1635 "visibility" => "direct"
1636 })
1637
1638 {:ok, private_activity} =
1639 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1640
1641 resp =
1642 conn
1643 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1644
1645 assert [%{"id" => id}] = json_response(resp, 200)
1646 assert id == to_string(activity.id)
1647
1648 resp =
1649 conn
1650 |> assign(:user, user_two)
1651 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1652
1653 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1654 assert id_one == to_string(direct_activity.id)
1655 assert id_two == to_string(activity.id)
1656
1657 resp =
1658 conn
1659 |> assign(:user, user_three)
1660 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1661
1662 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1663 assert id_one == to_string(private_activity.id)
1664 assert id_two == to_string(activity.id)
1665 end
1666
1667 test "unimplemented pinned statuses feature", %{conn: conn} do
1668 note = insert(:note_activity)
1669 user = User.get_cached_by_ap_id(note.data["actor"])
1670
1671 conn =
1672 conn
1673 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1674
1675 assert json_response(conn, 200) == []
1676 end
1677
1678 test "gets an users media", %{conn: conn} do
1679 note = insert(:note_activity)
1680 user = User.get_cached_by_ap_id(note.data["actor"])
1681
1682 file = %Plug.Upload{
1683 content_type: "image/jpg",
1684 path: Path.absname("test/fixtures/image.jpg"),
1685 filename: "an_image.jpg"
1686 }
1687
1688 {:ok, %{id: media_id}} = ActivityPub.upload(file, actor: user.ap_id)
1689
1690 {:ok, image_post} = CommonAPI.post(user, %{"status" => "cofe", "media_ids" => [media_id]})
1691
1692 conn =
1693 conn
1694 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1695
1696 assert [%{"id" => id}] = json_response(conn, 200)
1697 assert id == to_string(image_post.id)
1698
1699 conn =
1700 build_conn()
1701 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1702
1703 assert [%{"id" => id}] = json_response(conn, 200)
1704 assert id == to_string(image_post.id)
1705 end
1706
1707 test "gets a user's statuses without reblogs", %{conn: conn} do
1708 user = insert(:user)
1709 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1710 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1711
1712 conn =
1713 conn
1714 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1715
1716 assert [%{"id" => id}] = json_response(conn, 200)
1717 assert id == to_string(post.id)
1718
1719 conn =
1720 conn
1721 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1722
1723 assert [%{"id" => id}] = json_response(conn, 200)
1724 assert id == to_string(post.id)
1725 end
1726
1727 test "filters user's statuses by a hashtag", %{conn: conn} do
1728 user = insert(:user)
1729 {:ok, post} = CommonAPI.post(user, %{"status" => "#hashtag"})
1730 {:ok, _post} = CommonAPI.post(user, %{"status" => "hashtag"})
1731
1732 conn =
1733 conn
1734 |> get("/api/v1/accounts/#{user.id}/statuses", %{"tagged" => "hashtag"})
1735
1736 assert [%{"id" => id}] = json_response(conn, 200)
1737 assert id == to_string(post.id)
1738 end
1739 end
1740
1741 describe "user relationships" do
1742 test "returns the relationships for the current user", %{conn: conn} do
1743 user = insert(:user)
1744 other_user = insert(:user)
1745 {:ok, user} = User.follow(user, other_user)
1746
1747 conn =
1748 conn
1749 |> assign(:user, user)
1750 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1751
1752 assert [relationship] = json_response(conn, 200)
1753
1754 assert to_string(other_user.id) == relationship["id"]
1755 end
1756 end
1757
1758 describe "media upload" do
1759 setup do
1760 user = insert(:user)
1761
1762 conn =
1763 build_conn()
1764 |> assign(:user, user)
1765
1766 image = %Plug.Upload{
1767 content_type: "image/jpg",
1768 path: Path.absname("test/fixtures/image.jpg"),
1769 filename: "an_image.jpg"
1770 }
1771
1772 [conn: conn, image: image]
1773 end
1774
1775 clear_config([:media_proxy])
1776 clear_config([Pleroma.Upload])
1777
1778 test "returns uploaded image", %{conn: conn, image: image} do
1779 desc = "Description of the image"
1780
1781 media =
1782 conn
1783 |> post("/api/v1/media", %{"file" => image, "description" => desc})
1784 |> json_response(:ok)
1785
1786 assert media["type"] == "image"
1787 assert media["description"] == desc
1788 assert media["id"]
1789
1790 object = Repo.get(Object, media["id"])
1791 assert object.data["actor"] == User.ap_id(conn.assigns[:user])
1792 end
1793 end
1794
1795 describe "locked accounts" do
1796 test "/api/v1/follow_requests works" do
1797 user = insert(:user, %{info: %User.Info{locked: true}})
1798 other_user = insert(:user)
1799
1800 {:ok, _activity} = ActivityPub.follow(other_user, user)
1801
1802 user = User.get_cached_by_id(user.id)
1803 other_user = User.get_cached_by_id(other_user.id)
1804
1805 assert User.following?(other_user, user) == false
1806
1807 conn =
1808 build_conn()
1809 |> assign(:user, user)
1810 |> get("/api/v1/follow_requests")
1811
1812 assert [relationship] = json_response(conn, 200)
1813 assert to_string(other_user.id) == relationship["id"]
1814 end
1815
1816 test "/api/v1/follow_requests/:id/authorize works" do
1817 user = insert(:user, %{info: %User.Info{locked: true}})
1818 other_user = insert(:user)
1819
1820 {:ok, _activity} = ActivityPub.follow(other_user, user)
1821
1822 user = User.get_cached_by_id(user.id)
1823 other_user = User.get_cached_by_id(other_user.id)
1824
1825 assert User.following?(other_user, user) == false
1826
1827 conn =
1828 build_conn()
1829 |> assign(:user, user)
1830 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1831
1832 assert relationship = json_response(conn, 200)
1833 assert to_string(other_user.id) == relationship["id"]
1834
1835 user = User.get_cached_by_id(user.id)
1836 other_user = User.get_cached_by_id(other_user.id)
1837
1838 assert User.following?(other_user, user) == true
1839 end
1840
1841 test "verify_credentials", %{conn: conn} do
1842 user = insert(:user, %{info: %User.Info{default_scope: "private"}})
1843
1844 conn =
1845 conn
1846 |> assign(:user, user)
1847 |> get("/api/v1/accounts/verify_credentials")
1848
1849 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1850 assert id == to_string(user.id)
1851 end
1852
1853 test "/api/v1/follow_requests/:id/reject works" do
1854 user = insert(:user, %{info: %User.Info{locked: true}})
1855 other_user = insert(:user)
1856
1857 {:ok, _activity} = ActivityPub.follow(other_user, user)
1858
1859 user = User.get_cached_by_id(user.id)
1860
1861 conn =
1862 build_conn()
1863 |> assign(:user, user)
1864 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1865
1866 assert relationship = json_response(conn, 200)
1867 assert to_string(other_user.id) == relationship["id"]
1868
1869 user = User.get_cached_by_id(user.id)
1870 other_user = User.get_cached_by_id(other_user.id)
1871
1872 assert User.following?(other_user, user) == false
1873 end
1874 end
1875
1876 describe "account fetching" do
1877 test "works by id" do
1878 user = insert(:user)
1879
1880 conn =
1881 build_conn()
1882 |> get("/api/v1/accounts/#{user.id}")
1883
1884 assert %{"id" => id} = json_response(conn, 200)
1885 assert id == to_string(user.id)
1886
1887 conn =
1888 build_conn()
1889 |> get("/api/v1/accounts/-1")
1890
1891 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1892 end
1893
1894 test "works by nickname" do
1895 user = insert(:user)
1896
1897 conn =
1898 build_conn()
1899 |> get("/api/v1/accounts/#{user.nickname}")
1900
1901 assert %{"id" => id} = json_response(conn, 200)
1902 assert id == user.id
1903 end
1904
1905 test "works by nickname for remote users" do
1906 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1907 Pleroma.Config.put([:instance, :limit_to_local_content], false)
1908 user = insert(:user, nickname: "user@example.com", local: false)
1909
1910 conn =
1911 build_conn()
1912 |> get("/api/v1/accounts/#{user.nickname}")
1913
1914 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1915 assert %{"id" => id} = json_response(conn, 200)
1916 assert id == user.id
1917 end
1918
1919 test "respects limit_to_local_content == :all for remote user nicknames" do
1920 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1921 Pleroma.Config.put([:instance, :limit_to_local_content], :all)
1922
1923 user = insert(:user, nickname: "user@example.com", local: false)
1924
1925 conn =
1926 build_conn()
1927 |> get("/api/v1/accounts/#{user.nickname}")
1928
1929 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1930 assert json_response(conn, 404)
1931 end
1932
1933 test "respects limit_to_local_content == :unauthenticated for remote user nicknames" do
1934 limit_to_local = Pleroma.Config.get([:instance, :limit_to_local_content])
1935 Pleroma.Config.put([:instance, :limit_to_local_content], :unauthenticated)
1936
1937 user = insert(:user, nickname: "user@example.com", local: false)
1938 reading_user = insert(:user)
1939
1940 conn =
1941 build_conn()
1942 |> get("/api/v1/accounts/#{user.nickname}")
1943
1944 assert json_response(conn, 404)
1945
1946 conn =
1947 build_conn()
1948 |> assign(:user, reading_user)
1949 |> get("/api/v1/accounts/#{user.nickname}")
1950
1951 Pleroma.Config.put([:instance, :limit_to_local_content], limit_to_local)
1952 assert %{"id" => id} = json_response(conn, 200)
1953 assert id == user.id
1954 end
1955 end
1956
1957 test "mascot upload", %{conn: conn} do
1958 user = insert(:user)
1959
1960 non_image_file = %Plug.Upload{
1961 content_type: "audio/mpeg",
1962 path: Path.absname("test/fixtures/sound.mp3"),
1963 filename: "sound.mp3"
1964 }
1965
1966 conn =
1967 conn
1968 |> assign(:user, user)
1969 |> put("/api/v1/pleroma/mascot", %{"file" => non_image_file})
1970
1971 assert json_response(conn, 415)
1972
1973 file = %Plug.Upload{
1974 content_type: "image/jpg",
1975 path: Path.absname("test/fixtures/image.jpg"),
1976 filename: "an_image.jpg"
1977 }
1978
1979 conn =
1980 build_conn()
1981 |> assign(:user, user)
1982 |> put("/api/v1/pleroma/mascot", %{"file" => file})
1983
1984 assert %{"id" => _, "type" => image} = json_response(conn, 200)
1985 end
1986
1987 test "mascot retrieving", %{conn: conn} do
1988 user = insert(:user)
1989 # When user hasn't set a mascot, we should just get pleroma tan back
1990 conn =
1991 conn
1992 |> assign(:user, user)
1993 |> get("/api/v1/pleroma/mascot")
1994
1995 assert %{"url" => url} = json_response(conn, 200)
1996 assert url =~ "pleroma-fox-tan-smol"
1997
1998 # When a user sets their mascot, we should get that back
1999 file = %Plug.Upload{
2000 content_type: "image/jpg",
2001 path: Path.absname("test/fixtures/image.jpg"),
2002 filename: "an_image.jpg"
2003 }
2004
2005 conn =
2006 build_conn()
2007 |> assign(:user, user)
2008 |> put("/api/v1/pleroma/mascot", %{"file" => file})
2009
2010 assert json_response(conn, 200)
2011
2012 user = User.get_cached_by_id(user.id)
2013
2014 conn =
2015 build_conn()
2016 |> assign(:user, user)
2017 |> get("/api/v1/pleroma/mascot")
2018
2019 assert %{"url" => url, "type" => "image"} = json_response(conn, 200)
2020 assert url =~ "an_image"
2021 end
2022
2023 test "hashtag timeline", %{conn: conn} do
2024 following = insert(:user)
2025
2026 capture_log(fn ->
2027 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
2028
2029 {:ok, [_activity]} =
2030 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
2031
2032 nconn =
2033 conn
2034 |> get("/api/v1/timelines/tag/2hu")
2035
2036 assert [%{"id" => id}] = json_response(nconn, 200)
2037
2038 assert id == to_string(activity.id)
2039
2040 # works for different capitalization too
2041 nconn =
2042 conn
2043 |> get("/api/v1/timelines/tag/2HU")
2044
2045 assert [%{"id" => id}] = json_response(nconn, 200)
2046
2047 assert id == to_string(activity.id)
2048 end)
2049 end
2050
2051 test "multi-hashtag timeline", %{conn: conn} do
2052 user = insert(:user)
2053
2054 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
2055 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
2056 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
2057
2058 any_test =
2059 conn
2060 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
2061
2062 [status_none, status_test1, status_test] = json_response(any_test, 200)
2063
2064 assert to_string(activity_test.id) == status_test["id"]
2065 assert to_string(activity_test1.id) == status_test1["id"]
2066 assert to_string(activity_none.id) == status_none["id"]
2067
2068 restricted_test =
2069 conn
2070 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
2071
2072 assert [status_test1] == json_response(restricted_test, 200)
2073
2074 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
2075
2076 assert [status_none] == json_response(all_test, 200)
2077 end
2078
2079 test "getting followers", %{conn: conn} do
2080 user = insert(:user)
2081 other_user = insert(:user)
2082 {:ok, user} = User.follow(user, other_user)
2083
2084 conn =
2085 conn
2086 |> get("/api/v1/accounts/#{other_user.id}/followers")
2087
2088 assert [%{"id" => id}] = json_response(conn, 200)
2089 assert id == to_string(user.id)
2090 end
2091
2092 test "getting followers, hide_followers", %{conn: conn} do
2093 user = insert(:user)
2094 other_user = insert(:user, %{info: %{hide_followers: true}})
2095 {:ok, _user} = User.follow(user, other_user)
2096
2097 conn =
2098 conn
2099 |> get("/api/v1/accounts/#{other_user.id}/followers")
2100
2101 assert [] == json_response(conn, 200)
2102 end
2103
2104 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
2105 user = insert(:user)
2106 other_user = insert(:user, %{info: %{hide_followers: true}})
2107 {:ok, _user} = User.follow(user, other_user)
2108
2109 conn =
2110 conn
2111 |> assign(:user, other_user)
2112 |> get("/api/v1/accounts/#{other_user.id}/followers")
2113
2114 refute [] == json_response(conn, 200)
2115 end
2116
2117 test "getting followers, pagination", %{conn: conn} do
2118 user = insert(:user)
2119 follower1 = insert(:user)
2120 follower2 = insert(:user)
2121 follower3 = insert(:user)
2122 {:ok, _} = User.follow(follower1, user)
2123 {:ok, _} = User.follow(follower2, user)
2124 {:ok, _} = User.follow(follower3, user)
2125
2126 conn =
2127 conn
2128 |> assign(:user, user)
2129
2130 res_conn =
2131 conn
2132 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
2133
2134 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2135 assert id3 == follower3.id
2136 assert id2 == follower2.id
2137
2138 res_conn =
2139 conn
2140 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
2141
2142 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2143 assert id2 == follower2.id
2144 assert id1 == follower1.id
2145
2146 res_conn =
2147 conn
2148 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
2149
2150 assert [%{"id" => id2}] = json_response(res_conn, 200)
2151 assert id2 == follower2.id
2152
2153 assert [link_header] = get_resp_header(res_conn, "link")
2154 assert link_header =~ ~r/min_id=#{follower2.id}/
2155 assert link_header =~ ~r/max_id=#{follower2.id}/
2156 end
2157
2158 test "getting following", %{conn: conn} do
2159 user = insert(:user)
2160 other_user = insert(:user)
2161 {:ok, user} = User.follow(user, other_user)
2162
2163 conn =
2164 conn
2165 |> get("/api/v1/accounts/#{user.id}/following")
2166
2167 assert [%{"id" => id}] = json_response(conn, 200)
2168 assert id == to_string(other_user.id)
2169 end
2170
2171 test "getting following, hide_follows", %{conn: conn} do
2172 user = insert(:user, %{info: %{hide_follows: true}})
2173 other_user = insert(:user)
2174 {:ok, user} = User.follow(user, other_user)
2175
2176 conn =
2177 conn
2178 |> get("/api/v1/accounts/#{user.id}/following")
2179
2180 assert [] == json_response(conn, 200)
2181 end
2182
2183 test "getting following, hide_follows, same user requesting", %{conn: conn} do
2184 user = insert(:user, %{info: %{hide_follows: true}})
2185 other_user = insert(:user)
2186 {:ok, user} = User.follow(user, other_user)
2187
2188 conn =
2189 conn
2190 |> assign(:user, user)
2191 |> get("/api/v1/accounts/#{user.id}/following")
2192
2193 refute [] == json_response(conn, 200)
2194 end
2195
2196 test "getting following, pagination", %{conn: conn} do
2197 user = insert(:user)
2198 following1 = insert(:user)
2199 following2 = insert(:user)
2200 following3 = insert(:user)
2201 {:ok, _} = User.follow(user, following1)
2202 {:ok, _} = User.follow(user, following2)
2203 {:ok, _} = User.follow(user, following3)
2204
2205 conn =
2206 conn
2207 |> assign(:user, user)
2208
2209 res_conn =
2210 conn
2211 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
2212
2213 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
2214 assert id3 == following3.id
2215 assert id2 == following2.id
2216
2217 res_conn =
2218 conn
2219 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
2220
2221 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
2222 assert id2 == following2.id
2223 assert id1 == following1.id
2224
2225 res_conn =
2226 conn
2227 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
2228
2229 assert [%{"id" => id2}] = json_response(res_conn, 200)
2230 assert id2 == following2.id
2231
2232 assert [link_header] = get_resp_header(res_conn, "link")
2233 assert link_header =~ ~r/min_id=#{following2.id}/
2234 assert link_header =~ ~r/max_id=#{following2.id}/
2235 end
2236
2237 test "following / unfollowing a user", %{conn: conn} do
2238 user = insert(:user)
2239 other_user = insert(:user)
2240
2241 conn =
2242 conn
2243 |> assign(:user, user)
2244 |> post("/api/v1/accounts/#{other_user.id}/follow")
2245
2246 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
2247
2248 user = User.get_cached_by_id(user.id)
2249
2250 conn =
2251 build_conn()
2252 |> assign(:user, user)
2253 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
2254
2255 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
2256
2257 user = User.get_cached_by_id(user.id)
2258
2259 conn =
2260 build_conn()
2261 |> assign(:user, user)
2262 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
2263
2264 assert %{"id" => id} = json_response(conn, 200)
2265 assert id == to_string(other_user.id)
2266 end
2267
2268 test "following without reblogs" do
2269 follower = insert(:user)
2270 followed = insert(:user)
2271 other_user = insert(:user)
2272
2273 conn =
2274 build_conn()
2275 |> assign(:user, follower)
2276 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=false")
2277
2278 assert %{"showing_reblogs" => false} = json_response(conn, 200)
2279
2280 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hey"})
2281 {:ok, reblog, _} = CommonAPI.repeat(activity.id, followed)
2282
2283 conn =
2284 build_conn()
2285 |> assign(:user, User.get_cached_by_id(follower.id))
2286 |> get("/api/v1/timelines/home")
2287
2288 assert [] == json_response(conn, 200)
2289
2290 conn =
2291 build_conn()
2292 |> assign(:user, follower)
2293 |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
2294
2295 assert %{"showing_reblogs" => true} = json_response(conn, 200)
2296
2297 conn =
2298 build_conn()
2299 |> assign(:user, User.get_cached_by_id(follower.id))
2300 |> get("/api/v1/timelines/home")
2301
2302 expected_activity_id = reblog.id
2303 assert [%{"id" => ^expected_activity_id}] = json_response(conn, 200)
2304 end
2305
2306 test "following / unfollowing errors" do
2307 user = insert(:user)
2308
2309 conn =
2310 build_conn()
2311 |> assign(:user, user)
2312
2313 # self follow
2314 conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow")
2315 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2316
2317 # self unfollow
2318 user = User.get_cached_by_id(user.id)
2319 conn_res = post(conn, "/api/v1/accounts/#{user.id}/unfollow")
2320 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2321
2322 # self follow via uri
2323 user = User.get_cached_by_id(user.id)
2324 conn_res = post(conn, "/api/v1/follows", %{"uri" => user.nickname})
2325 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2326
2327 # follow non existing user
2328 conn_res = post(conn, "/api/v1/accounts/doesntexist/follow")
2329 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2330
2331 # follow non existing user via uri
2332 conn_res = post(conn, "/api/v1/follows", %{"uri" => "doesntexist"})
2333 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2334
2335 # unfollow non existing user
2336 conn_res = post(conn, "/api/v1/accounts/doesntexist/unfollow")
2337 assert %{"error" => "Record not found"} = json_response(conn_res, 404)
2338 end
2339
2340 describe "mute/unmute" do
2341 test "with notifications", %{conn: conn} do
2342 user = insert(:user)
2343 other_user = insert(:user)
2344
2345 conn =
2346 conn
2347 |> assign(:user, user)
2348 |> post("/api/v1/accounts/#{other_user.id}/mute")
2349
2350 response = json_response(conn, 200)
2351
2352 assert %{"id" => _id, "muting" => true, "muting_notifications" => true} = response
2353 user = User.get_cached_by_id(user.id)
2354
2355 conn =
2356 build_conn()
2357 |> assign(:user, user)
2358 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2359
2360 response = json_response(conn, 200)
2361 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2362 end
2363
2364 test "without notifications", %{conn: conn} do
2365 user = insert(:user)
2366 other_user = insert(:user)
2367
2368 conn =
2369 conn
2370 |> assign(:user, user)
2371 |> post("/api/v1/accounts/#{other_user.id}/mute", %{"notifications" => "false"})
2372
2373 response = json_response(conn, 200)
2374
2375 assert %{"id" => _id, "muting" => true, "muting_notifications" => false} = response
2376 user = User.get_cached_by_id(user.id)
2377
2378 conn =
2379 build_conn()
2380 |> assign(:user, user)
2381 |> post("/api/v1/accounts/#{other_user.id}/unmute")
2382
2383 response = json_response(conn, 200)
2384 assert %{"id" => _id, "muting" => false, "muting_notifications" => false} = response
2385 end
2386 end
2387
2388 test "subscribing / unsubscribing to a user", %{conn: conn} do
2389 user = insert(:user)
2390 subscription_target = insert(:user)
2391
2392 conn =
2393 conn
2394 |> assign(:user, user)
2395 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/subscribe")
2396
2397 assert %{"id" => _id, "subscribing" => true} = json_response(conn, 200)
2398
2399 conn =
2400 build_conn()
2401 |> assign(:user, user)
2402 |> post("/api/v1/pleroma/accounts/#{subscription_target.id}/unsubscribe")
2403
2404 assert %{"id" => _id, "subscribing" => false} = json_response(conn, 200)
2405 end
2406
2407 test "getting a list of mutes", %{conn: conn} do
2408 user = insert(:user)
2409 other_user = insert(:user)
2410
2411 {:ok, user} = User.mute(user, other_user)
2412
2413 conn =
2414 conn
2415 |> assign(:user, user)
2416 |> get("/api/v1/mutes")
2417
2418 other_user_id = to_string(other_user.id)
2419 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2420 end
2421
2422 test "blocking / unblocking a user", %{conn: conn} do
2423 user = insert(:user)
2424 other_user = insert(:user)
2425
2426 conn =
2427 conn
2428 |> assign(:user, user)
2429 |> post("/api/v1/accounts/#{other_user.id}/block")
2430
2431 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
2432
2433 user = User.get_cached_by_id(user.id)
2434
2435 conn =
2436 build_conn()
2437 |> assign(:user, user)
2438 |> post("/api/v1/accounts/#{other_user.id}/unblock")
2439
2440 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
2441 end
2442
2443 test "getting a list of blocks", %{conn: conn} do
2444 user = insert(:user)
2445 other_user = insert(:user)
2446
2447 {:ok, user} = User.block(user, other_user)
2448
2449 conn =
2450 conn
2451 |> assign(:user, user)
2452 |> get("/api/v1/blocks")
2453
2454 other_user_id = to_string(other_user.id)
2455 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
2456 end
2457
2458 test "blocking / unblocking a domain", %{conn: conn} do
2459 user = insert(:user)
2460 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
2461
2462 conn =
2463 conn
2464 |> assign(:user, user)
2465 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2466
2467 assert %{} = json_response(conn, 200)
2468 user = User.get_cached_by_ap_id(user.ap_id)
2469 assert User.blocks?(user, other_user)
2470
2471 conn =
2472 build_conn()
2473 |> assign(:user, user)
2474 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
2475
2476 assert %{} = json_response(conn, 200)
2477 user = User.get_cached_by_ap_id(user.ap_id)
2478 refute User.blocks?(user, other_user)
2479 end
2480
2481 test "getting a list of domain blocks", %{conn: conn} do
2482 user = insert(:user)
2483
2484 {:ok, user} = User.block_domain(user, "bad.site")
2485 {:ok, user} = User.block_domain(user, "even.worse.site")
2486
2487 conn =
2488 conn
2489 |> assign(:user, user)
2490 |> get("/api/v1/domain_blocks")
2491
2492 domain_blocks = json_response(conn, 200)
2493
2494 assert "bad.site" in domain_blocks
2495 assert "even.worse.site" in domain_blocks
2496 end
2497
2498 test "unimplemented follow_requests, blocks, domain blocks" do
2499 user = insert(:user)
2500
2501 ["blocks", "domain_blocks", "follow_requests"]
2502 |> Enum.each(fn endpoint ->
2503 conn =
2504 build_conn()
2505 |> assign(:user, user)
2506 |> get("/api/v1/#{endpoint}")
2507
2508 assert [] = json_response(conn, 200)
2509 end)
2510 end
2511
2512 test "returns the favorites of a user", %{conn: conn} do
2513 user = insert(:user)
2514 other_user = insert(:user)
2515
2516 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
2517 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
2518
2519 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
2520
2521 first_conn =
2522 conn
2523 |> assign(:user, user)
2524 |> get("/api/v1/favourites")
2525
2526 assert [status] = json_response(first_conn, 200)
2527 assert status["id"] == to_string(activity.id)
2528
2529 assert [{"link", _link_header}] =
2530 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
2531
2532 # Honours query params
2533 {:ok, second_activity} =
2534 CommonAPI.post(other_user, %{
2535 "status" =>
2536 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
2537 })
2538
2539 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
2540
2541 last_like = status["id"]
2542
2543 second_conn =
2544 conn
2545 |> assign(:user, user)
2546 |> get("/api/v1/favourites?since_id=#{last_like}")
2547
2548 assert [second_status] = json_response(second_conn, 200)
2549 assert second_status["id"] == to_string(second_activity.id)
2550
2551 third_conn =
2552 conn
2553 |> assign(:user, user)
2554 |> get("/api/v1/favourites?limit=0")
2555
2556 assert [] = json_response(third_conn, 200)
2557 end
2558
2559 describe "getting favorites timeline of specified user" do
2560 setup do
2561 [current_user, user] = insert_pair(:user, %{info: %{hide_favorites: false}})
2562 [current_user: current_user, user: user]
2563 end
2564
2565 test "returns list of statuses favorited by specified user", %{
2566 conn: conn,
2567 current_user: current_user,
2568 user: user
2569 } do
2570 [activity | _] = insert_pair(:note_activity)
2571 CommonAPI.favorite(activity.id, user)
2572
2573 response =
2574 conn
2575 |> assign(:user, current_user)
2576 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2577 |> json_response(:ok)
2578
2579 [like] = response
2580
2581 assert length(response) == 1
2582 assert like["id"] == activity.id
2583 end
2584
2585 test "returns favorites for specified user_id when user is not logged in", %{
2586 conn: conn,
2587 user: user
2588 } do
2589 activity = insert(:note_activity)
2590 CommonAPI.favorite(activity.id, user)
2591
2592 response =
2593 conn
2594 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2595 |> json_response(:ok)
2596
2597 assert length(response) == 1
2598 end
2599
2600 test "returns favorited DM only when user is logged in and he is one of recipients", %{
2601 conn: conn,
2602 current_user: current_user,
2603 user: user
2604 } do
2605 {:ok, direct} =
2606 CommonAPI.post(current_user, %{
2607 "status" => "Hi @#{user.nickname}!",
2608 "visibility" => "direct"
2609 })
2610
2611 CommonAPI.favorite(direct.id, user)
2612
2613 response =
2614 conn
2615 |> assign(:user, current_user)
2616 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2617 |> json_response(:ok)
2618
2619 assert length(response) == 1
2620
2621 anonymous_response =
2622 conn
2623 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2624 |> json_response(:ok)
2625
2626 assert Enum.empty?(anonymous_response)
2627 end
2628
2629 test "does not return others' favorited DM when user is not one of recipients", %{
2630 conn: conn,
2631 current_user: current_user,
2632 user: user
2633 } do
2634 user_two = insert(:user)
2635
2636 {:ok, direct} =
2637 CommonAPI.post(user_two, %{
2638 "status" => "Hi @#{user.nickname}!",
2639 "visibility" => "direct"
2640 })
2641
2642 CommonAPI.favorite(direct.id, user)
2643
2644 response =
2645 conn
2646 |> assign(:user, current_user)
2647 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2648 |> json_response(:ok)
2649
2650 assert Enum.empty?(response)
2651 end
2652
2653 test "paginates favorites using since_id and max_id", %{
2654 conn: conn,
2655 current_user: current_user,
2656 user: user
2657 } do
2658 activities = insert_list(10, :note_activity)
2659
2660 Enum.each(activities, fn activity ->
2661 CommonAPI.favorite(activity.id, user)
2662 end)
2663
2664 third_activity = Enum.at(activities, 2)
2665 seventh_activity = Enum.at(activities, 6)
2666
2667 response =
2668 conn
2669 |> assign(:user, current_user)
2670 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{
2671 since_id: third_activity.id,
2672 max_id: seventh_activity.id
2673 })
2674 |> json_response(:ok)
2675
2676 assert length(response) == 3
2677 refute third_activity in response
2678 refute seventh_activity in response
2679 end
2680
2681 test "limits favorites using limit parameter", %{
2682 conn: conn,
2683 current_user: current_user,
2684 user: user
2685 } do
2686 7
2687 |> insert_list(:note_activity)
2688 |> Enum.each(fn activity ->
2689 CommonAPI.favorite(activity.id, user)
2690 end)
2691
2692 response =
2693 conn
2694 |> assign(:user, current_user)
2695 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites", %{limit: "3"})
2696 |> json_response(:ok)
2697
2698 assert length(response) == 3
2699 end
2700
2701 test "returns empty response when user does not have any favorited statuses", %{
2702 conn: conn,
2703 current_user: current_user,
2704 user: user
2705 } do
2706 response =
2707 conn
2708 |> assign(:user, current_user)
2709 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2710 |> json_response(:ok)
2711
2712 assert Enum.empty?(response)
2713 end
2714
2715 test "returns 404 error when specified user is not exist", %{conn: conn} do
2716 conn = get(conn, "/api/v1/pleroma/accounts/test/favourites")
2717
2718 assert json_response(conn, 404) == %{"error" => "Record not found"}
2719 end
2720
2721 test "returns 403 error when user has hidden own favorites", %{
2722 conn: conn,
2723 current_user: current_user
2724 } do
2725 user = insert(:user, %{info: %{hide_favorites: true}})
2726 activity = insert(:note_activity)
2727 CommonAPI.favorite(activity.id, user)
2728
2729 conn =
2730 conn
2731 |> assign(:user, current_user)
2732 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2733
2734 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2735 end
2736
2737 test "hides favorites for new users by default", %{conn: conn, current_user: current_user} do
2738 user = insert(:user)
2739 activity = insert(:note_activity)
2740 CommonAPI.favorite(activity.id, user)
2741
2742 conn =
2743 conn
2744 |> assign(:user, current_user)
2745 |> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
2746
2747 assert user.info.hide_favorites
2748 assert json_response(conn, 403) == %{"error" => "Can't get favorites"}
2749 end
2750 end
2751
2752 test "get instance information", %{conn: conn} do
2753 conn = get(conn, "/api/v1/instance")
2754 assert result = json_response(conn, 200)
2755
2756 email = Config.get([:instance, :email])
2757 # Note: not checking for "max_toot_chars" since it's optional
2758 assert %{
2759 "uri" => _,
2760 "title" => _,
2761 "description" => _,
2762 "version" => _,
2763 "email" => from_config_email,
2764 "urls" => %{
2765 "streaming_api" => _
2766 },
2767 "stats" => _,
2768 "thumbnail" => _,
2769 "languages" => _,
2770 "registrations" => _,
2771 "poll_limits" => _
2772 } = result
2773
2774 assert email == from_config_email
2775 end
2776
2777 test "get instance stats", %{conn: conn} do
2778 user = insert(:user, %{local: true})
2779
2780 user2 = insert(:user, %{local: true})
2781 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
2782
2783 insert(:user, %{local: false, nickname: "u@peer1.com"})
2784 insert(:user, %{local: false, nickname: "u@peer2.com"})
2785
2786 {:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
2787
2788 # Stats should count users with missing or nil `info.deactivated` value
2789 user = User.get_cached_by_id(user.id)
2790 info_change = Changeset.change(user.info, %{deactivated: nil})
2791
2792 {:ok, _user} =
2793 user
2794 |> Changeset.change()
2795 |> Changeset.put_embed(:info, info_change)
2796 |> User.update_and_set_cache()
2797
2798 Pleroma.Stats.force_update()
2799
2800 conn = get(conn, "/api/v1/instance")
2801
2802 assert result = json_response(conn, 200)
2803
2804 stats = result["stats"]
2805
2806 assert stats
2807 assert stats["user_count"] == 1
2808 assert stats["status_count"] == 1
2809 assert stats["domain_count"] == 2
2810 end
2811
2812 test "get peers", %{conn: conn} do
2813 insert(:user, %{local: false, nickname: "u@peer1.com"})
2814 insert(:user, %{local: false, nickname: "u@peer2.com"})
2815
2816 Pleroma.Stats.force_update()
2817
2818 conn = get(conn, "/api/v1/instance/peers")
2819
2820 assert result = json_response(conn, 200)
2821
2822 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2823 end
2824
2825 test "put settings", %{conn: conn} do
2826 user = insert(:user)
2827
2828 conn =
2829 conn
2830 |> assign(:user, user)
2831 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2832
2833 assert _result = json_response(conn, 200)
2834
2835 user = User.get_cached_by_ap_id(user.ap_id)
2836 assert user.info.settings == %{"programming" => "socks"}
2837 end
2838
2839 describe "pinned statuses" do
2840 setup do
2841 user = insert(:user)
2842 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2843
2844 [user: user, activity: activity]
2845 end
2846
2847 clear_config([:instance, :max_pinned_statuses]) do
2848 Config.put([:instance, :max_pinned_statuses], 1)
2849 end
2850
2851 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2852 {:ok, _} = CommonAPI.pin(activity.id, user)
2853
2854 result =
2855 conn
2856 |> assign(:user, user)
2857 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2858 |> json_response(200)
2859
2860 id_str = to_string(activity.id)
2861
2862 assert [%{"id" => ^id_str, "pinned" => true}] = result
2863 end
2864
2865 test "pin status", %{conn: conn, user: user, activity: activity} do
2866 id_str = to_string(activity.id)
2867
2868 assert %{"id" => ^id_str, "pinned" => true} =
2869 conn
2870 |> assign(:user, user)
2871 |> post("/api/v1/statuses/#{activity.id}/pin")
2872 |> json_response(200)
2873
2874 assert [%{"id" => ^id_str, "pinned" => true}] =
2875 conn
2876 |> assign(:user, user)
2877 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2878 |> json_response(200)
2879 end
2880
2881 test "/pin: returns 400 error when activity is not public", %{conn: conn, user: user} do
2882 {:ok, dm} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
2883
2884 conn =
2885 conn
2886 |> assign(:user, user)
2887 |> post("/api/v1/statuses/#{dm.id}/pin")
2888
2889 assert json_response(conn, 400) == %{"error" => "Could not pin"}
2890 end
2891
2892 test "unpin status", %{conn: conn, user: user, activity: activity} do
2893 {:ok, _} = CommonAPI.pin(activity.id, user)
2894
2895 id_str = to_string(activity.id)
2896 user = refresh_record(user)
2897
2898 assert %{"id" => ^id_str, "pinned" => false} =
2899 conn
2900 |> assign(:user, user)
2901 |> post("/api/v1/statuses/#{activity.id}/unpin")
2902 |> json_response(200)
2903
2904 assert [] =
2905 conn
2906 |> assign(:user, user)
2907 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2908 |> json_response(200)
2909 end
2910
2911 test "/unpin: returns 400 error when activity is not exist", %{conn: conn, user: user} do
2912 conn =
2913 conn
2914 |> assign(:user, user)
2915 |> post("/api/v1/statuses/1/unpin")
2916
2917 assert json_response(conn, 400) == %{"error" => "Could not unpin"}
2918 end
2919
2920 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2921 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2922
2923 id_str_one = to_string(activity_one.id)
2924
2925 assert %{"id" => ^id_str_one, "pinned" => true} =
2926 conn
2927 |> assign(:user, user)
2928 |> post("/api/v1/statuses/#{id_str_one}/pin")
2929 |> json_response(200)
2930
2931 user = refresh_record(user)
2932
2933 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2934 conn
2935 |> assign(:user, user)
2936 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2937 |> json_response(400)
2938 end
2939 end
2940
2941 describe "cards" do
2942 setup do
2943 Config.put([:rich_media, :enabled], true)
2944
2945 user = insert(:user)
2946 %{user: user}
2947 end
2948
2949 test "returns rich-media card", %{conn: conn, user: user} do
2950 {:ok, activity} = CommonAPI.post(user, %{"status" => "https://example.com/ogp"})
2951
2952 card_data = %{
2953 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2954 "provider_name" => "example.com",
2955 "provider_url" => "https://example.com",
2956 "title" => "The Rock",
2957 "type" => "link",
2958 "url" => "https://example.com/ogp",
2959 "description" =>
2960 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
2961 "pleroma" => %{
2962 "opengraph" => %{
2963 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2964 "title" => "The Rock",
2965 "type" => "video.movie",
2966 "url" => "https://example.com/ogp",
2967 "description" =>
2968 "Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer."
2969 }
2970 }
2971 }
2972
2973 response =
2974 conn
2975 |> get("/api/v1/statuses/#{activity.id}/card")
2976 |> json_response(200)
2977
2978 assert response == card_data
2979
2980 # works with private posts
2981 {:ok, activity} =
2982 CommonAPI.post(user, %{"status" => "https://example.com/ogp", "visibility" => "direct"})
2983
2984 response_two =
2985 conn
2986 |> assign(:user, user)
2987 |> get("/api/v1/statuses/#{activity.id}/card")
2988 |> json_response(200)
2989
2990 assert response_two == card_data
2991 end
2992
2993 test "replaces missing description with an empty string", %{conn: conn, user: user} do
2994 {:ok, activity} =
2995 CommonAPI.post(user, %{"status" => "https://example.com/ogp-missing-data"})
2996
2997 response =
2998 conn
2999 |> get("/api/v1/statuses/#{activity.id}/card")
3000 |> json_response(:ok)
3001
3002 assert response == %{
3003 "type" => "link",
3004 "title" => "Pleroma",
3005 "description" => "",
3006 "image" => nil,
3007 "provider_name" => "example.com",
3008 "provider_url" => "https://example.com",
3009 "url" => "https://example.com/ogp-missing-data",
3010 "pleroma" => %{
3011 "opengraph" => %{
3012 "title" => "Pleroma",
3013 "type" => "website",
3014 "url" => "https://example.com/ogp-missing-data"
3015 }
3016 }
3017 }
3018 end
3019 end
3020
3021 test "bookmarks" do
3022 user = insert(:user)
3023 for_user = insert(:user)
3024
3025 {:ok, activity1} =
3026 CommonAPI.post(user, %{
3027 "status" => "heweoo?"
3028 })
3029
3030 {:ok, activity2} =
3031 CommonAPI.post(user, %{
3032 "status" => "heweoo!"
3033 })
3034
3035 response1 =
3036 build_conn()
3037 |> assign(:user, for_user)
3038 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
3039
3040 assert json_response(response1, 200)["bookmarked"] == true
3041
3042 response2 =
3043 build_conn()
3044 |> assign(:user, for_user)
3045 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
3046
3047 assert json_response(response2, 200)["bookmarked"] == true
3048
3049 bookmarks =
3050 build_conn()
3051 |> assign(:user, for_user)
3052 |> get("/api/v1/bookmarks")
3053
3054 assert [json_response(response2, 200), json_response(response1, 200)] ==
3055 json_response(bookmarks, 200)
3056
3057 response1 =
3058 build_conn()
3059 |> assign(:user, for_user)
3060 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
3061
3062 assert json_response(response1, 200)["bookmarked"] == false
3063
3064 bookmarks =
3065 build_conn()
3066 |> assign(:user, for_user)
3067 |> get("/api/v1/bookmarks")
3068
3069 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
3070 end
3071
3072 describe "conversation muting" do
3073 setup do
3074 post_user = insert(:user)
3075 user = insert(:user)
3076
3077 {:ok, activity} = CommonAPI.post(post_user, %{"status" => "HIE"})
3078
3079 [user: user, activity: activity]
3080 end
3081
3082 test "mute conversation", %{conn: conn, user: user, activity: activity} do
3083 id_str = to_string(activity.id)
3084
3085 assert %{"id" => ^id_str, "muted" => true} =
3086 conn
3087 |> assign(:user, user)
3088 |> post("/api/v1/statuses/#{activity.id}/mute")
3089 |> json_response(200)
3090 end
3091
3092 test "cannot mute already muted conversation", %{conn: conn, user: user, activity: activity} do
3093 {:ok, _} = CommonAPI.add_mute(user, activity)
3094
3095 conn =
3096 conn
3097 |> assign(:user, user)
3098 |> post("/api/v1/statuses/#{activity.id}/mute")
3099
3100 assert json_response(conn, 400) == %{"error" => "conversation is already muted"}
3101 end
3102
3103 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
3104 {:ok, _} = CommonAPI.add_mute(user, activity)
3105
3106 id_str = to_string(activity.id)
3107 user = refresh_record(user)
3108
3109 assert %{"id" => ^id_str, "muted" => false} =
3110 conn
3111 |> assign(:user, user)
3112 |> post("/api/v1/statuses/#{activity.id}/unmute")
3113 |> json_response(200)
3114 end
3115 end
3116
3117 describe "reports" do
3118 setup do
3119 reporter = insert(:user)
3120 target_user = insert(:user)
3121
3122 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
3123
3124 [reporter: reporter, target_user: target_user, activity: activity]
3125 end
3126
3127 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
3128 assert %{"action_taken" => false, "id" => _} =
3129 conn
3130 |> assign(:user, reporter)
3131 |> post("/api/v1/reports", %{"account_id" => target_user.id})
3132 |> json_response(200)
3133 end
3134
3135 test "submit a report with statuses and comment", %{
3136 conn: conn,
3137 reporter: reporter,
3138 target_user: target_user,
3139 activity: activity
3140 } do
3141 assert %{"action_taken" => false, "id" => _} =
3142 conn
3143 |> assign(:user, reporter)
3144 |> post("/api/v1/reports", %{
3145 "account_id" => target_user.id,
3146 "status_ids" => [activity.id],
3147 "comment" => "bad status!",
3148 "forward" => "false"
3149 })
3150 |> json_response(200)
3151 end
3152
3153 test "account_id is required", %{
3154 conn: conn,
3155 reporter: reporter,
3156 activity: activity
3157 } do
3158 assert %{"error" => "Valid `account_id` required"} =
3159 conn
3160 |> assign(:user, reporter)
3161 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
3162 |> json_response(400)
3163 end
3164
3165 test "comment must be up to the size specified in the config", %{
3166 conn: conn,
3167 reporter: reporter,
3168 target_user: target_user
3169 } do
3170 max_size = Config.get([:instance, :max_report_comment_size], 1000)
3171 comment = String.pad_trailing("a", max_size + 1, "a")
3172
3173 error = %{"error" => "Comment must be up to #{max_size} characters"}
3174
3175 assert ^error =
3176 conn
3177 |> assign(:user, reporter)
3178 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
3179 |> json_response(400)
3180 end
3181
3182 test "returns error when account is not exist", %{
3183 conn: conn,
3184 reporter: reporter,
3185 activity: activity
3186 } do
3187 conn =
3188 conn
3189 |> assign(:user, reporter)
3190 |> post("/api/v1/reports", %{"status_ids" => [activity.id], "account_id" => "foo"})
3191
3192 assert json_response(conn, 400) == %{"error" => "Account not found"}
3193 end
3194 end
3195
3196 describe "link headers" do
3197 test "preserves parameters in link headers", %{conn: conn} do
3198 user = insert(:user)
3199 other_user = insert(:user)
3200
3201 {:ok, activity1} =
3202 CommonAPI.post(other_user, %{
3203 "status" => "hi @#{user.nickname}",
3204 "visibility" => "public"
3205 })
3206
3207 {:ok, activity2} =
3208 CommonAPI.post(other_user, %{
3209 "status" => "hi @#{user.nickname}",
3210 "visibility" => "public"
3211 })
3212
3213 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
3214 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
3215
3216 conn =
3217 conn
3218 |> assign(:user, user)
3219 |> get("/api/v1/notifications", %{media_only: true})
3220
3221 assert [link_header] = get_resp_header(conn, "link")
3222 assert link_header =~ ~r/media_only=true/
3223 assert link_header =~ ~r/min_id=#{notification2.id}/
3224 assert link_header =~ ~r/max_id=#{notification1.id}/
3225 end
3226 end
3227
3228 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
3229 # Need to set an old-style integer ID to reproduce the problem
3230 # (these are no longer assigned to new accounts but were preserved
3231 # for existing accounts during the migration to flakeIDs)
3232 user_one = insert(:user, %{id: 1212})
3233 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
3234
3235 resp_one =
3236 conn
3237 |> get("/api/v1/accounts/#{user_one.id}")
3238
3239 resp_two =
3240 conn
3241 |> get("/api/v1/accounts/#{user_two.nickname}")
3242
3243 resp_three =
3244 conn
3245 |> get("/api/v1/accounts/#{user_two.id}")
3246
3247 acc_one = json_response(resp_one, 200)
3248 acc_two = json_response(resp_two, 200)
3249 acc_three = json_response(resp_three, 200)
3250 refute acc_one == acc_two
3251 assert acc_two == acc_three
3252 end
3253
3254 describe "custom emoji" do
3255 test "with tags", %{conn: conn} do
3256 [emoji | _body] =
3257 conn
3258 |> get("/api/v1/custom_emojis")
3259 |> json_response(200)
3260
3261 assert Map.has_key?(emoji, "shortcode")
3262 assert Map.has_key?(emoji, "static_url")
3263 assert Map.has_key?(emoji, "tags")
3264 assert is_list(emoji["tags"])
3265 assert Map.has_key?(emoji, "category")
3266 assert Map.has_key?(emoji, "url")
3267 assert Map.has_key?(emoji, "visible_in_picker")
3268 end
3269 end
3270
3271 describe "index/2 redirections" do
3272 setup %{conn: conn} do
3273 session_opts = [
3274 store: :cookie,
3275 key: "_test",
3276 signing_salt: "cooldude"
3277 ]
3278
3279 conn =
3280 conn
3281 |> Plug.Session.call(Plug.Session.init(session_opts))
3282 |> fetch_session()
3283
3284 test_path = "/web/statuses/test"
3285 %{conn: conn, path: test_path}
3286 end
3287
3288 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
3289 conn = get(conn, path)
3290
3291 assert conn.status == 302
3292 assert redirected_to(conn) == "/web/login"
3293 end
3294
3295 test "redirects not logged-in users to the login page on private instances", %{
3296 conn: conn,
3297 path: path
3298 } do
3299 Config.put([:instance, :public], false)
3300
3301 conn = get(conn, path)
3302
3303 assert conn.status == 302
3304 assert redirected_to(conn) == "/web/login"
3305 end
3306
3307 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
3308 token = insert(:oauth_token)
3309
3310 conn =
3311 conn
3312 |> assign(:user, token.user)
3313 |> put_session(:oauth_token, token.token)
3314 |> get(path)
3315
3316 assert conn.status == 200
3317 end
3318
3319 test "saves referer path to session", %{conn: conn, path: path} do
3320 conn = get(conn, path)
3321 return_to = Plug.Conn.get_session(conn, :return_to)
3322
3323 assert return_to == path
3324 end
3325
3326 test "redirects to the saved path after log in", %{conn: conn, path: path} do
3327 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3328 auth = insert(:oauth_authorization, app: app)
3329
3330 conn =
3331 conn
3332 |> put_session(:return_to, path)
3333 |> get("/web/login", %{code: auth.token})
3334
3335 assert conn.status == 302
3336 assert redirected_to(conn) == path
3337 end
3338
3339 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
3340 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
3341 auth = insert(:oauth_authorization, app: app)
3342
3343 conn = get(conn, "/web/login", %{code: auth.token})
3344
3345 assert conn.status == 302
3346 assert redirected_to(conn) == "/web/getting-started"
3347 end
3348 end
3349
3350 describe "scheduled activities" do
3351 test "creates a scheduled activity", %{conn: conn} do
3352 user = insert(:user)
3353 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3354
3355 conn =
3356 conn
3357 |> assign(:user, user)
3358 |> post("/api/v1/statuses", %{
3359 "status" => "scheduled",
3360 "scheduled_at" => scheduled_at
3361 })
3362
3363 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
3364 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
3365 assert [] == Repo.all(Activity)
3366 end
3367
3368 test "creates a scheduled activity with a media attachment", %{conn: conn} do
3369 user = insert(:user)
3370 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3371
3372 file = %Plug.Upload{
3373 content_type: "image/jpg",
3374 path: Path.absname("test/fixtures/image.jpg"),
3375 filename: "an_image.jpg"
3376 }
3377
3378 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
3379
3380 conn =
3381 conn
3382 |> assign(:user, user)
3383 |> post("/api/v1/statuses", %{
3384 "media_ids" => [to_string(upload.id)],
3385 "status" => "scheduled",
3386 "scheduled_at" => scheduled_at
3387 })
3388
3389 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
3390 assert %{"type" => "image"} = media_attachment
3391 end
3392
3393 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
3394 %{conn: conn} do
3395 user = insert(:user)
3396
3397 scheduled_at =
3398 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
3399
3400 conn =
3401 conn
3402 |> assign(:user, user)
3403 |> post("/api/v1/statuses", %{
3404 "status" => "not scheduled",
3405 "scheduled_at" => scheduled_at
3406 })
3407
3408 assert %{"content" => "not scheduled"} = json_response(conn, 200)
3409 assert [] == Repo.all(ScheduledActivity)
3410 end
3411
3412 test "returns error when daily user limit is exceeded", %{conn: conn} do
3413 user = insert(:user)
3414
3415 today =
3416 NaiveDateTime.utc_now()
3417 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3418 |> NaiveDateTime.to_iso8601()
3419
3420 attrs = %{params: %{}, scheduled_at: today}
3421 {:ok, _} = ScheduledActivity.create(user, attrs)
3422 {:ok, _} = ScheduledActivity.create(user, attrs)
3423
3424 conn =
3425 conn
3426 |> assign(:user, user)
3427 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
3428
3429 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
3430 end
3431
3432 test "returns error when total user limit is exceeded", %{conn: conn} do
3433 user = insert(:user)
3434
3435 today =
3436 NaiveDateTime.utc_now()
3437 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
3438 |> NaiveDateTime.to_iso8601()
3439
3440 tomorrow =
3441 NaiveDateTime.utc_now()
3442 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
3443 |> NaiveDateTime.to_iso8601()
3444
3445 attrs = %{params: %{}, scheduled_at: today}
3446 {:ok, _} = ScheduledActivity.create(user, attrs)
3447 {:ok, _} = ScheduledActivity.create(user, attrs)
3448 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
3449
3450 conn =
3451 conn
3452 |> assign(:user, user)
3453 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
3454
3455 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
3456 end
3457
3458 test "shows scheduled activities", %{conn: conn} do
3459 user = insert(:user)
3460 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
3461 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
3462 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
3463 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
3464
3465 conn =
3466 conn
3467 |> assign(:user, user)
3468
3469 # min_id
3470 conn_res =
3471 conn
3472 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
3473
3474 result = json_response(conn_res, 200)
3475 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3476
3477 # since_id
3478 conn_res =
3479 conn
3480 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
3481
3482 result = json_response(conn_res, 200)
3483 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
3484
3485 # max_id
3486 conn_res =
3487 conn
3488 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
3489
3490 result = json_response(conn_res, 200)
3491 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
3492 end
3493
3494 test "shows a scheduled activity", %{conn: conn} do
3495 user = insert(:user)
3496 scheduled_activity = insert(:scheduled_activity, user: user)
3497
3498 res_conn =
3499 conn
3500 |> assign(:user, user)
3501 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3502
3503 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
3504 assert scheduled_activity_id == scheduled_activity.id |> to_string()
3505
3506 res_conn =
3507 conn
3508 |> assign(:user, user)
3509 |> get("/api/v1/scheduled_statuses/404")
3510
3511 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3512 end
3513
3514 test "updates a scheduled activity", %{conn: conn} do
3515 user = insert(:user)
3516 scheduled_activity = insert(:scheduled_activity, user: user)
3517
3518 new_scheduled_at =
3519 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
3520
3521 res_conn =
3522 conn
3523 |> assign(:user, user)
3524 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
3525 scheduled_at: new_scheduled_at
3526 })
3527
3528 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
3529 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
3530
3531 res_conn =
3532 conn
3533 |> assign(:user, user)
3534 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
3535
3536 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3537 end
3538
3539 test "deletes a scheduled activity", %{conn: conn} do
3540 user = insert(:user)
3541 scheduled_activity = insert(:scheduled_activity, user: user)
3542
3543 res_conn =
3544 conn
3545 |> assign(:user, user)
3546 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3547
3548 assert %{} = json_response(res_conn, 200)
3549 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
3550
3551 res_conn =
3552 conn
3553 |> assign(:user, user)
3554 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
3555
3556 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
3557 end
3558 end
3559
3560 test "Repeated posts that are replies incorrectly have in_reply_to_id null", %{conn: conn} do
3561 user1 = insert(:user)
3562 user2 = insert(:user)
3563 user3 = insert(:user)
3564
3565 {:ok, replied_to} = CommonAPI.post(user1, %{"status" => "cofe"})
3566
3567 # Reply to status from another user
3568 conn1 =
3569 conn
3570 |> assign(:user, user2)
3571 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
3572
3573 assert %{"content" => "xD", "id" => id} = json_response(conn1, 200)
3574
3575 activity = Activity.get_by_id_with_object(id)
3576
3577 assert Object.normalize(activity).data["inReplyTo"] == Object.normalize(replied_to).data["id"]
3578 assert Activity.get_in_reply_to_activity(activity).id == replied_to.id
3579
3580 # Reblog from the third user
3581 conn2 =
3582 conn
3583 |> assign(:user, user3)
3584 |> post("/api/v1/statuses/#{activity.id}/reblog")
3585
3586 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
3587 json_response(conn2, 200)
3588
3589 assert to_string(activity.id) == id
3590
3591 # Getting third user status
3592 conn3 =
3593 conn
3594 |> assign(:user, user3)
3595 |> get("api/v1/timelines/home")
3596
3597 [reblogged_activity] = json_response(conn3, 200)
3598
3599 assert reblogged_activity["reblog"]["in_reply_to_id"] == replied_to.id
3600
3601 replied_to_user = User.get_by_ap_id(replied_to.data["actor"])
3602 assert reblogged_activity["reblog"]["in_reply_to_account_id"] == replied_to_user.id
3603 end
3604
3605 describe "create account by app" do
3606 test "Account registration via Application", %{conn: conn} do
3607 conn =
3608 conn
3609 |> post("/api/v1/apps", %{
3610 client_name: "client_name",
3611 redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
3612 scopes: "read, write, follow"
3613 })
3614
3615 %{
3616 "client_id" => client_id,
3617 "client_secret" => client_secret,
3618 "id" => _,
3619 "name" => "client_name",
3620 "redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
3621 "vapid_key" => _,
3622 "website" => nil
3623 } = json_response(conn, 200)
3624
3625 conn =
3626 conn
3627 |> post("/oauth/token", %{
3628 grant_type: "client_credentials",
3629 client_id: client_id,
3630 client_secret: client_secret
3631 })
3632
3633 assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
3634 json_response(conn, 200)
3635
3636 assert token
3637 token_from_db = Repo.get_by(Token, token: token)
3638 assert token_from_db
3639 assert refresh
3640 assert scope == "read write follow"
3641
3642 conn =
3643 build_conn()
3644 |> put_req_header("authorization", "Bearer " <> token)
3645 |> post("/api/v1/accounts", %{
3646 username: "lain",
3647 email: "lain@example.org",
3648 password: "PlzDontHackLain",
3649 agreement: true
3650 })
3651
3652 %{
3653 "access_token" => token,
3654 "created_at" => _created_at,
3655 "scope" => _scope,
3656 "token_type" => "Bearer"
3657 } = json_response(conn, 200)
3658
3659 token_from_db = Repo.get_by(Token, token: token)
3660 assert token_from_db
3661 token_from_db = Repo.preload(token_from_db, :user)
3662 assert token_from_db.user
3663
3664 assert token_from_db.user.info.confirmation_pending
3665 end
3666
3667 test "rate limit", %{conn: conn} do
3668 app_token = insert(:oauth_token, user: nil)
3669
3670 conn =
3671 put_req_header(conn, "authorization", "Bearer " <> app_token.token)
3672 |> Map.put(:remote_ip, {15, 15, 15, 15})
3673
3674 for i <- 1..5 do
3675 conn =
3676 conn
3677 |> post("/api/v1/accounts", %{
3678 username: "#{i}lain",
3679 email: "#{i}lain@example.org",
3680 password: "PlzDontHackLain",
3681 agreement: true
3682 })
3683
3684 %{
3685 "access_token" => token,
3686 "created_at" => _created_at,
3687 "scope" => _scope,
3688 "token_type" => "Bearer"
3689 } = json_response(conn, 200)
3690
3691 token_from_db = Repo.get_by(Token, token: token)
3692 assert token_from_db
3693 token_from_db = Repo.preload(token_from_db, :user)
3694 assert token_from_db.user
3695
3696 assert token_from_db.user.info.confirmation_pending
3697 end
3698
3699 conn =
3700 conn
3701 |> post("/api/v1/accounts", %{
3702 username: "6lain",
3703 email: "6lain@example.org",
3704 password: "PlzDontHackLain",
3705 agreement: true
3706 })
3707
3708 assert json_response(conn, :too_many_requests) == %{"error" => "Throttled"}
3709 end
3710 end
3711
3712 describe "GET /api/v1/polls/:id" do
3713 test "returns poll entity for object id", %{conn: conn} do
3714 user = insert(:user)
3715
3716 {:ok, activity} =
3717 CommonAPI.post(user, %{
3718 "status" => "Pleroma does",
3719 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20}
3720 })
3721
3722 object = Object.normalize(activity)
3723
3724 conn =
3725 conn
3726 |> assign(:user, user)
3727 |> get("/api/v1/polls/#{object.id}")
3728
3729 response = json_response(conn, 200)
3730 id = to_string(object.id)
3731 assert %{"id" => ^id, "expired" => false, "multiple" => false} = response
3732 end
3733
3734 test "does not expose polls for private statuses", %{conn: conn} do
3735 user = insert(:user)
3736 other_user = insert(:user)
3737
3738 {:ok, activity} =
3739 CommonAPI.post(user, %{
3740 "status" => "Pleroma does",
3741 "poll" => %{"options" => ["what Mastodon't", "n't what Mastodoes"], "expires_in" => 20},
3742 "visibility" => "private"
3743 })
3744
3745 object = Object.normalize(activity)
3746
3747 conn =
3748 conn
3749 |> assign(:user, other_user)
3750 |> get("/api/v1/polls/#{object.id}")
3751
3752 assert json_response(conn, 404)
3753 end
3754 end
3755
3756 describe "POST /api/v1/polls/:id/votes" do
3757 test "votes are added to the poll", %{conn: conn} do
3758 user = insert(:user)
3759 other_user = insert(:user)
3760
3761 {:ok, activity} =
3762 CommonAPI.post(user, %{
3763 "status" => "A very delicious sandwich",
3764 "poll" => %{
3765 "options" => ["Lettuce", "Grilled Bacon", "Tomato"],
3766 "expires_in" => 20,
3767 "multiple" => true
3768 }
3769 })
3770
3771 object = Object.normalize(activity)
3772
3773 conn =
3774 conn
3775 |> assign(:user, other_user)
3776 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1, 2]})
3777
3778 assert json_response(conn, 200)
3779 object = Object.get_by_id(object.id)
3780
3781 assert Enum.all?(object.data["anyOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3782 total_items == 1
3783 end)
3784 end
3785
3786 test "author can't vote", %{conn: conn} do
3787 user = insert(:user)
3788
3789 {:ok, activity} =
3790 CommonAPI.post(user, %{
3791 "status" => "Am I cute?",
3792 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3793 })
3794
3795 object = Object.normalize(activity)
3796
3797 assert conn
3798 |> assign(:user, user)
3799 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [1]})
3800 |> json_response(422) == %{"error" => "Poll's author can't vote"}
3801
3802 object = Object.get_by_id(object.id)
3803
3804 refute Enum.at(object.data["oneOf"], 1)["replies"]["totalItems"] == 1
3805 end
3806
3807 test "does not allow multiple choices on a single-choice question", %{conn: conn} do
3808 user = insert(:user)
3809 other_user = insert(:user)
3810
3811 {:ok, activity} =
3812 CommonAPI.post(user, %{
3813 "status" => "The glass is",
3814 "poll" => %{"options" => ["half empty", "half full"], "expires_in" => 20}
3815 })
3816
3817 object = Object.normalize(activity)
3818
3819 assert conn
3820 |> assign(:user, other_user)
3821 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0, 1]})
3822 |> json_response(422) == %{"error" => "Too many choices"}
3823
3824 object = Object.get_by_id(object.id)
3825
3826 refute Enum.any?(object.data["oneOf"], fn %{"replies" => %{"totalItems" => total_items}} ->
3827 total_items == 1
3828 end)
3829 end
3830
3831 test "does not allow choice index to be greater than options count", %{conn: conn} do
3832 user = insert(:user)
3833 other_user = insert(:user)
3834
3835 {:ok, activity} =
3836 CommonAPI.post(user, %{
3837 "status" => "Am I cute?",
3838 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20}
3839 })
3840
3841 object = Object.normalize(activity)
3842
3843 conn =
3844 conn
3845 |> assign(:user, other_user)
3846 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [2]})
3847
3848 assert json_response(conn, 422) == %{"error" => "Invalid indices"}
3849 end
3850
3851 test "returns 404 error when object is not exist", %{conn: conn} do
3852 user = insert(:user)
3853
3854 conn =
3855 conn
3856 |> assign(:user, user)
3857 |> post("/api/v1/polls/1/votes", %{"choices" => [0]})
3858
3859 assert json_response(conn, 404) == %{"error" => "Record not found"}
3860 end
3861
3862 test "returns 404 when poll is private and not available for user", %{conn: conn} do
3863 user = insert(:user)
3864 other_user = insert(:user)
3865
3866 {:ok, activity} =
3867 CommonAPI.post(user, %{
3868 "status" => "Am I cute?",
3869 "poll" => %{"options" => ["Yes", "No"], "expires_in" => 20},
3870 "visibility" => "private"
3871 })
3872
3873 object = Object.normalize(activity)
3874
3875 conn =
3876 conn
3877 |> assign(:user, other_user)
3878 |> post("/api/v1/polls/#{object.id}/votes", %{"choices" => [0]})
3879
3880 assert json_response(conn, 404) == %{"error" => "Record not found"}
3881 end
3882 end
3883
3884 describe "GET /api/v1/statuses/:id/favourited_by" do
3885 setup do
3886 user = insert(:user)
3887 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3888
3889 conn =
3890 build_conn()
3891 |> assign(:user, user)
3892
3893 [conn: conn, activity: activity]
3894 end
3895
3896 test "returns users who have favorited the status", %{conn: conn, activity: activity} do
3897 other_user = insert(:user)
3898 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3899
3900 response =
3901 conn
3902 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3903 |> json_response(:ok)
3904
3905 [%{"id" => id}] = response
3906
3907 assert id == other_user.id
3908 end
3909
3910 test "returns empty array when status has not been favorited yet", %{
3911 conn: conn,
3912 activity: activity
3913 } do
3914 response =
3915 conn
3916 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3917 |> json_response(:ok)
3918
3919 assert Enum.empty?(response)
3920 end
3921
3922 test "does not return users who have favorited the status but are blocked", %{
3923 conn: %{assigns: %{user: user}} = conn,
3924 activity: activity
3925 } do
3926 other_user = insert(:user)
3927 {:ok, user} = User.block(user, other_user)
3928
3929 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3930
3931 response =
3932 conn
3933 |> assign(:user, user)
3934 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3935 |> json_response(:ok)
3936
3937 assert Enum.empty?(response)
3938 end
3939
3940 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
3941 other_user = insert(:user)
3942 {:ok, _, _} = CommonAPI.favorite(activity.id, other_user)
3943
3944 response =
3945 conn
3946 |> assign(:user, nil)
3947 |> get("/api/v1/statuses/#{activity.id}/favourited_by")
3948 |> json_response(:ok)
3949
3950 [%{"id" => id}] = response
3951 assert id == other_user.id
3952 end
3953 end
3954
3955 describe "GET /api/v1/statuses/:id/reblogged_by" do
3956 setup do
3957 user = insert(:user)
3958 {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
3959
3960 conn =
3961 build_conn()
3962 |> assign(:user, user)
3963
3964 [conn: conn, activity: activity]
3965 end
3966
3967 test "returns users who have reblogged the status", %{conn: conn, activity: activity} do
3968 other_user = insert(:user)
3969 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
3970
3971 response =
3972 conn
3973 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3974 |> json_response(:ok)
3975
3976 [%{"id" => id}] = response
3977
3978 assert id == other_user.id
3979 end
3980
3981 test "returns empty array when status has not been reblogged yet", %{
3982 conn: conn,
3983 activity: activity
3984 } do
3985 response =
3986 conn
3987 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
3988 |> json_response(:ok)
3989
3990 assert Enum.empty?(response)
3991 end
3992
3993 test "does not return users who have reblogged the status but are blocked", %{
3994 conn: %{assigns: %{user: user}} = conn,
3995 activity: activity
3996 } do
3997 other_user = insert(:user)
3998 {:ok, user} = User.block(user, other_user)
3999
4000 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
4001
4002 response =
4003 conn
4004 |> assign(:user, user)
4005 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
4006 |> json_response(:ok)
4007
4008 assert Enum.empty?(response)
4009 end
4010
4011 test "does not fail on an unauthenticated request", %{conn: conn, activity: activity} do
4012 other_user = insert(:user)
4013 {:ok, _, _} = CommonAPI.repeat(activity.id, other_user)
4014
4015 response =
4016 conn
4017 |> assign(:user, nil)
4018 |> get("/api/v1/statuses/#{activity.id}/reblogged_by")
4019 |> json_response(:ok)
4020
4021 [%{"id" => id}] = response
4022 assert id == other_user.id
4023 end
4024 end
4025
4026 describe "POST /auth/password, with valid parameters" do
4027 setup %{conn: conn} do
4028 user = insert(:user)
4029 conn = post(conn, "/auth/password?email=#{user.email}")
4030 %{conn: conn, user: user}
4031 end
4032
4033 test "it returns 204", %{conn: conn} do
4034 assert json_response(conn, :no_content)
4035 end
4036
4037 test "it creates a PasswordResetToken record for user", %{user: user} do
4038 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
4039 assert token_record
4040 end
4041
4042 test "it sends an email to user", %{user: user} do
4043 token_record = Repo.get_by(Pleroma.PasswordResetToken, user_id: user.id)
4044
4045 email = Pleroma.Emails.UserEmail.password_reset_email(user, token_record.token)
4046 notify_email = Config.get([:instance, :notify_email])
4047 instance_name = Config.get([:instance, :name])
4048
4049 assert_email_sent(
4050 from: {instance_name, notify_email},
4051 to: {user.name, user.email},
4052 html_body: email.html_body
4053 )
4054 end
4055 end
4056
4057 describe "POST /auth/password, with invalid parameters" do
4058 setup do
4059 user = insert(:user)
4060 {:ok, user: user}
4061 end
4062
4063 test "it returns 404 when user is not found", %{conn: conn, user: user} do
4064 conn = post(conn, "/auth/password?email=nonexisting_#{user.email}")
4065 assert conn.status == 404
4066 assert conn.resp_body == ""
4067 end
4068
4069 test "it returns 400 when user is not local", %{conn: conn, user: user} do
4070 {:ok, user} = Repo.update(Changeset.change(user, local: false))
4071 conn = post(conn, "/auth/password?email=#{user.email}")
4072 assert conn.status == 400
4073 assert conn.resp_body == ""
4074 end
4075 end
4076
4077 describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
4078 setup do
4079 user = insert(:user)
4080 info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
4081
4082 {:ok, user} =
4083 user
4084 |> Changeset.change()
4085 |> Changeset.put_embed(:info, info_change)
4086 |> Repo.update()
4087
4088 assert user.info.confirmation_pending
4089
4090 [user: user]
4091 end
4092
4093 clear_config([:instance, :account_activation_required]) do
4094 Config.put([:instance, :account_activation_required], true)
4095 end
4096
4097 test "resend account confirmation email", %{conn: conn, user: user} do
4098 conn
4099 |> assign(:user, user)
4100 |> post("/api/v1/pleroma/accounts/confirmation_resend?email=#{user.email}")
4101 |> json_response(:no_content)
4102
4103 email = Pleroma.Emails.UserEmail.account_confirmation_email(user)
4104 notify_email = Config.get([:instance, :notify_email])
4105 instance_name = Config.get([:instance, :name])
4106
4107 assert_email_sent(
4108 from: {instance_name, notify_email},
4109 to: {user.name, user.email},
4110 html_body: email.html_body
4111 )
4112 end
4113 end
4114
4115 describe "GET /api/v1/suggestions" do
4116 setup do
4117 user = insert(:user)
4118 other_user = insert(:user)
4119 host = Config.get([Pleroma.Web.Endpoint, :url, :host])
4120 url500 = "http://test500?#{host}&#{user.nickname}"
4121 url200 = "http://test200?#{host}&#{user.nickname}"
4122
4123 mock(fn
4124 %{method: :get, url: ^url500} ->
4125 %Tesla.Env{status: 500, body: "bad request"}
4126
4127 %{method: :get, url: ^url200} ->
4128 %Tesla.Env{
4129 status: 200,
4130 body:
4131 ~s([{"acct":"yj455","avatar":"https://social.heldscal.la/avatar/201.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/201.jpeg"}, {"acct":"#{
4132 other_user.ap_id
4133 }","avatar":"https://social.heldscal.la/avatar/202.jpeg","avatar_static":"https://social.heldscal.la/avatar/s/202.jpeg"}])
4134 }
4135 end)
4136
4137 [user: user, other_user: other_user]
4138 end
4139
4140 clear_config(:suggestions)
4141
4142 test "returns empty result when suggestions disabled", %{conn: conn, user: user} do
4143 Config.put([:suggestions, :enabled], false)
4144
4145 res =
4146 conn
4147 |> assign(:user, user)
4148 |> get("/api/v1/suggestions")
4149 |> json_response(200)
4150
4151 assert res == []
4152 end
4153
4154 test "returns error", %{conn: conn, user: user} do
4155 Config.put([:suggestions, :enabled], true)
4156 Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}")
4157
4158 res =
4159 conn
4160 |> assign(:user, user)
4161 |> get("/api/v1/suggestions")
4162 |> json_response(500)
4163
4164 assert res == "Something went wrong"
4165 end
4166
4167 test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do
4168 Config.put([:suggestions, :enabled], true)
4169 Config.put([:suggestions, :third_party_engine], "http://test200?{{host}}&{{user}}")
4170
4171 res =
4172 conn
4173 |> assign(:user, user)
4174 |> get("/api/v1/suggestions")
4175 |> json_response(200)
4176
4177 assert res == [
4178 %{
4179 "acct" => "yj455",
4180 "avatar" => "https://social.heldscal.la/avatar/201.jpeg",
4181 "avatar_static" => "https://social.heldscal.la/avatar/s/201.jpeg",
4182 "id" => 0
4183 },
4184 %{
4185 "acct" => other_user.ap_id,
4186 "avatar" => "https://social.heldscal.la/avatar/202.jpeg",
4187 "avatar_static" => "https://social.heldscal.la/avatar/s/202.jpeg",
4188 "id" => other_user.id
4189 }
4190 ]
4191 end
4192 end
4193 end