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