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