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