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