Conversations: Fix specs.
[akkoma] / test / web / mastodon_api / mastodon_api_controller_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
6 use Pleroma.Web.ConnCase
7
8 alias Ecto.Changeset
9 alias Pleroma.Activity
10 alias Pleroma.Notification
11 alias Pleroma.Object
12 alias Pleroma.Repo
13 alias Pleroma.ScheduledActivity
14 alias Pleroma.User
15 alias Pleroma.Web.ActivityPub.ActivityPub
16 alias Pleroma.Web.CommonAPI
17 alias Pleroma.Web.MastodonAPI.FilterView
18 alias Pleroma.Web.OAuth.App
19 alias Pleroma.Web.OStatus
20 alias Pleroma.Web.Push
21 alias Pleroma.Web.TwitterAPI.TwitterAPI
22 import Pleroma.Factory
23 import ExUnit.CaptureLog
24 import Tesla.Mock
25
26 setup do
27 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
28 :ok
29 end
30
31 test "the home timeline", %{conn: conn} do
32 user = insert(:user)
33 following = insert(:user)
34
35 {:ok, _activity} = TwitterAPI.create_status(following, %{"status" => "test"})
36
37 conn =
38 conn
39 |> assign(:user, user)
40 |> get("/api/v1/timelines/home")
41
42 assert Enum.empty?(json_response(conn, 200))
43
44 {:ok, user} = User.follow(user, following)
45
46 conn =
47 build_conn()
48 |> assign(:user, user)
49 |> get("/api/v1/timelines/home")
50
51 assert [%{"content" => "test"}] = json_response(conn, 200)
52 end
53
54 test "the public timeline", %{conn: conn} do
55 following = insert(:user)
56
57 capture_log(fn ->
58 {:ok, _activity} = TwitterAPI.create_status(following, %{"status" => "test"})
59
60 {:ok, [_activity]} =
61 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
62
63 conn =
64 conn
65 |> get("/api/v1/timelines/public", %{"local" => "False"})
66
67 assert length(json_response(conn, 200)) == 2
68
69 conn =
70 build_conn()
71 |> get("/api/v1/timelines/public", %{"local" => "True"})
72
73 assert [%{"content" => "test"}] = json_response(conn, 200)
74
75 conn =
76 build_conn()
77 |> get("/api/v1/timelines/public", %{"local" => "1"})
78
79 assert [%{"content" => "test"}] = json_response(conn, 200)
80 end)
81 end
82
83 test "posting a status", %{conn: conn} do
84 user = insert(:user)
85
86 idempotency_key = "Pikachu rocks!"
87
88 conn_one =
89 conn
90 |> assign(:user, user)
91 |> put_req_header("idempotency-key", idempotency_key)
92 |> post("/api/v1/statuses", %{
93 "status" => "cofe",
94 "spoiler_text" => "2hu",
95 "sensitive" => "false"
96 })
97
98 {:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
99 # Six hours
100 assert ttl > :timer.seconds(6 * 60 * 60 - 1)
101
102 assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
103 json_response(conn_one, 200)
104
105 assert Activity.get_by_id(id)
106
107 conn_two =
108 conn
109 |> assign(:user, user)
110 |> put_req_header("idempotency-key", idempotency_key)
111 |> post("/api/v1/statuses", %{
112 "status" => "cofe",
113 "spoiler_text" => "2hu",
114 "sensitive" => "false"
115 })
116
117 assert %{"id" => second_id} = json_response(conn_two, 200)
118
119 assert id == second_id
120
121 conn_three =
122 conn
123 |> assign(:user, user)
124 |> post("/api/v1/statuses", %{
125 "status" => "cofe",
126 "spoiler_text" => "2hu",
127 "sensitive" => "false"
128 })
129
130 assert %{"id" => third_id} = json_response(conn_three, 200)
131
132 refute id == third_id
133 end
134
135 test "posting a sensitive status", %{conn: conn} do
136 user = insert(:user)
137
138 conn =
139 conn
140 |> assign(:user, user)
141 |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
142
143 assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200)
144 assert Activity.get_by_id(id)
145 end
146
147 test "posting a fake status", %{conn: conn} do
148 user = insert(:user)
149
150 real_conn =
151 conn
152 |> assign(:user, user)
153 |> post("/api/v1/statuses", %{
154 "status" =>
155 "\"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"
156 })
157
158 real_status = json_response(real_conn, 200)
159
160 assert real_status
161 assert Object.get_by_ap_id(real_status["uri"])
162
163 real_status =
164 real_status
165 |> Map.put("id", nil)
166 |> Map.put("url", nil)
167 |> Map.put("uri", nil)
168 |> Map.put("created_at", nil)
169 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
170
171 fake_conn =
172 conn
173 |> assign(:user, user)
174 |> post("/api/v1/statuses", %{
175 "status" =>
176 "\"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",
177 "preview" => true
178 })
179
180 fake_status = json_response(fake_conn, 200)
181
182 assert fake_status
183 refute Object.get_by_ap_id(fake_status["uri"])
184
185 fake_status =
186 fake_status
187 |> Map.put("id", nil)
188 |> Map.put("url", nil)
189 |> Map.put("uri", nil)
190 |> Map.put("created_at", nil)
191 |> Kernel.put_in(["pleroma", "conversation_id"], nil)
192
193 assert real_status == fake_status
194 end
195
196 test "posting a status with OGP link preview", %{conn: conn} do
197 Pleroma.Config.put([:rich_media, :enabled], true)
198 user = insert(:user)
199
200 conn =
201 conn
202 |> assign(:user, user)
203 |> post("/api/v1/statuses", %{
204 "status" => "http://example.com/ogp"
205 })
206
207 assert %{"id" => id, "card" => %{"title" => "The Rock"}} = json_response(conn, 200)
208 assert Activity.get_by_id(id)
209 Pleroma.Config.put([:rich_media, :enabled], false)
210 end
211
212 test "posting a direct status", %{conn: conn} do
213 user1 = insert(:user)
214 user2 = insert(:user)
215 content = "direct cofe @#{user2.nickname}"
216
217 conn =
218 conn
219 |> assign(:user, user1)
220 |> post("api/v1/statuses", %{"status" => content, "visibility" => "direct"})
221
222 assert %{"id" => id, "visibility" => "direct"} = json_response(conn, 200)
223 assert activity = Activity.get_by_id(id)
224 assert activity.recipients == [user2.ap_id, user1.ap_id]
225 assert activity.data["to"] == [user2.ap_id]
226 assert activity.data["cc"] == []
227 end
228
229 test "direct timeline", %{conn: conn} do
230 user_one = insert(:user)
231 user_two = insert(:user)
232
233 {:ok, user_two} = User.follow(user_two, user_one)
234
235 {:ok, direct} =
236 CommonAPI.post(user_one, %{
237 "status" => "Hi @#{user_two.nickname}!",
238 "visibility" => "direct"
239 })
240
241 {:ok, _follower_only} =
242 CommonAPI.post(user_one, %{
243 "status" => "Hi @#{user_two.nickname}!",
244 "visibility" => "private"
245 })
246
247 # Only direct should be visible here
248 res_conn =
249 conn
250 |> assign(:user, user_two)
251 |> get("api/v1/timelines/direct")
252
253 [status] = json_response(res_conn, 200)
254
255 assert %{"visibility" => "direct"} = status
256 assert status["url"] != direct.data["id"]
257
258 # User should be able to see his own direct message
259 res_conn =
260 build_conn()
261 |> assign(:user, user_one)
262 |> get("api/v1/timelines/direct")
263
264 [status] = json_response(res_conn, 200)
265
266 assert %{"visibility" => "direct"} = status
267
268 # Both should be visible here
269 res_conn =
270 conn
271 |> assign(:user, user_two)
272 |> get("api/v1/timelines/home")
273
274 [_s1, _s2] = json_response(res_conn, 200)
275
276 # Test pagination
277 Enum.each(1..20, fn _ ->
278 {:ok, _} =
279 CommonAPI.post(user_one, %{
280 "status" => "Hi @#{user_two.nickname}!",
281 "visibility" => "direct"
282 })
283 end)
284
285 res_conn =
286 conn
287 |> assign(:user, user_two)
288 |> get("api/v1/timelines/direct")
289
290 statuses = json_response(res_conn, 200)
291 assert length(statuses) == 20
292
293 res_conn =
294 conn
295 |> assign(:user, user_two)
296 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
297
298 [status] = json_response(res_conn, 200)
299
300 assert status["url"] != direct.data["id"]
301 end
302
303 test "Conversations", %{conn: conn} do
304 user_one = insert(:user)
305 user_two = insert(:user)
306
307 {:ok, user_two} = User.follow(user_two, user_one)
308
309 {:ok, direct} =
310 CommonAPI.post(user_one, %{
311 "status" => "Hi @#{user_two.nickname}!",
312 "visibility" => "direct"
313 })
314
315 {:ok, _follower_only} =
316 CommonAPI.post(user_one, %{
317 "status" => "Hi @#{user_two.nickname}!",
318 "visibility" => "private"
319 })
320
321 res_conn =
322 conn
323 |> assign(:user, user_one)
324 |> get("/api/v1/conversations")
325
326 assert response = json_response(res_conn, 200)
327
328 assert %{
329 "id" => res_id,
330 "accounts" => res_accounts,
331 "last_status" => res_last_status,
332 "unread" => unread
333 } = response
334
335 assert unread == false
336
337 # Apparently undocumented API endpoint
338 res_conn =
339 conn
340 |> assign(:user, user_one)
341 |> post("/api/v1/conversations/#{res_id}/read")
342
343 assert response == json_response(res_conn, 200)
344
345 # (vanilla) Mastodon frontend behaviour
346 res_conn =
347 conn
348 |> assign(:user, user_one)
349 |> get("/api/v1/statuses/#{res_last_status.id}/context")
350
351 assert %{ancestors: [], descendants: []} == json_response(res_conn, 200)
352 end
353
354 test "doesn't include DMs from blocked users", %{conn: conn} do
355 blocker = insert(:user)
356 blocked = insert(:user)
357 user = insert(:user)
358 {:ok, blocker} = User.block(blocker, blocked)
359
360 {:ok, _blocked_direct} =
361 CommonAPI.post(blocked, %{
362 "status" => "Hi @#{blocker.nickname}!",
363 "visibility" => "direct"
364 })
365
366 {:ok, direct} =
367 CommonAPI.post(user, %{
368 "status" => "Hi @#{blocker.nickname}!",
369 "visibility" => "direct"
370 })
371
372 res_conn =
373 conn
374 |> assign(:user, user)
375 |> get("api/v1/timelines/direct")
376
377 [status] = json_response(res_conn, 200)
378 assert status["id"] == direct.id
379 end
380
381 test "replying to a status", %{conn: conn} do
382 user = insert(:user)
383
384 {:ok, replied_to} = TwitterAPI.create_status(user, %{"status" => "cofe"})
385
386 conn =
387 conn
388 |> assign(:user, user)
389 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
390
391 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
392
393 activity = Activity.get_by_id(id)
394
395 assert activity.data["context"] == replied_to.data["context"]
396 assert activity.data["object"]["inReplyToStatusId"] == replied_to.id
397 end
398
399 test "posting a status with an invalid in_reply_to_id", %{conn: conn} do
400 user = insert(:user)
401
402 conn =
403 conn
404 |> assign(:user, user)
405 |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => ""})
406
407 assert %{"content" => "xD", "id" => id} = json_response(conn, 200)
408
409 activity = Activity.get_by_id(id)
410
411 assert activity
412 end
413
414 test "verify_credentials", %{conn: conn} do
415 user = insert(:user)
416
417 conn =
418 conn
419 |> assign(:user, user)
420 |> get("/api/v1/accounts/verify_credentials")
421
422 assert %{"id" => id, "source" => %{"privacy" => "public"}} = json_response(conn, 200)
423 assert id == to_string(user.id)
424 end
425
426 test "verify_credentials default scope unlisted", %{conn: conn} do
427 user = insert(:user, %{info: %Pleroma.User.Info{default_scope: "unlisted"}})
428
429 conn =
430 conn
431 |> assign(:user, user)
432 |> get("/api/v1/accounts/verify_credentials")
433
434 assert %{"id" => id, "source" => %{"privacy" => "unlisted"}} = json_response(conn, 200)
435 assert id == to_string(user.id)
436 end
437
438 test "apps/verify_credentials", %{conn: conn} do
439 token = insert(:oauth_token)
440
441 conn =
442 conn
443 |> assign(:user, token.user)
444 |> assign(:token, token)
445 |> get("/api/v1/apps/verify_credentials")
446
447 app = Repo.preload(token, :app).app
448
449 expected = %{
450 "name" => app.client_name,
451 "website" => app.website,
452 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
453 }
454
455 assert expected == json_response(conn, 200)
456 end
457
458 test "creates an oauth app", %{conn: conn} do
459 user = insert(:user)
460 app_attrs = build(:oauth_app)
461
462 conn =
463 conn
464 |> assign(:user, user)
465 |> post("/api/v1/apps", %{
466 client_name: app_attrs.client_name,
467 redirect_uris: app_attrs.redirect_uris
468 })
469
470 [app] = Repo.all(App)
471
472 expected = %{
473 "name" => app.client_name,
474 "website" => app.website,
475 "client_id" => app.client_id,
476 "client_secret" => app.client_secret,
477 "id" => app.id |> to_string(),
478 "redirect_uri" => app.redirect_uris,
479 "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
480 }
481
482 assert expected == json_response(conn, 200)
483 end
484
485 test "get a status", %{conn: conn} do
486 activity = insert(:note_activity)
487
488 conn =
489 conn
490 |> get("/api/v1/statuses/#{activity.id}")
491
492 assert %{"id" => id} = json_response(conn, 200)
493 assert id == to_string(activity.id)
494 end
495
496 describe "deleting a status" do
497 test "when you created it", %{conn: conn} do
498 activity = insert(:note_activity)
499 author = User.get_by_ap_id(activity.data["actor"])
500
501 conn =
502 conn
503 |> assign(:user, author)
504 |> delete("/api/v1/statuses/#{activity.id}")
505
506 assert %{} = json_response(conn, 200)
507
508 refute Activity.get_by_id(activity.id)
509 end
510
511 test "when you didn't create it", %{conn: conn} do
512 activity = insert(:note_activity)
513 user = insert(:user)
514
515 conn =
516 conn
517 |> assign(:user, user)
518 |> delete("/api/v1/statuses/#{activity.id}")
519
520 assert %{"error" => _} = json_response(conn, 403)
521
522 assert Activity.get_by_id(activity.id) == activity
523 end
524
525 test "when you're an admin or moderator", %{conn: conn} do
526 activity1 = insert(:note_activity)
527 activity2 = insert(:note_activity)
528 admin = insert(:user, info: %{is_admin: true})
529 moderator = insert(:user, info: %{is_moderator: true})
530
531 res_conn =
532 conn
533 |> assign(:user, admin)
534 |> delete("/api/v1/statuses/#{activity1.id}")
535
536 assert %{} = json_response(res_conn, 200)
537
538 res_conn =
539 conn
540 |> assign(:user, moderator)
541 |> delete("/api/v1/statuses/#{activity2.id}")
542
543 assert %{} = json_response(res_conn, 200)
544
545 refute Activity.get_by_id(activity1.id)
546 refute Activity.get_by_id(activity2.id)
547 end
548 end
549
550 describe "filters" do
551 test "creating a filter", %{conn: conn} do
552 user = insert(:user)
553
554 filter = %Pleroma.Filter{
555 phrase: "knights",
556 context: ["home"]
557 }
558
559 conn =
560 conn
561 |> assign(:user, user)
562 |> post("/api/v1/filters", %{"phrase" => filter.phrase, context: filter.context})
563
564 assert response = json_response(conn, 200)
565 assert response["phrase"] == filter.phrase
566 assert response["context"] == filter.context
567 assert response["id"] != nil
568 assert response["id"] != ""
569 end
570
571 test "fetching a list of filters", %{conn: conn} do
572 user = insert(:user)
573
574 query_one = %Pleroma.Filter{
575 user_id: user.id,
576 filter_id: 1,
577 phrase: "knights",
578 context: ["home"]
579 }
580
581 query_two = %Pleroma.Filter{
582 user_id: user.id,
583 filter_id: 2,
584 phrase: "who",
585 context: ["home"]
586 }
587
588 {:ok, filter_one} = Pleroma.Filter.create(query_one)
589 {:ok, filter_two} = Pleroma.Filter.create(query_two)
590
591 response =
592 conn
593 |> assign(:user, user)
594 |> get("/api/v1/filters")
595 |> json_response(200)
596
597 assert response ==
598 render_json(
599 FilterView,
600 "filters.json",
601 filters: [filter_two, filter_one]
602 )
603 end
604
605 test "get a filter", %{conn: conn} do
606 user = insert(:user)
607
608 query = %Pleroma.Filter{
609 user_id: user.id,
610 filter_id: 2,
611 phrase: "knight",
612 context: ["home"]
613 }
614
615 {:ok, filter} = Pleroma.Filter.create(query)
616
617 conn =
618 conn
619 |> assign(:user, user)
620 |> get("/api/v1/filters/#{filter.filter_id}")
621
622 assert _response = json_response(conn, 200)
623 end
624
625 test "update a filter", %{conn: conn} do
626 user = insert(:user)
627
628 query = %Pleroma.Filter{
629 user_id: user.id,
630 filter_id: 2,
631 phrase: "knight",
632 context: ["home"]
633 }
634
635 {:ok, _filter} = Pleroma.Filter.create(query)
636
637 new = %Pleroma.Filter{
638 phrase: "nii",
639 context: ["home"]
640 }
641
642 conn =
643 conn
644 |> assign(:user, user)
645 |> put("/api/v1/filters/#{query.filter_id}", %{
646 phrase: new.phrase,
647 context: new.context
648 })
649
650 assert response = json_response(conn, 200)
651 assert response["phrase"] == new.phrase
652 assert response["context"] == new.context
653 end
654
655 test "delete a filter", %{conn: conn} do
656 user = insert(:user)
657
658 query = %Pleroma.Filter{
659 user_id: user.id,
660 filter_id: 2,
661 phrase: "knight",
662 context: ["home"]
663 }
664
665 {:ok, filter} = Pleroma.Filter.create(query)
666
667 conn =
668 conn
669 |> assign(:user, user)
670 |> delete("/api/v1/filters/#{filter.filter_id}")
671
672 assert response = json_response(conn, 200)
673 assert response == %{}
674 end
675 end
676
677 describe "lists" do
678 test "creating a list", %{conn: conn} do
679 user = insert(:user)
680
681 conn =
682 conn
683 |> assign(:user, user)
684 |> post("/api/v1/lists", %{"title" => "cuties"})
685
686 assert %{"title" => title} = json_response(conn, 200)
687 assert title == "cuties"
688 end
689
690 test "adding users to a list", %{conn: conn} do
691 user = insert(:user)
692 other_user = insert(:user)
693 {:ok, list} = Pleroma.List.create("name", user)
694
695 conn =
696 conn
697 |> assign(:user, user)
698 |> post("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
699
700 assert %{} == json_response(conn, 200)
701 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
702 assert following == [other_user.follower_address]
703 end
704
705 test "removing users from a list", %{conn: conn} do
706 user = insert(:user)
707 other_user = insert(:user)
708 third_user = insert(:user)
709 {:ok, list} = Pleroma.List.create("name", user)
710 {:ok, list} = Pleroma.List.follow(list, other_user)
711 {:ok, list} = Pleroma.List.follow(list, third_user)
712
713 conn =
714 conn
715 |> assign(:user, user)
716 |> delete("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
717
718 assert %{} == json_response(conn, 200)
719 %Pleroma.List{following: following} = Pleroma.List.get(list.id, user)
720 assert following == [third_user.follower_address]
721 end
722
723 test "listing users in a list", %{conn: conn} do
724 user = insert(:user)
725 other_user = insert(:user)
726 {:ok, list} = Pleroma.List.create("name", user)
727 {:ok, list} = Pleroma.List.follow(list, other_user)
728
729 conn =
730 conn
731 |> assign(:user, user)
732 |> get("/api/v1/lists/#{list.id}/accounts", %{"account_ids" => [other_user.id]})
733
734 assert [%{"id" => id}] = json_response(conn, 200)
735 assert id == to_string(other_user.id)
736 end
737
738 test "retrieving a list", %{conn: conn} do
739 user = insert(:user)
740 {:ok, list} = Pleroma.List.create("name", user)
741
742 conn =
743 conn
744 |> assign(:user, user)
745 |> get("/api/v1/lists/#{list.id}")
746
747 assert %{"id" => id} = json_response(conn, 200)
748 assert id == to_string(list.id)
749 end
750
751 test "renaming a list", %{conn: conn} do
752 user = insert(:user)
753 {:ok, list} = Pleroma.List.create("name", user)
754
755 conn =
756 conn
757 |> assign(:user, user)
758 |> put("/api/v1/lists/#{list.id}", %{"title" => "newname"})
759
760 assert %{"title" => name} = json_response(conn, 200)
761 assert name == "newname"
762 end
763
764 test "deleting a list", %{conn: conn} do
765 user = insert(:user)
766 {:ok, list} = Pleroma.List.create("name", user)
767
768 conn =
769 conn
770 |> assign(:user, user)
771 |> delete("/api/v1/lists/#{list.id}")
772
773 assert %{} = json_response(conn, 200)
774 assert is_nil(Repo.get(Pleroma.List, list.id))
775 end
776
777 test "list timeline", %{conn: conn} do
778 user = insert(:user)
779 other_user = insert(:user)
780 {:ok, _activity_one} = TwitterAPI.create_status(user, %{"status" => "Marisa is cute."})
781 {:ok, activity_two} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
782 {:ok, list} = Pleroma.List.create("name", user)
783 {:ok, list} = Pleroma.List.follow(list, other_user)
784
785 conn =
786 conn
787 |> assign(:user, user)
788 |> get("/api/v1/timelines/list/#{list.id}")
789
790 assert [%{"id" => id}] = json_response(conn, 200)
791
792 assert id == to_string(activity_two.id)
793 end
794
795 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
796 user = insert(:user)
797 other_user = insert(:user)
798 {:ok, activity_one} = TwitterAPI.create_status(other_user, %{"status" => "Marisa is cute."})
799
800 {:ok, _activity_two} =
801 TwitterAPI.create_status(other_user, %{
802 "status" => "Marisa is cute.",
803 "visibility" => "private"
804 })
805
806 {:ok, list} = Pleroma.List.create("name", user)
807 {:ok, list} = Pleroma.List.follow(list, other_user)
808
809 conn =
810 conn
811 |> assign(:user, user)
812 |> get("/api/v1/timelines/list/#{list.id}")
813
814 assert [%{"id" => id}] = json_response(conn, 200)
815
816 assert id == to_string(activity_one.id)
817 end
818 end
819
820 describe "notifications" do
821 test "list of notifications", %{conn: conn} do
822 user = insert(:user)
823 other_user = insert(:user)
824
825 {:ok, activity} =
826 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
827
828 {:ok, [_notification]} = Notification.create_notifications(activity)
829
830 conn =
831 conn
832 |> assign(:user, user)
833 |> get("/api/v1/notifications")
834
835 expected_response =
836 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
837 user.ap_id
838 }\">@<span>#{user.nickname}</span></a></span>"
839
840 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
841 assert response == expected_response
842 end
843
844 test "getting a single notification", %{conn: conn} do
845 user = insert(:user)
846 other_user = insert(:user)
847
848 {:ok, activity} =
849 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
850
851 {:ok, [notification]} = Notification.create_notifications(activity)
852
853 conn =
854 conn
855 |> assign(:user, user)
856 |> get("/api/v1/notifications/#{notification.id}")
857
858 expected_response =
859 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
860 user.ap_id
861 }\">@<span>#{user.nickname}</span></a></span>"
862
863 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
864 assert response == expected_response
865 end
866
867 test "dismissing a single notification", %{conn: conn} do
868 user = insert(:user)
869 other_user = insert(:user)
870
871 {:ok, activity} =
872 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
873
874 {:ok, [notification]} = Notification.create_notifications(activity)
875
876 conn =
877 conn
878 |> assign(:user, user)
879 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
880
881 assert %{} = json_response(conn, 200)
882 end
883
884 test "clearing all notifications", %{conn: conn} do
885 user = insert(:user)
886 other_user = insert(:user)
887
888 {:ok, activity} =
889 TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"})
890
891 {:ok, [_notification]} = Notification.create_notifications(activity)
892
893 conn =
894 conn
895 |> assign(:user, user)
896 |> post("/api/v1/notifications/clear")
897
898 assert %{} = json_response(conn, 200)
899
900 conn =
901 build_conn()
902 |> assign(:user, user)
903 |> get("/api/v1/notifications")
904
905 assert all = json_response(conn, 200)
906 assert all == []
907 end
908
909 test "paginates notifications using min_id, since_id, max_id, and limit", %{conn: conn} do
910 user = insert(:user)
911 other_user = insert(:user)
912
913 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
914 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
915 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
916 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
917
918 notification1_id = Repo.get_by(Notification, activity_id: activity1.id).id |> to_string()
919 notification2_id = Repo.get_by(Notification, activity_id: activity2.id).id |> to_string()
920 notification3_id = Repo.get_by(Notification, activity_id: activity3.id).id |> to_string()
921 notification4_id = Repo.get_by(Notification, activity_id: activity4.id).id |> to_string()
922
923 conn =
924 conn
925 |> assign(:user, user)
926
927 # min_id
928 conn_res =
929 conn
930 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
931
932 result = json_response(conn_res, 200)
933 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
934
935 # since_id
936 conn_res =
937 conn
938 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
939
940 result = json_response(conn_res, 200)
941 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
942
943 # max_id
944 conn_res =
945 conn
946 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
947
948 result = json_response(conn_res, 200)
949 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
950 end
951
952 test "filters notifications using exclude_types", %{conn: conn} do
953 user = insert(:user)
954 other_user = insert(:user)
955
956 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
957 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
958 {:ok, favorite_activity, _} = CommonAPI.favorite(create_activity.id, other_user)
959 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
960 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
961
962 mention_notification_id =
963 Repo.get_by(Notification, activity_id: mention_activity.id).id |> to_string()
964
965 favorite_notification_id =
966 Repo.get_by(Notification, activity_id: favorite_activity.id).id |> to_string()
967
968 reblog_notification_id =
969 Repo.get_by(Notification, activity_id: reblog_activity.id).id |> to_string()
970
971 follow_notification_id =
972 Repo.get_by(Notification, activity_id: follow_activity.id).id |> to_string()
973
974 conn =
975 conn
976 |> assign(:user, user)
977
978 conn_res =
979 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
980
981 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
982
983 conn_res =
984 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
985
986 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
987
988 conn_res =
989 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
990
991 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
992
993 conn_res =
994 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
995
996 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
997 end
998 end
999
1000 describe "reblogging" do
1001 test "reblogs and returns the reblogged status", %{conn: conn} do
1002 activity = insert(:note_activity)
1003 user = insert(:user)
1004
1005 conn =
1006 conn
1007 |> assign(:user, user)
1008 |> post("/api/v1/statuses/#{activity.id}/reblog")
1009
1010 assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} =
1011 json_response(conn, 200)
1012
1013 assert to_string(activity.id) == id
1014 end
1015 end
1016
1017 describe "unreblogging" do
1018 test "unreblogs and returns the unreblogged status", %{conn: conn} do
1019 activity = insert(:note_activity)
1020 user = insert(:user)
1021
1022 {:ok, _, _} = CommonAPI.repeat(activity.id, user)
1023
1024 conn =
1025 conn
1026 |> assign(:user, user)
1027 |> post("/api/v1/statuses/#{activity.id}/unreblog")
1028
1029 assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
1030
1031 assert to_string(activity.id) == id
1032 end
1033 end
1034
1035 describe "favoriting" do
1036 test "favs a status and returns it", %{conn: conn} do
1037 activity = insert(:note_activity)
1038 user = insert(:user)
1039
1040 conn =
1041 conn
1042 |> assign(:user, user)
1043 |> post("/api/v1/statuses/#{activity.id}/favourite")
1044
1045 assert %{"id" => id, "favourites_count" => 1, "favourited" => true} =
1046 json_response(conn, 200)
1047
1048 assert to_string(activity.id) == id
1049 end
1050
1051 test "returns 500 for a wrong id", %{conn: conn} do
1052 user = insert(:user)
1053
1054 resp =
1055 conn
1056 |> assign(:user, user)
1057 |> post("/api/v1/statuses/1/favourite")
1058 |> json_response(500)
1059
1060 assert resp == "Something went wrong"
1061 end
1062 end
1063
1064 describe "unfavoriting" do
1065 test "unfavorites a status and returns it", %{conn: conn} do
1066 activity = insert(:note_activity)
1067 user = insert(:user)
1068
1069 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1070
1071 conn =
1072 conn
1073 |> assign(:user, user)
1074 |> post("/api/v1/statuses/#{activity.id}/unfavourite")
1075
1076 assert %{"id" => id, "favourites_count" => 0, "favourited" => false} =
1077 json_response(conn, 200)
1078
1079 assert to_string(activity.id) == id
1080 end
1081 end
1082
1083 describe "user timelines" do
1084 test "gets a users statuses", %{conn: conn} do
1085 user_one = insert(:user)
1086 user_two = insert(:user)
1087 user_three = insert(:user)
1088
1089 {:ok, user_three} = User.follow(user_three, user_one)
1090
1091 {:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
1092
1093 {:ok, direct_activity} =
1094 CommonAPI.post(user_one, %{
1095 "status" => "Hi, @#{user_two.nickname}.",
1096 "visibility" => "direct"
1097 })
1098
1099 {:ok, private_activity} =
1100 CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
1101
1102 resp =
1103 conn
1104 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1105
1106 assert [%{"id" => id}] = json_response(resp, 200)
1107 assert id == to_string(activity.id)
1108
1109 resp =
1110 conn
1111 |> assign(:user, user_two)
1112 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1113
1114 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1115 assert id_one == to_string(direct_activity.id)
1116 assert id_two == to_string(activity.id)
1117
1118 resp =
1119 conn
1120 |> assign(:user, user_three)
1121 |> get("/api/v1/accounts/#{user_one.id}/statuses")
1122
1123 assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
1124 assert id_one == to_string(private_activity.id)
1125 assert id_two == to_string(activity.id)
1126 end
1127
1128 test "unimplemented pinned statuses feature", %{conn: conn} do
1129 note = insert(:note_activity)
1130 user = User.get_by_ap_id(note.data["actor"])
1131
1132 conn =
1133 conn
1134 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
1135
1136 assert json_response(conn, 200) == []
1137 end
1138
1139 test "gets an users media", %{conn: conn} do
1140 note = insert(:note_activity)
1141 user = User.get_by_ap_id(note.data["actor"])
1142
1143 file = %Plug.Upload{
1144 content_type: "image/jpg",
1145 path: Path.absname("test/fixtures/image.jpg"),
1146 filename: "an_image.jpg"
1147 }
1148
1149 media =
1150 TwitterAPI.upload(file, user, "json")
1151 |> Poison.decode!()
1152
1153 {:ok, image_post} =
1154 TwitterAPI.create_status(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]})
1155
1156 conn =
1157 conn
1158 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
1159
1160 assert [%{"id" => id}] = json_response(conn, 200)
1161 assert id == to_string(image_post.id)
1162
1163 conn =
1164 build_conn()
1165 |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
1166
1167 assert [%{"id" => id}] = json_response(conn, 200)
1168 assert id == to_string(image_post.id)
1169 end
1170
1171 test "gets a user's statuses without reblogs", %{conn: conn} do
1172 user = insert(:user)
1173 {:ok, post} = CommonAPI.post(user, %{"status" => "HI!!!"})
1174 {:ok, _, _} = CommonAPI.repeat(post.id, user)
1175
1176 conn =
1177 conn
1178 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "true"})
1179
1180 assert [%{"id" => id}] = json_response(conn, 200)
1181 assert id == to_string(post.id)
1182
1183 conn =
1184 conn
1185 |> get("/api/v1/accounts/#{user.id}/statuses", %{"exclude_reblogs" => "1"})
1186
1187 assert [%{"id" => id}] = json_response(conn, 200)
1188 assert id == to_string(post.id)
1189 end
1190 end
1191
1192 describe "user relationships" do
1193 test "returns the relationships for the current user", %{conn: conn} do
1194 user = insert(:user)
1195 other_user = insert(:user)
1196 {:ok, user} = User.follow(user, other_user)
1197
1198 conn =
1199 conn
1200 |> assign(:user, user)
1201 |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
1202
1203 assert [relationship] = json_response(conn, 200)
1204
1205 assert to_string(other_user.id) == relationship["id"]
1206 end
1207 end
1208
1209 describe "locked accounts" do
1210 test "/api/v1/follow_requests works" do
1211 user = insert(:user, %{info: %Pleroma.User.Info{locked: true}})
1212 other_user = insert(:user)
1213
1214 {:ok, _activity} = ActivityPub.follow(other_user, user)
1215
1216 user = User.get_by_id(user.id)
1217 other_user = User.get_by_id(other_user.id)
1218
1219 assert User.following?(other_user, user) == false
1220
1221 conn =
1222 build_conn()
1223 |> assign(:user, user)
1224 |> get("/api/v1/follow_requests")
1225
1226 assert [relationship] = json_response(conn, 200)
1227 assert to_string(other_user.id) == relationship["id"]
1228 end
1229
1230 test "/api/v1/follow_requests/:id/authorize works" do
1231 user = insert(:user, %{info: %User.Info{locked: true}})
1232 other_user = insert(:user)
1233
1234 {:ok, _activity} = ActivityPub.follow(other_user, user)
1235
1236 user = User.get_by_id(user.id)
1237 other_user = User.get_by_id(other_user.id)
1238
1239 assert User.following?(other_user, user) == false
1240
1241 conn =
1242 build_conn()
1243 |> assign(:user, user)
1244 |> post("/api/v1/follow_requests/#{other_user.id}/authorize")
1245
1246 assert relationship = json_response(conn, 200)
1247 assert to_string(other_user.id) == relationship["id"]
1248
1249 user = User.get_by_id(user.id)
1250 other_user = User.get_by_id(other_user.id)
1251
1252 assert User.following?(other_user, user) == true
1253 end
1254
1255 test "verify_credentials", %{conn: conn} do
1256 user = insert(:user, %{info: %Pleroma.User.Info{default_scope: "private"}})
1257
1258 conn =
1259 conn
1260 |> assign(:user, user)
1261 |> get("/api/v1/accounts/verify_credentials")
1262
1263 assert %{"id" => id, "source" => %{"privacy" => "private"}} = json_response(conn, 200)
1264 assert id == to_string(user.id)
1265 end
1266
1267 test "/api/v1/follow_requests/:id/reject works" do
1268 user = insert(:user, %{info: %Pleroma.User.Info{locked: true}})
1269 other_user = insert(:user)
1270
1271 {:ok, _activity} = ActivityPub.follow(other_user, user)
1272
1273 user = User.get_by_id(user.id)
1274
1275 conn =
1276 build_conn()
1277 |> assign(:user, user)
1278 |> post("/api/v1/follow_requests/#{other_user.id}/reject")
1279
1280 assert relationship = json_response(conn, 200)
1281 assert to_string(other_user.id) == relationship["id"]
1282
1283 user = User.get_by_id(user.id)
1284 other_user = User.get_by_id(other_user.id)
1285
1286 assert User.following?(other_user, user) == false
1287 end
1288 end
1289
1290 test "account fetching", %{conn: conn} do
1291 user = insert(:user)
1292
1293 conn =
1294 conn
1295 |> get("/api/v1/accounts/#{user.id}")
1296
1297 assert %{"id" => id} = json_response(conn, 200)
1298 assert id == to_string(user.id)
1299
1300 conn =
1301 build_conn()
1302 |> get("/api/v1/accounts/-1")
1303
1304 assert %{"error" => "Can't find user"} = json_response(conn, 404)
1305 end
1306
1307 test "account fetching also works nickname", %{conn: conn} do
1308 user = insert(:user)
1309
1310 conn =
1311 conn
1312 |> get("/api/v1/accounts/#{user.nickname}")
1313
1314 assert %{"id" => id} = json_response(conn, 200)
1315 assert id == user.id
1316 end
1317
1318 test "media upload", %{conn: conn} do
1319 file = %Plug.Upload{
1320 content_type: "image/jpg",
1321 path: Path.absname("test/fixtures/image.jpg"),
1322 filename: "an_image.jpg"
1323 }
1324
1325 desc = "Description of the image"
1326
1327 user = insert(:user)
1328
1329 conn =
1330 conn
1331 |> assign(:user, user)
1332 |> post("/api/v1/media", %{"file" => file, "description" => desc})
1333
1334 assert media = json_response(conn, 200)
1335
1336 assert media["type"] == "image"
1337 assert media["description"] == desc
1338 assert media["id"]
1339
1340 object = Repo.get(Object, media["id"])
1341 assert object.data["actor"] == User.ap_id(user)
1342 end
1343
1344 test "hashtag timeline", %{conn: conn} do
1345 following = insert(:user)
1346
1347 capture_log(fn ->
1348 {:ok, activity} = TwitterAPI.create_status(following, %{"status" => "test #2hu"})
1349
1350 {:ok, [_activity]} =
1351 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
1352
1353 nconn =
1354 conn
1355 |> get("/api/v1/timelines/tag/2hu")
1356
1357 assert [%{"id" => id}] = json_response(nconn, 200)
1358
1359 assert id == to_string(activity.id)
1360
1361 # works for different capitalization too
1362 nconn =
1363 conn
1364 |> get("/api/v1/timelines/tag/2HU")
1365
1366 assert [%{"id" => id}] = json_response(nconn, 200)
1367
1368 assert id == to_string(activity.id)
1369 end)
1370 end
1371
1372 test "multi-hashtag timeline", %{conn: conn} do
1373 user = insert(:user)
1374
1375 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
1376 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
1377 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
1378
1379 any_test =
1380 conn
1381 |> get("/api/v1/timelines/tag/test", %{"any" => ["test1"]})
1382
1383 [status_none, status_test1, status_test] = json_response(any_test, 200)
1384
1385 assert to_string(activity_test.id) == status_test["id"]
1386 assert to_string(activity_test1.id) == status_test1["id"]
1387 assert to_string(activity_none.id) == status_none["id"]
1388
1389 restricted_test =
1390 conn
1391 |> get("/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
1392
1393 assert [status_test1] == json_response(restricted_test, 200)
1394
1395 all_test = conn |> get("/api/v1/timelines/tag/test", %{"all" => ["none"]})
1396
1397 assert [status_none] == json_response(all_test, 200)
1398 end
1399
1400 test "getting followers", %{conn: conn} do
1401 user = insert(:user)
1402 other_user = insert(:user)
1403 {:ok, user} = User.follow(user, other_user)
1404
1405 conn =
1406 conn
1407 |> get("/api/v1/accounts/#{other_user.id}/followers")
1408
1409 assert [%{"id" => id}] = json_response(conn, 200)
1410 assert id == to_string(user.id)
1411 end
1412
1413 test "getting followers, hide_followers", %{conn: conn} do
1414 user = insert(:user)
1415 other_user = insert(:user, %{info: %{hide_followers: true}})
1416 {:ok, _user} = User.follow(user, other_user)
1417
1418 conn =
1419 conn
1420 |> get("/api/v1/accounts/#{other_user.id}/followers")
1421
1422 assert [] == json_response(conn, 200)
1423 end
1424
1425 test "getting followers, hide_followers, same user requesting", %{conn: conn} do
1426 user = insert(:user)
1427 other_user = insert(:user, %{info: %{hide_followers: true}})
1428 {:ok, _user} = User.follow(user, other_user)
1429
1430 conn =
1431 conn
1432 |> assign(:user, other_user)
1433 |> get("/api/v1/accounts/#{other_user.id}/followers")
1434
1435 refute [] == json_response(conn, 200)
1436 end
1437
1438 test "getting followers, pagination", %{conn: conn} do
1439 user = insert(:user)
1440 follower1 = insert(:user)
1441 follower2 = insert(:user)
1442 follower3 = insert(:user)
1443 {:ok, _} = User.follow(follower1, user)
1444 {:ok, _} = User.follow(follower2, user)
1445 {:ok, _} = User.follow(follower3, user)
1446
1447 conn =
1448 conn
1449 |> assign(:user, user)
1450
1451 res_conn =
1452 conn
1453 |> get("/api/v1/accounts/#{user.id}/followers?since_id=#{follower1.id}")
1454
1455 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1456 assert id3 == follower3.id
1457 assert id2 == follower2.id
1458
1459 res_conn =
1460 conn
1461 |> get("/api/v1/accounts/#{user.id}/followers?max_id=#{follower3.id}")
1462
1463 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1464 assert id2 == follower2.id
1465 assert id1 == follower1.id
1466
1467 res_conn =
1468 conn
1469 |> get("/api/v1/accounts/#{user.id}/followers?limit=1&max_id=#{follower3.id}")
1470
1471 assert [%{"id" => id2}] = json_response(res_conn, 200)
1472 assert id2 == follower2.id
1473
1474 assert [link_header] = get_resp_header(res_conn, "link")
1475 assert link_header =~ ~r/since_id=#{follower2.id}/
1476 assert link_header =~ ~r/max_id=#{follower2.id}/
1477 end
1478
1479 test "getting following", %{conn: conn} do
1480 user = insert(:user)
1481 other_user = insert(:user)
1482 {:ok, user} = User.follow(user, other_user)
1483
1484 conn =
1485 conn
1486 |> get("/api/v1/accounts/#{user.id}/following")
1487
1488 assert [%{"id" => id}] = json_response(conn, 200)
1489 assert id == to_string(other_user.id)
1490 end
1491
1492 test "getting following, hide_follows", %{conn: conn} do
1493 user = insert(:user, %{info: %{hide_follows: true}})
1494 other_user = insert(:user)
1495 {:ok, user} = User.follow(user, other_user)
1496
1497 conn =
1498 conn
1499 |> get("/api/v1/accounts/#{user.id}/following")
1500
1501 assert [] == json_response(conn, 200)
1502 end
1503
1504 test "getting following, hide_follows, same user requesting", %{conn: conn} do
1505 user = insert(:user, %{info: %{hide_follows: true}})
1506 other_user = insert(:user)
1507 {:ok, user} = User.follow(user, other_user)
1508
1509 conn =
1510 conn
1511 |> assign(:user, user)
1512 |> get("/api/v1/accounts/#{user.id}/following")
1513
1514 refute [] == json_response(conn, 200)
1515 end
1516
1517 test "getting following, pagination", %{conn: conn} do
1518 user = insert(:user)
1519 following1 = insert(:user)
1520 following2 = insert(:user)
1521 following3 = insert(:user)
1522 {:ok, _} = User.follow(user, following1)
1523 {:ok, _} = User.follow(user, following2)
1524 {:ok, _} = User.follow(user, following3)
1525
1526 conn =
1527 conn
1528 |> assign(:user, user)
1529
1530 res_conn =
1531 conn
1532 |> get("/api/v1/accounts/#{user.id}/following?since_id=#{following1.id}")
1533
1534 assert [%{"id" => id3}, %{"id" => id2}] = json_response(res_conn, 200)
1535 assert id3 == following3.id
1536 assert id2 == following2.id
1537
1538 res_conn =
1539 conn
1540 |> get("/api/v1/accounts/#{user.id}/following?max_id=#{following3.id}")
1541
1542 assert [%{"id" => id2}, %{"id" => id1}] = json_response(res_conn, 200)
1543 assert id2 == following2.id
1544 assert id1 == following1.id
1545
1546 res_conn =
1547 conn
1548 |> get("/api/v1/accounts/#{user.id}/following?limit=1&max_id=#{following3.id}")
1549
1550 assert [%{"id" => id2}] = json_response(res_conn, 200)
1551 assert id2 == following2.id
1552
1553 assert [link_header] = get_resp_header(res_conn, "link")
1554 assert link_header =~ ~r/since_id=#{following2.id}/
1555 assert link_header =~ ~r/max_id=#{following2.id}/
1556 end
1557
1558 test "following / unfollowing a user", %{conn: conn} do
1559 user = insert(:user)
1560 other_user = insert(:user)
1561
1562 conn =
1563 conn
1564 |> assign(:user, user)
1565 |> post("/api/v1/accounts/#{other_user.id}/follow")
1566
1567 assert %{"id" => _id, "following" => true} = json_response(conn, 200)
1568
1569 user = User.get_by_id(user.id)
1570
1571 conn =
1572 build_conn()
1573 |> assign(:user, user)
1574 |> post("/api/v1/accounts/#{other_user.id}/unfollow")
1575
1576 assert %{"id" => _id, "following" => false} = json_response(conn, 200)
1577
1578 user = User.get_by_id(user.id)
1579
1580 conn =
1581 build_conn()
1582 |> assign(:user, user)
1583 |> post("/api/v1/follows", %{"uri" => other_user.nickname})
1584
1585 assert %{"id" => id} = json_response(conn, 200)
1586 assert id == to_string(other_user.id)
1587 end
1588
1589 test "muting / unmuting a user", %{conn: conn} do
1590 user = insert(:user)
1591 other_user = insert(:user)
1592
1593 conn =
1594 conn
1595 |> assign(:user, user)
1596 |> post("/api/v1/accounts/#{other_user.id}/mute")
1597
1598 assert %{"id" => _id, "muting" => true} = json_response(conn, 200)
1599
1600 user = User.get_by_id(user.id)
1601
1602 conn =
1603 build_conn()
1604 |> assign(:user, user)
1605 |> post("/api/v1/accounts/#{other_user.id}/unmute")
1606
1607 assert %{"id" => _id, "muting" => false} = json_response(conn, 200)
1608 end
1609
1610 test "getting a list of mutes", %{conn: conn} do
1611 user = insert(:user)
1612 other_user = insert(:user)
1613
1614 {:ok, user} = User.mute(user, other_user)
1615
1616 conn =
1617 conn
1618 |> assign(:user, user)
1619 |> get("/api/v1/mutes")
1620
1621 other_user_id = to_string(other_user.id)
1622 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1623 end
1624
1625 test "blocking / unblocking a user", %{conn: conn} do
1626 user = insert(:user)
1627 other_user = insert(:user)
1628
1629 conn =
1630 conn
1631 |> assign(:user, user)
1632 |> post("/api/v1/accounts/#{other_user.id}/block")
1633
1634 assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
1635
1636 user = User.get_by_id(user.id)
1637
1638 conn =
1639 build_conn()
1640 |> assign(:user, user)
1641 |> post("/api/v1/accounts/#{other_user.id}/unblock")
1642
1643 assert %{"id" => _id, "blocking" => false} = json_response(conn, 200)
1644 end
1645
1646 test "getting a list of blocks", %{conn: conn} do
1647 user = insert(:user)
1648 other_user = insert(:user)
1649
1650 {:ok, user} = User.block(user, other_user)
1651
1652 conn =
1653 conn
1654 |> assign(:user, user)
1655 |> get("/api/v1/blocks")
1656
1657 other_user_id = to_string(other_user.id)
1658 assert [%{"id" => ^other_user_id}] = json_response(conn, 200)
1659 end
1660
1661 test "blocking / unblocking a domain", %{conn: conn} do
1662 user = insert(:user)
1663 other_user = insert(:user, %{ap_id: "https://dogwhistle.zone/@pundit"})
1664
1665 conn =
1666 conn
1667 |> assign(:user, user)
1668 |> post("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1669
1670 assert %{} = json_response(conn, 200)
1671 user = User.get_cached_by_ap_id(user.ap_id)
1672 assert User.blocks?(user, other_user)
1673
1674 conn =
1675 build_conn()
1676 |> assign(:user, user)
1677 |> delete("/api/v1/domain_blocks", %{"domain" => "dogwhistle.zone"})
1678
1679 assert %{} = json_response(conn, 200)
1680 user = User.get_cached_by_ap_id(user.ap_id)
1681 refute User.blocks?(user, other_user)
1682 end
1683
1684 test "getting a list of domain blocks", %{conn: conn} do
1685 user = insert(:user)
1686
1687 {:ok, user} = User.block_domain(user, "bad.site")
1688 {:ok, user} = User.block_domain(user, "even.worse.site")
1689
1690 conn =
1691 conn
1692 |> assign(:user, user)
1693 |> get("/api/v1/domain_blocks")
1694
1695 domain_blocks = json_response(conn, 200)
1696
1697 assert "bad.site" in domain_blocks
1698 assert "even.worse.site" in domain_blocks
1699 end
1700
1701 test "unimplemented follow_requests, blocks, domain blocks" do
1702 user = insert(:user)
1703
1704 ["blocks", "domain_blocks", "follow_requests"]
1705 |> Enum.each(fn endpoint ->
1706 conn =
1707 build_conn()
1708 |> assign(:user, user)
1709 |> get("/api/v1/#{endpoint}")
1710
1711 assert [] = json_response(conn, 200)
1712 end)
1713 end
1714
1715 test "account search", %{conn: conn} do
1716 user = insert(:user)
1717 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
1718 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
1719
1720 results =
1721 conn
1722 |> assign(:user, user)
1723 |> get("/api/v1/accounts/search", %{"q" => "shp"})
1724 |> json_response(200)
1725
1726 result_ids = for result <- results, do: result["acct"]
1727
1728 assert user_two.nickname in result_ids
1729 assert user_three.nickname in result_ids
1730
1731 results =
1732 conn
1733 |> assign(:user, user)
1734 |> get("/api/v1/accounts/search", %{"q" => "2hu"})
1735 |> json_response(200)
1736
1737 result_ids = for result <- results, do: result["acct"]
1738
1739 assert user_three.nickname in result_ids
1740 end
1741
1742 test "search", %{conn: conn} do
1743 user = insert(:user)
1744 user_two = insert(:user, %{nickname: "shp@shitposter.club"})
1745 user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
1746
1747 {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
1748
1749 {:ok, _activity} =
1750 CommonAPI.post(user, %{
1751 "status" => "This is about 2hu, but private",
1752 "visibility" => "private"
1753 })
1754
1755 {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"})
1756
1757 conn =
1758 conn
1759 |> get("/api/v1/search", %{"q" => "2hu"})
1760
1761 assert results = json_response(conn, 200)
1762
1763 [account | _] = results["accounts"]
1764 assert account["id"] == to_string(user_three.id)
1765
1766 assert results["hashtags"] == []
1767
1768 [status] = results["statuses"]
1769 assert status["id"] == to_string(activity.id)
1770 end
1771
1772 test "search fetches remote statuses", %{conn: conn} do
1773 capture_log(fn ->
1774 conn =
1775 conn
1776 |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"})
1777
1778 assert results = json_response(conn, 200)
1779
1780 [status] = results["statuses"]
1781 assert status["uri"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
1782 end)
1783 end
1784
1785 test "search doesn't show statuses that it shouldn't", %{conn: conn} do
1786 {:ok, activity} =
1787 CommonAPI.post(insert(:user), %{
1788 "status" => "This is about 2hu, but private",
1789 "visibility" => "private"
1790 })
1791
1792 capture_log(fn ->
1793 conn =
1794 conn
1795 |> get("/api/v1/search", %{"q" => activity.data["object"]["id"]})
1796
1797 assert results = json_response(conn, 200)
1798
1799 [] = results["statuses"]
1800 end)
1801 end
1802
1803 test "search fetches remote accounts", %{conn: conn} do
1804 conn =
1805 conn
1806 |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"})
1807
1808 assert results = json_response(conn, 200)
1809 [account] = results["accounts"]
1810 assert account["acct"] == "shp@social.heldscal.la"
1811 end
1812
1813 test "returns the favorites of a user", %{conn: conn} do
1814 user = insert(:user)
1815 other_user = insert(:user)
1816
1817 {:ok, _} = CommonAPI.post(other_user, %{"status" => "bla"})
1818 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "traps are happy"})
1819
1820 {:ok, _, _} = CommonAPI.favorite(activity.id, user)
1821
1822 first_conn =
1823 conn
1824 |> assign(:user, user)
1825 |> get("/api/v1/favourites")
1826
1827 assert [status] = json_response(first_conn, 200)
1828 assert status["id"] == to_string(activity.id)
1829
1830 assert [{"link", _link_header}] =
1831 Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
1832
1833 # Honours query params
1834 {:ok, second_activity} =
1835 CommonAPI.post(other_user, %{
1836 "status" =>
1837 "Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
1838 })
1839
1840 {:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
1841
1842 last_like = status["id"]
1843
1844 second_conn =
1845 conn
1846 |> assign(:user, user)
1847 |> get("/api/v1/favourites?since_id=#{last_like}")
1848
1849 assert [second_status] = json_response(second_conn, 200)
1850 assert second_status["id"] == to_string(second_activity.id)
1851
1852 third_conn =
1853 conn
1854 |> assign(:user, user)
1855 |> get("/api/v1/favourites?limit=0")
1856
1857 assert [] = json_response(third_conn, 200)
1858 end
1859
1860 describe "updating credentials" do
1861 test "updates the user's bio", %{conn: conn} do
1862 user = insert(:user)
1863 user2 = insert(:user)
1864
1865 conn =
1866 conn
1867 |> assign(:user, user)
1868 |> patch("/api/v1/accounts/update_credentials", %{
1869 "note" => "I drink #cofe with @#{user2.nickname}"
1870 })
1871
1872 assert user = json_response(conn, 200)
1873
1874 assert user["note"] ==
1875 ~s(I drink <a class="hashtag" data-tag="cofe" href="http://localhost:4001/tag/cofe" rel="tag">#cofe</a> with <span class="h-card"><a data-user=") <>
1876 user2.id <>
1877 ~s(" class="u-url mention" href=") <>
1878 user2.ap_id <> ~s(">@<span>) <> user2.nickname <> ~s(</span></a></span>)
1879 end
1880
1881 test "updates the user's locking status", %{conn: conn} do
1882 user = insert(:user)
1883
1884 conn =
1885 conn
1886 |> assign(:user, user)
1887 |> patch("/api/v1/accounts/update_credentials", %{locked: "true"})
1888
1889 assert user = json_response(conn, 200)
1890 assert user["locked"] == true
1891 end
1892
1893 test "updates the user's name", %{conn: conn} do
1894 user = insert(:user)
1895
1896 conn =
1897 conn
1898 |> assign(:user, user)
1899 |> patch("/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"})
1900
1901 assert user = json_response(conn, 200)
1902 assert user["display_name"] == "markorepairs"
1903 end
1904
1905 test "updates the user's avatar", %{conn: conn} do
1906 user = insert(:user)
1907
1908 new_avatar = %Plug.Upload{
1909 content_type: "image/jpg",
1910 path: Path.absname("test/fixtures/image.jpg"),
1911 filename: "an_image.jpg"
1912 }
1913
1914 conn =
1915 conn
1916 |> assign(:user, user)
1917 |> patch("/api/v1/accounts/update_credentials", %{"avatar" => new_avatar})
1918
1919 assert user_response = json_response(conn, 200)
1920 assert user_response["avatar"] != User.avatar_url(user)
1921 end
1922
1923 test "updates the user's banner", %{conn: conn} do
1924 user = insert(:user)
1925
1926 new_header = %Plug.Upload{
1927 content_type: "image/jpg",
1928 path: Path.absname("test/fixtures/image.jpg"),
1929 filename: "an_image.jpg"
1930 }
1931
1932 conn =
1933 conn
1934 |> assign(:user, user)
1935 |> patch("/api/v1/accounts/update_credentials", %{"header" => new_header})
1936
1937 assert user_response = json_response(conn, 200)
1938 assert user_response["header"] != User.banner_url(user)
1939 end
1940
1941 test "requires 'write' permission", %{conn: conn} do
1942 token1 = insert(:oauth_token, scopes: ["read"])
1943 token2 = insert(:oauth_token, scopes: ["write", "follow"])
1944
1945 for token <- [token1, token2] do
1946 conn =
1947 conn
1948 |> put_req_header("authorization", "Bearer #{token.token}")
1949 |> patch("/api/v1/accounts/update_credentials", %{})
1950
1951 if token == token1 do
1952 assert %{"error" => "Insufficient permissions: write."} == json_response(conn, 403)
1953 else
1954 assert json_response(conn, 200)
1955 end
1956 end
1957 end
1958 end
1959
1960 test "get instance information", %{conn: conn} do
1961 conn = get(conn, "/api/v1/instance")
1962 assert result = json_response(conn, 200)
1963
1964 # Note: not checking for "max_toot_chars" since it's optional
1965 assert %{
1966 "uri" => _,
1967 "title" => _,
1968 "description" => _,
1969 "version" => _,
1970 "email" => _,
1971 "urls" => %{
1972 "streaming_api" => _
1973 },
1974 "stats" => _,
1975 "thumbnail" => _,
1976 "languages" => _,
1977 "registrations" => _
1978 } = result
1979 end
1980
1981 test "get instance stats", %{conn: conn} do
1982 user = insert(:user, %{local: true})
1983
1984 user2 = insert(:user, %{local: true})
1985 {:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
1986
1987 insert(:user, %{local: false, nickname: "u@peer1.com"})
1988 insert(:user, %{local: false, nickname: "u@peer2.com"})
1989
1990 {:ok, _} = TwitterAPI.create_status(user, %{"status" => "cofe"})
1991
1992 # Stats should count users with missing or nil `info.deactivated` value
1993 user = User.get_by_id(user.id)
1994 info_change = Changeset.change(user.info, %{deactivated: nil})
1995
1996 {:ok, _user} =
1997 user
1998 |> Changeset.change()
1999 |> Changeset.put_embed(:info, info_change)
2000 |> User.update_and_set_cache()
2001
2002 Pleroma.Stats.update_stats()
2003
2004 conn = get(conn, "/api/v1/instance")
2005
2006 assert result = json_response(conn, 200)
2007
2008 stats = result["stats"]
2009
2010 assert stats
2011 assert stats["user_count"] == 1
2012 assert stats["status_count"] == 1
2013 assert stats["domain_count"] == 2
2014 end
2015
2016 test "get peers", %{conn: conn} do
2017 insert(:user, %{local: false, nickname: "u@peer1.com"})
2018 insert(:user, %{local: false, nickname: "u@peer2.com"})
2019
2020 Pleroma.Stats.update_stats()
2021
2022 conn = get(conn, "/api/v1/instance/peers")
2023
2024 assert result = json_response(conn, 200)
2025
2026 assert ["peer1.com", "peer2.com"] == Enum.sort(result)
2027 end
2028
2029 test "put settings", %{conn: conn} do
2030 user = insert(:user)
2031
2032 conn =
2033 conn
2034 |> assign(:user, user)
2035 |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
2036
2037 assert _result = json_response(conn, 200)
2038
2039 user = User.get_cached_by_ap_id(user.ap_id)
2040 assert user.info.settings == %{"programming" => "socks"}
2041 end
2042
2043 describe "pinned statuses" do
2044 setup do
2045 Pleroma.Config.put([:instance, :max_pinned_statuses], 1)
2046
2047 user = insert(:user)
2048 {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!"})
2049
2050 [user: user, activity: activity]
2051 end
2052
2053 test "returns pinned statuses", %{conn: conn, user: user, activity: activity} do
2054 {:ok, _} = CommonAPI.pin(activity.id, user)
2055
2056 result =
2057 conn
2058 |> assign(:user, user)
2059 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2060 |> json_response(200)
2061
2062 id_str = to_string(activity.id)
2063
2064 assert [%{"id" => ^id_str, "pinned" => true}] = result
2065 end
2066
2067 test "pin status", %{conn: conn, user: user, activity: activity} do
2068 id_str = to_string(activity.id)
2069
2070 assert %{"id" => ^id_str, "pinned" => true} =
2071 conn
2072 |> assign(:user, user)
2073 |> post("/api/v1/statuses/#{activity.id}/pin")
2074 |> json_response(200)
2075
2076 assert [%{"id" => ^id_str, "pinned" => true}] =
2077 conn
2078 |> assign(:user, user)
2079 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2080 |> json_response(200)
2081 end
2082
2083 test "unpin status", %{conn: conn, user: user, activity: activity} do
2084 {:ok, _} = CommonAPI.pin(activity.id, user)
2085
2086 id_str = to_string(activity.id)
2087 user = refresh_record(user)
2088
2089 assert %{"id" => ^id_str, "pinned" => false} =
2090 conn
2091 |> assign(:user, user)
2092 |> post("/api/v1/statuses/#{activity.id}/unpin")
2093 |> json_response(200)
2094
2095 assert [] =
2096 conn
2097 |> assign(:user, user)
2098 |> get("/api/v1/accounts/#{user.id}/statuses?pinned=true")
2099 |> json_response(200)
2100 end
2101
2102 test "max pinned statuses", %{conn: conn, user: user, activity: activity_one} do
2103 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "HI!!!"})
2104
2105 id_str_one = to_string(activity_one.id)
2106
2107 assert %{"id" => ^id_str_one, "pinned" => true} =
2108 conn
2109 |> assign(:user, user)
2110 |> post("/api/v1/statuses/#{id_str_one}/pin")
2111 |> json_response(200)
2112
2113 user = refresh_record(user)
2114
2115 assert %{"error" => "You have already pinned the maximum number of statuses"} =
2116 conn
2117 |> assign(:user, user)
2118 |> post("/api/v1/statuses/#{activity_two.id}/pin")
2119 |> json_response(400)
2120 end
2121
2122 test "Status rich-media Card", %{conn: conn, user: user} do
2123 Pleroma.Config.put([:rich_media, :enabled], true)
2124 {:ok, activity} = CommonAPI.post(user, %{"status" => "http://example.com/ogp"})
2125
2126 response =
2127 conn
2128 |> get("/api/v1/statuses/#{activity.id}/card")
2129 |> json_response(200)
2130
2131 assert response == %{
2132 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2133 "provider_name" => "www.imdb.com",
2134 "provider_url" => "http://www.imdb.com",
2135 "title" => "The Rock",
2136 "type" => "link",
2137 "url" => "http://www.imdb.com/title/tt0117500/",
2138 "description" => nil,
2139 "pleroma" => %{
2140 "opengraph" => %{
2141 "image" => "http://ia.media-imdb.com/images/rock.jpg",
2142 "title" => "The Rock",
2143 "type" => "video.movie",
2144 "url" => "http://www.imdb.com/title/tt0117500/"
2145 }
2146 }
2147 }
2148
2149 # works with private posts
2150 {:ok, activity} =
2151 CommonAPI.post(user, %{"status" => "http://example.com/ogp", "visibility" => "direct"})
2152
2153 response_two =
2154 conn
2155 |> assign(:user, user)
2156 |> get("/api/v1/statuses/#{activity.id}/card")
2157 |> json_response(200)
2158
2159 assert response_two == response
2160
2161 Pleroma.Config.put([:rich_media, :enabled], false)
2162 end
2163 end
2164
2165 test "bookmarks" do
2166 user = insert(:user)
2167 for_user = insert(:user)
2168
2169 {:ok, activity1} =
2170 CommonAPI.post(user, %{
2171 "status" => "heweoo?"
2172 })
2173
2174 {:ok, activity2} =
2175 CommonAPI.post(user, %{
2176 "status" => "heweoo!"
2177 })
2178
2179 response1 =
2180 build_conn()
2181 |> assign(:user, for_user)
2182 |> post("/api/v1/statuses/#{activity1.id}/bookmark")
2183
2184 assert json_response(response1, 200)["bookmarked"] == true
2185
2186 response2 =
2187 build_conn()
2188 |> assign(:user, for_user)
2189 |> post("/api/v1/statuses/#{activity2.id}/bookmark")
2190
2191 assert json_response(response2, 200)["bookmarked"] == true
2192
2193 bookmarks =
2194 build_conn()
2195 |> assign(:user, for_user)
2196 |> get("/api/v1/bookmarks")
2197
2198 assert [json_response(response2, 200), json_response(response1, 200)] ==
2199 json_response(bookmarks, 200)
2200
2201 response1 =
2202 build_conn()
2203 |> assign(:user, for_user)
2204 |> post("/api/v1/statuses/#{activity1.id}/unbookmark")
2205
2206 assert json_response(response1, 200)["bookmarked"] == false
2207
2208 bookmarks =
2209 build_conn()
2210 |> assign(:user, for_user)
2211 |> get("/api/v1/bookmarks")
2212
2213 assert [json_response(response2, 200)] == json_response(bookmarks, 200)
2214 end
2215
2216 describe "conversation muting" do
2217 setup do
2218 user = insert(:user)
2219 {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"})
2220
2221 [user: user, activity: activity]
2222 end
2223
2224 test "mute conversation", %{conn: conn, user: user, activity: activity} do
2225 id_str = to_string(activity.id)
2226
2227 assert %{"id" => ^id_str, "muted" => true} =
2228 conn
2229 |> assign(:user, user)
2230 |> post("/api/v1/statuses/#{activity.id}/mute")
2231 |> json_response(200)
2232 end
2233
2234 test "unmute conversation", %{conn: conn, user: user, activity: activity} do
2235 {:ok, _} = CommonAPI.add_mute(user, activity)
2236
2237 id_str = to_string(activity.id)
2238 user = refresh_record(user)
2239
2240 assert %{"id" => ^id_str, "muted" => false} =
2241 conn
2242 |> assign(:user, user)
2243 |> post("/api/v1/statuses/#{activity.id}/unmute")
2244 |> json_response(200)
2245 end
2246 end
2247
2248 test "flavours switching (Pleroma Extension)", %{conn: conn} do
2249 user = insert(:user)
2250
2251 get_old_flavour =
2252 conn
2253 |> assign(:user, user)
2254 |> get("/api/v1/pleroma/flavour")
2255
2256 assert "glitch" == json_response(get_old_flavour, 200)
2257
2258 set_flavour =
2259 conn
2260 |> assign(:user, user)
2261 |> post("/api/v1/pleroma/flavour/vanilla")
2262
2263 assert "vanilla" == json_response(set_flavour, 200)
2264
2265 get_new_flavour =
2266 conn
2267 |> assign(:user, user)
2268 |> post("/api/v1/pleroma/flavour/vanilla")
2269
2270 assert json_response(set_flavour, 200) == json_response(get_new_flavour, 200)
2271 end
2272
2273 describe "reports" do
2274 setup do
2275 reporter = insert(:user)
2276 target_user = insert(:user)
2277
2278 {:ok, activity} = CommonAPI.post(target_user, %{"status" => "foobar"})
2279
2280 [reporter: reporter, target_user: target_user, activity: activity]
2281 end
2282
2283 test "submit a basic report", %{conn: conn, reporter: reporter, target_user: target_user} do
2284 assert %{"action_taken" => false, "id" => _} =
2285 conn
2286 |> assign(:user, reporter)
2287 |> post("/api/v1/reports", %{"account_id" => target_user.id})
2288 |> json_response(200)
2289 end
2290
2291 test "submit a report with statuses and comment", %{
2292 conn: conn,
2293 reporter: reporter,
2294 target_user: target_user,
2295 activity: activity
2296 } do
2297 assert %{"action_taken" => false, "id" => _} =
2298 conn
2299 |> assign(:user, reporter)
2300 |> post("/api/v1/reports", %{
2301 "account_id" => target_user.id,
2302 "status_ids" => [activity.id],
2303 "comment" => "bad status!"
2304 })
2305 |> json_response(200)
2306 end
2307
2308 test "account_id is required", %{
2309 conn: conn,
2310 reporter: reporter,
2311 activity: activity
2312 } do
2313 assert %{"error" => "Valid `account_id` required"} =
2314 conn
2315 |> assign(:user, reporter)
2316 |> post("/api/v1/reports", %{"status_ids" => [activity.id]})
2317 |> json_response(400)
2318 end
2319
2320 test "comment must be up to the size specified in the config", %{
2321 conn: conn,
2322 reporter: reporter,
2323 target_user: target_user
2324 } do
2325 max_size = Pleroma.Config.get([:instance, :max_report_comment_size], 1000)
2326 comment = String.pad_trailing("a", max_size + 1, "a")
2327
2328 error = %{"error" => "Comment must be up to #{max_size} characters"}
2329
2330 assert ^error =
2331 conn
2332 |> assign(:user, reporter)
2333 |> post("/api/v1/reports", %{"account_id" => target_user.id, "comment" => comment})
2334 |> json_response(400)
2335 end
2336 end
2337
2338 describe "link headers" do
2339 test "preserves parameters in link headers", %{conn: conn} do
2340 user = insert(:user)
2341 other_user = insert(:user)
2342
2343 {:ok, activity1} =
2344 CommonAPI.post(other_user, %{
2345 "status" => "hi @#{user.nickname}",
2346 "visibility" => "public"
2347 })
2348
2349 {:ok, activity2} =
2350 CommonAPI.post(other_user, %{
2351 "status" => "hi @#{user.nickname}",
2352 "visibility" => "public"
2353 })
2354
2355 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
2356 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
2357
2358 conn =
2359 conn
2360 |> assign(:user, user)
2361 |> get("/api/v1/notifications", %{media_only: true})
2362
2363 assert [link_header] = get_resp_header(conn, "link")
2364 assert link_header =~ ~r/media_only=true/
2365 assert link_header =~ ~r/since_id=#{notification2.id}/
2366 assert link_header =~ ~r/max_id=#{notification1.id}/
2367 end
2368 end
2369
2370 test "accounts fetches correct account for nicknames beginning with numbers", %{conn: conn} do
2371 # Need to set an old-style integer ID to reproduce the problem
2372 # (these are no longer assigned to new accounts but were preserved
2373 # for existing accounts during the migration to flakeIDs)
2374 user_one = insert(:user, %{id: 1212})
2375 user_two = insert(:user, %{nickname: "#{user_one.id}garbage"})
2376
2377 resp_one =
2378 conn
2379 |> get("/api/v1/accounts/#{user_one.id}")
2380
2381 resp_two =
2382 conn
2383 |> get("/api/v1/accounts/#{user_two.nickname}")
2384
2385 resp_three =
2386 conn
2387 |> get("/api/v1/accounts/#{user_two.id}")
2388
2389 acc_one = json_response(resp_one, 200)
2390 acc_two = json_response(resp_two, 200)
2391 acc_three = json_response(resp_three, 200)
2392 refute acc_one == acc_two
2393 assert acc_two == acc_three
2394 end
2395
2396 describe "custom emoji" do
2397 test "with tags", %{conn: conn} do
2398 [emoji | _body] =
2399 conn
2400 |> get("/api/v1/custom_emojis")
2401 |> json_response(200)
2402
2403 assert Map.has_key?(emoji, "shortcode")
2404 assert Map.has_key?(emoji, "static_url")
2405 assert Map.has_key?(emoji, "tags")
2406 assert is_list(emoji["tags"])
2407 assert Map.has_key?(emoji, "url")
2408 assert Map.has_key?(emoji, "visible_in_picker")
2409 end
2410 end
2411
2412 describe "index/2 redirections" do
2413 setup %{conn: conn} do
2414 session_opts = [
2415 store: :cookie,
2416 key: "_test",
2417 signing_salt: "cooldude"
2418 ]
2419
2420 conn =
2421 conn
2422 |> Plug.Session.call(Plug.Session.init(session_opts))
2423 |> fetch_session()
2424
2425 test_path = "/web/statuses/test"
2426 %{conn: conn, path: test_path}
2427 end
2428
2429 test "redirects not logged-in users to the login page", %{conn: conn, path: path} do
2430 conn = get(conn, path)
2431
2432 assert conn.status == 302
2433 assert redirected_to(conn) == "/web/login"
2434 end
2435
2436 test "does not redirect logged in users to the login page", %{conn: conn, path: path} do
2437 token = insert(:oauth_token)
2438
2439 conn =
2440 conn
2441 |> assign(:user, token.user)
2442 |> put_session(:oauth_token, token.token)
2443 |> get(path)
2444
2445 assert conn.status == 200
2446 end
2447
2448 test "saves referer path to session", %{conn: conn, path: path} do
2449 conn = get(conn, path)
2450 return_to = Plug.Conn.get_session(conn, :return_to)
2451
2452 assert return_to == path
2453 end
2454
2455 test "redirects to the saved path after log in", %{conn: conn, path: path} do
2456 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
2457 auth = insert(:oauth_authorization, app: app)
2458
2459 conn =
2460 conn
2461 |> put_session(:return_to, path)
2462 |> get("/web/login", %{code: auth.token})
2463
2464 assert conn.status == 302
2465 assert redirected_to(conn) == path
2466 end
2467
2468 test "redirects to the getting-started page when referer is not present", %{conn: conn} do
2469 app = insert(:oauth_app, client_name: "Mastodon-Local", redirect_uris: ".")
2470 auth = insert(:oauth_authorization, app: app)
2471
2472 conn = get(conn, "/web/login", %{code: auth.token})
2473
2474 assert conn.status == 302
2475 assert redirected_to(conn) == "/web/getting-started"
2476 end
2477 end
2478
2479 describe "scheduled activities" do
2480 test "creates a scheduled activity", %{conn: conn} do
2481 user = insert(:user)
2482 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
2483
2484 conn =
2485 conn
2486 |> assign(:user, user)
2487 |> post("/api/v1/statuses", %{
2488 "status" => "scheduled",
2489 "scheduled_at" => scheduled_at
2490 })
2491
2492 assert %{"scheduled_at" => expected_scheduled_at} = json_response(conn, 200)
2493 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(scheduled_at)
2494 assert [] == Repo.all(Activity)
2495 end
2496
2497 test "creates a scheduled activity with a media attachment", %{conn: conn} do
2498 user = insert(:user)
2499 scheduled_at = NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
2500
2501 file = %Plug.Upload{
2502 content_type: "image/jpg",
2503 path: Path.absname("test/fixtures/image.jpg"),
2504 filename: "an_image.jpg"
2505 }
2506
2507 {:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
2508
2509 conn =
2510 conn
2511 |> assign(:user, user)
2512 |> post("/api/v1/statuses", %{
2513 "media_ids" => [to_string(upload.id)],
2514 "status" => "scheduled",
2515 "scheduled_at" => scheduled_at
2516 })
2517
2518 assert %{"media_attachments" => [media_attachment]} = json_response(conn, 200)
2519 assert %{"type" => "image"} = media_attachment
2520 end
2521
2522 test "skips the scheduling and creates the activity if scheduled_at is earlier than 5 minutes from now",
2523 %{conn: conn} do
2524 user = insert(:user)
2525
2526 scheduled_at =
2527 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(5) - 1, :millisecond)
2528
2529 conn =
2530 conn
2531 |> assign(:user, user)
2532 |> post("/api/v1/statuses", %{
2533 "status" => "not scheduled",
2534 "scheduled_at" => scheduled_at
2535 })
2536
2537 assert %{"content" => "not scheduled"} = json_response(conn, 200)
2538 assert [] == Repo.all(ScheduledActivity)
2539 end
2540
2541 test "returns error when daily user limit is exceeded", %{conn: conn} do
2542 user = insert(:user)
2543
2544 today =
2545 NaiveDateTime.utc_now()
2546 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
2547 |> NaiveDateTime.to_iso8601()
2548
2549 attrs = %{params: %{}, scheduled_at: today}
2550 {:ok, _} = ScheduledActivity.create(user, attrs)
2551 {:ok, _} = ScheduledActivity.create(user, attrs)
2552
2553 conn =
2554 conn
2555 |> assign(:user, user)
2556 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => today})
2557
2558 assert %{"error" => "daily limit exceeded"} == json_response(conn, 422)
2559 end
2560
2561 test "returns error when total user limit is exceeded", %{conn: conn} do
2562 user = insert(:user)
2563
2564 today =
2565 NaiveDateTime.utc_now()
2566 |> NaiveDateTime.add(:timer.minutes(6), :millisecond)
2567 |> NaiveDateTime.to_iso8601()
2568
2569 tomorrow =
2570 NaiveDateTime.utc_now()
2571 |> NaiveDateTime.add(:timer.hours(36), :millisecond)
2572 |> NaiveDateTime.to_iso8601()
2573
2574 attrs = %{params: %{}, scheduled_at: today}
2575 {:ok, _} = ScheduledActivity.create(user, attrs)
2576 {:ok, _} = ScheduledActivity.create(user, attrs)
2577 {:ok, _} = ScheduledActivity.create(user, %{params: %{}, scheduled_at: tomorrow})
2578
2579 conn =
2580 conn
2581 |> assign(:user, user)
2582 |> post("/api/v1/statuses", %{"status" => "scheduled", "scheduled_at" => tomorrow})
2583
2584 assert %{"error" => "total limit exceeded"} == json_response(conn, 422)
2585 end
2586
2587 test "shows scheduled activities", %{conn: conn} do
2588 user = insert(:user)
2589 scheduled_activity_id1 = insert(:scheduled_activity, user: user).id |> to_string()
2590 scheduled_activity_id2 = insert(:scheduled_activity, user: user).id |> to_string()
2591 scheduled_activity_id3 = insert(:scheduled_activity, user: user).id |> to_string()
2592 scheduled_activity_id4 = insert(:scheduled_activity, user: user).id |> to_string()
2593
2594 conn =
2595 conn
2596 |> assign(:user, user)
2597
2598 # min_id
2599 conn_res =
2600 conn
2601 |> get("/api/v1/scheduled_statuses?limit=2&min_id=#{scheduled_activity_id1}")
2602
2603 result = json_response(conn_res, 200)
2604 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
2605
2606 # since_id
2607 conn_res =
2608 conn
2609 |> get("/api/v1/scheduled_statuses?limit=2&since_id=#{scheduled_activity_id1}")
2610
2611 result = json_response(conn_res, 200)
2612 assert [%{"id" => ^scheduled_activity_id4}, %{"id" => ^scheduled_activity_id3}] = result
2613
2614 # max_id
2615 conn_res =
2616 conn
2617 |> get("/api/v1/scheduled_statuses?limit=2&max_id=#{scheduled_activity_id4}")
2618
2619 result = json_response(conn_res, 200)
2620 assert [%{"id" => ^scheduled_activity_id3}, %{"id" => ^scheduled_activity_id2}] = result
2621 end
2622
2623 test "shows a scheduled activity", %{conn: conn} do
2624 user = insert(:user)
2625 scheduled_activity = insert(:scheduled_activity, user: user)
2626
2627 res_conn =
2628 conn
2629 |> assign(:user, user)
2630 |> get("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
2631
2632 assert %{"id" => scheduled_activity_id} = json_response(res_conn, 200)
2633 assert scheduled_activity_id == scheduled_activity.id |> to_string()
2634
2635 res_conn =
2636 conn
2637 |> assign(:user, user)
2638 |> get("/api/v1/scheduled_statuses/404")
2639
2640 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
2641 end
2642
2643 test "updates a scheduled activity", %{conn: conn} do
2644 user = insert(:user)
2645 scheduled_activity = insert(:scheduled_activity, user: user)
2646
2647 new_scheduled_at =
2648 NaiveDateTime.add(NaiveDateTime.utc_now(), :timer.minutes(120), :millisecond)
2649
2650 res_conn =
2651 conn
2652 |> assign(:user, user)
2653 |> put("/api/v1/scheduled_statuses/#{scheduled_activity.id}", %{
2654 scheduled_at: new_scheduled_at
2655 })
2656
2657 assert %{"scheduled_at" => expected_scheduled_at} = json_response(res_conn, 200)
2658 assert expected_scheduled_at == Pleroma.Web.CommonAPI.Utils.to_masto_date(new_scheduled_at)
2659
2660 res_conn =
2661 conn
2662 |> assign(:user, user)
2663 |> put("/api/v1/scheduled_statuses/404", %{scheduled_at: new_scheduled_at})
2664
2665 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
2666 end
2667
2668 test "deletes a scheduled activity", %{conn: conn} do
2669 user = insert(:user)
2670 scheduled_activity = insert(:scheduled_activity, user: user)
2671
2672 res_conn =
2673 conn
2674 |> assign(:user, user)
2675 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
2676
2677 assert %{} = json_response(res_conn, 200)
2678 assert nil == Repo.get(ScheduledActivity, scheduled_activity.id)
2679
2680 res_conn =
2681 conn
2682 |> assign(:user, user)
2683 |> delete("/api/v1/scheduled_statuses/#{scheduled_activity.id}")
2684
2685 assert %{"error" => "Record not found"} = json_response(res_conn, 404)
2686 end
2687 end
2688 end