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