added only_media flag to home timeline
[akkoma] / test / pleroma / web / mastodon_api / controllers / timeline_controller_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
6 use Pleroma.Web.ConnCase
7
8 import Pleroma.Factory
9 import Tesla.Mock
10
11 alias Pleroma.User
12 alias Pleroma.Web.CommonAPI
13
14 setup do
15 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
16 :ok
17 end
18
19 describe "home" do
20 setup do: oauth_access(["read:statuses"])
21
22 test "does NOT embed account/pleroma/relationship in statuses", %{
23 user: user,
24 conn: conn
25 } do
26 other_user = insert(:user)
27
28 {:ok, _} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"})
29
30 response =
31 conn
32 |> assign(:user, user)
33 |> get("/api/v1/timelines/home")
34 |> json_response_and_validate_schema(200)
35
36 assert Enum.all?(response, fn n ->
37 get_in(n, ["account", "pleroma", "relationship"]) == %{}
38 end)
39 end
40
41 test "the home timeline when the direct messages are excluded", %{user: user, conn: conn} do
42 {:ok, public_activity} = CommonAPI.post(user, %{status: ".", visibility: "public"})
43 {:ok, direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
44
45 {:ok, unlisted_activity} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"})
46
47 {:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
48
49 conn = get(conn, "/api/v1/timelines/home?exclude_visibilities[]=direct")
50
51 assert status_ids = json_response_and_validate_schema(conn, :ok) |> Enum.map(& &1["id"])
52 assert public_activity.id in status_ids
53 assert unlisted_activity.id in status_ids
54 assert private_activity.id in status_ids
55 refute direct_activity.id in status_ids
56 end
57
58 test "muted emotions", %{user: user, conn: conn} do
59 other_user = insert(:user)
60 {:ok, activity} = CommonAPI.post(user, %{status: "."})
61
62 {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅")
63 User.mute(user, other_user)
64
65 result =
66 conn
67 |> assign(:user, user)
68 |> get("/api/v1/timelines/home")
69 |> json_response_and_validate_schema(200)
70
71 assert [
72 %{
73 "pleroma" => %{
74 "emoji_reactions" => []
75 }
76 }
77 ] = result
78
79 result =
80 conn
81 |> assign(:user, user)
82 |> get("/api/v1/timelines/home?with_muted=true")
83 |> json_response_and_validate_schema(200)
84
85 assert [
86 %{
87 "pleroma" => %{
88 "emoji_reactions" => [%{"count" => 1, "me" => false, "name" => "🎅"}]
89 }
90 }
91 ] = result
92 end
93
94 test "local/remote filtering", %{conn: conn, user: user} do
95 local = insert(:user)
96 remote = insert(:user, local: false)
97
98 {:ok, user, local} = User.follow(user, local)
99 {:ok, _user, remote} = User.follow(user, remote)
100
101 object1 =
102 insert(:note, %{
103 data: %{
104 "to" => ["https://www.w3.org/ns/activitystreams#Public", User.ap_followers(local)]
105 },
106 user: local
107 })
108
109 activity1 =
110 insert(:note_activity, %{
111 note: object1,
112 recipients: ["https://www.w3.org/ns/activitystreams#Public", User.ap_followers(local)],
113 user: local
114 })
115
116 object2 =
117 insert(:note, %{
118 data: %{
119 "to" => ["https://www.w3.org/ns/activitystreams#Public", User.ap_followers(remote)]
120 },
121 user: remote
122 })
123
124 activity2 =
125 insert(:note_activity, %{
126 note: object2,
127 recipients: ["https://www.w3.org/ns/activitystreams#Public", User.ap_followers(remote)],
128 user: remote,
129 local: false
130 })
131
132 resp1 =
133 conn
134 |> get("/api/v1/timelines/home")
135 |> json_response_and_validate_schema(200)
136
137 without_filter_ids = Enum.map(resp1, & &1["id"])
138
139 assert activity1.id in without_filter_ids
140 assert activity2.id in without_filter_ids
141
142 resp2 =
143 conn
144 |> get("/api/v1/timelines/home?local=true")
145 |> json_response_and_validate_schema(200)
146
147 only_local_ids = Enum.map(resp2, & &1["id"])
148
149 assert activity1.id in only_local_ids
150 refute activity2.id in only_local_ids
151
152 resp3 =
153 conn
154 |> get("/api/v1/timelines/home?only_remote=true")
155 |> json_response_and_validate_schema(200)
156
157 only_remote_ids = Enum.map(resp3, & &1["id"])
158
159 refute activity1.id in only_remote_ids
160 assert activity2.id in only_remote_ids
161
162 resp4 =
163 conn
164 |> get("/api/v1/timelines/home?only_remote=true&local=true")
165 |> json_response_and_validate_schema(200)
166
167 assert resp4 == []
168 end
169
170 test "only_media flag", %{conn: conn, user: user} do
171 other = insert(:user)
172 {:ok, _, other} = User.follow(user, other)
173
174 without_media =
175 insert(:note_activity,
176 user: other,
177 recipients: ["https://www.w3.org/ns/activitystreams#Public", User.ap_followers(other)]
178 )
179
180 obj =
181 insert(:note, %{
182 data: %{
183 "attachment" => [
184 %{
185 "mediaType" => "image/jpeg",
186 "name" => "an_image.jpg",
187 "type" => "Document",
188 "url" => [
189 %{
190 "href" =>
191 "http://localhost:4001/media/8270697e-104f-4a54-a7c1-514bb6713f2c/some_image.jpg",
192 "mediaType" => "image/jpeg",
193 "type" => "Link"
194 }
195 ]
196 }
197 ]
198 },
199 user: other
200 })
201
202 with_media =
203 insert(:note_activity, %{
204 note: obj,
205 recipients: ["https://www.w3.org/ns/activitystreams#Public", User.ap_followers(other)],
206 user: other
207 })
208
209 resp1 =
210 conn
211 |> get("/api/v1/timelines/home")
212 |> json_response_and_validate_schema(200)
213
214 without_filter_ids = Enum.map(resp1, & &1["id"])
215
216 assert without_media.id in without_filter_ids
217 assert with_media.id in without_filter_ids
218
219 resp2 =
220 conn
221 |> get("/api/v1/timelines/home?only_media=true")
222 |> json_response_and_validate_schema(200)
223
224 only_media_ids = Enum.map(resp2, & &1["id"])
225
226 refute without_media.id in only_media_ids
227 assert with_media.id in only_media_ids
228 end
229 end
230
231 describe "public" do
232 @tag capture_log: true
233 test "the public timeline", %{conn: conn} do
234 user = insert(:user)
235
236 {:ok, activity} = CommonAPI.post(user, %{status: "test"})
237
238 _activity = insert(:note_activity, local: false)
239
240 conn = get(conn, "/api/v1/timelines/public?local=False")
241
242 assert length(json_response_and_validate_schema(conn, :ok)) == 2
243
244 conn = get(build_conn(), "/api/v1/timelines/public?local=True")
245
246 assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok)
247
248 conn = get(build_conn(), "/api/v1/timelines/public?local=1")
249
250 assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok)
251
252 # does not contain repeats
253 {:ok, _} = CommonAPI.repeat(activity.id, user)
254
255 conn = get(build_conn(), "/api/v1/timelines/public?local=true")
256
257 assert [_] = json_response_and_validate_schema(conn, :ok)
258 end
259
260 test "the public timeline includes only public statuses for an authenticated user" do
261 %{user: user, conn: conn} = oauth_access(["read:statuses"])
262
263 {:ok, _activity} = CommonAPI.post(user, %{status: "test"})
264 {:ok, _activity} = CommonAPI.post(user, %{status: "test", visibility: "private"})
265 {:ok, _activity} = CommonAPI.post(user, %{status: "test", visibility: "unlisted"})
266 {:ok, _activity} = CommonAPI.post(user, %{status: "test", visibility: "direct"})
267
268 res_conn = get(conn, "/api/v1/timelines/public")
269 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
270 end
271
272 test "doesn't return replies if follower is posting with blocked user" do
273 %{conn: conn, user: blocker} = oauth_access(["read:statuses"])
274 [blockee, friend] = insert_list(2, :user)
275 {:ok, blocker, friend} = User.follow(blocker, friend)
276 {:ok, _} = User.block(blocker, blockee)
277
278 conn = assign(conn, :user, blocker)
279
280 {:ok, %{id: activity_id} = activity} = CommonAPI.post(friend, %{status: "hey!"})
281
282 {:ok, reply_from_blockee} =
283 CommonAPI.post(blockee, %{status: "heya", in_reply_to_status_id: activity})
284
285 {:ok, _reply_from_friend} =
286 CommonAPI.post(friend, %{status: "status", in_reply_to_status_id: reply_from_blockee})
287
288 # Still shows replies from yourself
289 {:ok, %{id: reply_from_me}} =
290 CommonAPI.post(blocker, %{status: "status", in_reply_to_status_id: reply_from_blockee})
291
292 response =
293 get(conn, "/api/v1/timelines/public")
294 |> json_response_and_validate_schema(200)
295
296 assert length(response) == 2
297 [%{"id" => ^reply_from_me}, %{"id" => ^activity_id}] = response
298 end
299
300 test "doesn't return replies if follow is posting with users from blocked domain" do
301 %{conn: conn, user: blocker} = oauth_access(["read:statuses"])
302 friend = insert(:user)
303 blockee = insert(:user, ap_id: "https://example.com/users/blocked")
304 {:ok, blocker, friend} = User.follow(blocker, friend)
305 {:ok, blocker} = User.block_domain(blocker, "example.com")
306
307 conn = assign(conn, :user, blocker)
308
309 {:ok, %{id: activity_id} = activity} = CommonAPI.post(friend, %{status: "hey!"})
310
311 {:ok, reply_from_blockee} =
312 CommonAPI.post(blockee, %{status: "heya", in_reply_to_status_id: activity})
313
314 {:ok, _reply_from_friend} =
315 CommonAPI.post(friend, %{status: "status", in_reply_to_status_id: reply_from_blockee})
316
317 res_conn = get(conn, "/api/v1/timelines/public")
318
319 activities = json_response_and_validate_schema(res_conn, 200)
320 [%{"id" => ^activity_id}] = activities
321 end
322
323 test "can be filtered by instance", %{conn: conn} do
324 user = insert(:user, ap_id: "https://lain.com/users/lain")
325 insert(:note_activity, local: false)
326 insert(:note_activity, local: false)
327
328 {:ok, _} = CommonAPI.post(user, %{status: "test"})
329
330 conn = get(conn, "/api/v1/timelines/public?instance=lain.com")
331
332 assert length(json_response_and_validate_schema(conn, :ok)) == 1
333 end
334
335 test "muted emotions", %{conn: conn} do
336 user = insert(:user)
337 token = insert(:oauth_token, user: user, scopes: ["read:statuses"])
338
339 conn =
340 conn
341 |> assign(:user, user)
342 |> assign(:token, token)
343
344 other_user = insert(:user)
345 {:ok, activity} = CommonAPI.post(user, %{status: "."})
346
347 {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅")
348 User.mute(user, other_user)
349
350 result =
351 conn
352 |> get("/api/v1/timelines/public")
353 |> json_response_and_validate_schema(200)
354
355 assert [
356 %{
357 "pleroma" => %{
358 "emoji_reactions" => []
359 }
360 }
361 ] = result
362
363 result =
364 conn
365 |> get("/api/v1/timelines/public?with_muted=true")
366 |> json_response_and_validate_schema(200)
367
368 assert [
369 %{
370 "pleroma" => %{
371 "emoji_reactions" => [%{"count" => 1, "me" => false, "name" => "🎅"}]
372 }
373 }
374 ] = result
375 end
376 end
377
378 defp local_and_remote_activities do
379 insert(:note_activity)
380 insert(:note_activity, local: false)
381 :ok
382 end
383
384 describe "public with restrict unauthenticated timeline for local and federated timelines" do
385 setup do: local_and_remote_activities()
386
387 setup do: clear_config([:restrict_unauthenticated, :timelines, :local], true)
388
389 setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], true)
390
391 test "if user is unauthenticated", %{conn: conn} do
392 res_conn = get(conn, "/api/v1/timelines/public?local=true")
393
394 assert json_response_and_validate_schema(res_conn, :unauthorized) == %{
395 "error" => "authorization required for timeline view"
396 }
397
398 res_conn = get(conn, "/api/v1/timelines/public?local=false")
399
400 assert json_response_and_validate_schema(res_conn, :unauthorized) == %{
401 "error" => "authorization required for timeline view"
402 }
403 end
404
405 test "if user is authenticated" do
406 %{conn: conn} = oauth_access(["read:statuses"])
407
408 res_conn = get(conn, "/api/v1/timelines/public?local=true")
409 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
410
411 res_conn = get(conn, "/api/v1/timelines/public?local=false")
412 assert length(json_response_and_validate_schema(res_conn, 200)) == 2
413 end
414 end
415
416 describe "public with restrict unauthenticated timeline for local" do
417 setup do: local_and_remote_activities()
418
419 setup do: clear_config([:restrict_unauthenticated, :timelines, :local], true)
420
421 test "if user is unauthenticated", %{conn: conn} do
422 res_conn = get(conn, "/api/v1/timelines/public?local=true")
423
424 assert json_response_and_validate_schema(res_conn, :unauthorized) == %{
425 "error" => "authorization required for timeline view"
426 }
427
428 res_conn = get(conn, "/api/v1/timelines/public?local=false")
429 assert length(json_response_and_validate_schema(res_conn, 200)) == 2
430 end
431
432 test "if user is authenticated", %{conn: _conn} do
433 %{conn: conn} = oauth_access(["read:statuses"])
434
435 res_conn = get(conn, "/api/v1/timelines/public?local=true")
436 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
437
438 res_conn = get(conn, "/api/v1/timelines/public?local=false")
439 assert length(json_response_and_validate_schema(res_conn, 200)) == 2
440 end
441 end
442
443 describe "public with restrict unauthenticated timeline for remote" do
444 setup do: local_and_remote_activities()
445
446 setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], true)
447
448 test "if user is unauthenticated", %{conn: conn} do
449 res_conn = get(conn, "/api/v1/timelines/public?local=true")
450 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
451
452 res_conn = get(conn, "/api/v1/timelines/public?local=false")
453
454 assert json_response_and_validate_schema(res_conn, :unauthorized) == %{
455 "error" => "authorization required for timeline view"
456 }
457 end
458
459 test "if user is authenticated", %{conn: _conn} do
460 %{conn: conn} = oauth_access(["read:statuses"])
461
462 res_conn = get(conn, "/api/v1/timelines/public?local=true")
463 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
464
465 res_conn = get(conn, "/api/v1/timelines/public?local=false")
466 assert length(json_response_and_validate_schema(res_conn, 200)) == 2
467 end
468 end
469
470 describe "direct" do
471 test "direct timeline", %{conn: conn} do
472 user_one = insert(:user)
473 user_two = insert(:user)
474
475 {:ok, user_two, user_one} = User.follow(user_two, user_one)
476
477 {:ok, direct} =
478 CommonAPI.post(user_one, %{
479 status: "Hi @#{user_two.nickname}!",
480 visibility: "direct"
481 })
482
483 {:ok, _follower_only} =
484 CommonAPI.post(user_one, %{
485 status: "Hi @#{user_two.nickname}!",
486 visibility: "private"
487 })
488
489 conn_user_two =
490 conn
491 |> assign(:user, user_two)
492 |> assign(:token, insert(:oauth_token, user: user_two, scopes: ["read:statuses"]))
493
494 # Only direct should be visible here
495 res_conn = get(conn_user_two, "api/v1/timelines/direct")
496
497 assert [status] = json_response_and_validate_schema(res_conn, :ok)
498
499 assert %{"visibility" => "direct"} = status
500 assert status["url"] != direct.data["id"]
501
502 # User should be able to see their own direct message
503 res_conn =
504 build_conn()
505 |> assign(:user, user_one)
506 |> assign(:token, insert(:oauth_token, user: user_one, scopes: ["read:statuses"]))
507 |> get("api/v1/timelines/direct")
508
509 [status] = json_response_and_validate_schema(res_conn, :ok)
510
511 assert %{"visibility" => "direct"} = status
512
513 # Both should be visible here
514 res_conn = get(conn_user_two, "api/v1/timelines/home")
515
516 [_s1, _s2] = json_response_and_validate_schema(res_conn, :ok)
517
518 # Test pagination
519 Enum.each(1..20, fn _ ->
520 {:ok, _} =
521 CommonAPI.post(user_one, %{
522 status: "Hi @#{user_two.nickname}!",
523 visibility: "direct"
524 })
525 end)
526
527 res_conn = get(conn_user_two, "api/v1/timelines/direct")
528
529 statuses = json_response_and_validate_schema(res_conn, :ok)
530 assert length(statuses) == 20
531
532 max_id = List.last(statuses)["id"]
533
534 res_conn = get(conn_user_two, "api/v1/timelines/direct?max_id=#{max_id}")
535
536 assert [status] = json_response_and_validate_schema(res_conn, :ok)
537
538 assert status["url"] != direct.data["id"]
539 end
540
541 test "doesn't include DMs from blocked users" do
542 %{user: blocker, conn: conn} = oauth_access(["read:statuses"])
543 blocked = insert(:user)
544 other_user = insert(:user)
545 {:ok, _user_relationship} = User.block(blocker, blocked)
546
547 {:ok, _blocked_direct} =
548 CommonAPI.post(blocked, %{
549 status: "Hi @#{blocker.nickname}!",
550 visibility: "direct"
551 })
552
553 {:ok, direct} =
554 CommonAPI.post(other_user, %{
555 status: "Hi @#{blocker.nickname}!",
556 visibility: "direct"
557 })
558
559 res_conn = get(conn, "api/v1/timelines/direct")
560
561 [status] = json_response_and_validate_schema(res_conn, :ok)
562 assert status["id"] == direct.id
563 end
564 end
565
566 describe "list" do
567 setup do: oauth_access(["read:lists"])
568
569 test "does not contain retoots", %{user: user, conn: conn} do
570 other_user = insert(:user)
571 {:ok, activity_one} = CommonAPI.post(user, %{status: "Marisa is cute."})
572 {:ok, activity_two} = CommonAPI.post(other_user, %{status: "Marisa is stupid."})
573 {:ok, _} = CommonAPI.repeat(activity_one.id, other_user)
574
575 {:ok, list} = Pleroma.List.create("name", user)
576 {:ok, list} = Pleroma.List.follow(list, other_user)
577
578 conn = get(conn, "/api/v1/timelines/list/#{list.id}")
579
580 assert [%{"id" => id}] = json_response_and_validate_schema(conn, :ok)
581
582 assert id == to_string(activity_two.id)
583 end
584
585 test "works with pagination", %{user: user, conn: conn} do
586 other_user = insert(:user)
587 {:ok, list} = Pleroma.List.create("name", user)
588 {:ok, list} = Pleroma.List.follow(list, other_user)
589
590 Enum.each(1..30, fn i ->
591 CommonAPI.post(other_user, %{status: "post number #{i}"})
592 end)
593
594 res =
595 get(conn, "/api/v1/timelines/list/#{list.id}?limit=1")
596 |> json_response_and_validate_schema(:ok)
597
598 assert length(res) == 1
599
600 [first] = res
601
602 res =
603 get(conn, "/api/v1/timelines/list/#{list.id}?max_id=#{first["id"]}&limit=30")
604 |> json_response_and_validate_schema(:ok)
605
606 assert length(res) == 29
607 end
608
609 test "list timeline", %{user: user, conn: conn} do
610 other_user = insert(:user)
611 {:ok, _activity_one} = CommonAPI.post(user, %{status: "Marisa is cute."})
612 {:ok, activity_two} = CommonAPI.post(other_user, %{status: "Marisa is cute."})
613 {:ok, list} = Pleroma.List.create("name", user)
614 {:ok, list} = Pleroma.List.follow(list, other_user)
615
616 conn = get(conn, "/api/v1/timelines/list/#{list.id}")
617
618 assert [%{"id" => id}] = json_response_and_validate_schema(conn, :ok)
619
620 assert id == to_string(activity_two.id)
621 end
622
623 test "list timeline does not leak non-public statuses for unfollowed users", %{
624 user: user,
625 conn: conn
626 } do
627 other_user = insert(:user)
628 {:ok, activity_one} = CommonAPI.post(other_user, %{status: "Marisa is cute."})
629
630 {:ok, _activity_two} =
631 CommonAPI.post(other_user, %{
632 status: "Marisa is cute.",
633 visibility: "private"
634 })
635
636 {:ok, list} = Pleroma.List.create("name", user)
637 {:ok, list} = Pleroma.List.follow(list, other_user)
638
639 conn = get(conn, "/api/v1/timelines/list/#{list.id}")
640
641 assert [%{"id" => id}] = json_response_and_validate_schema(conn, :ok)
642
643 assert id == to_string(activity_one.id)
644 end
645
646 test "muted emotions", %{user: user, conn: conn} do
647 user2 = insert(:user)
648 user3 = insert(:user)
649 {:ok, activity} = CommonAPI.post(user2, %{status: "."})
650
651 {:ok, _} = CommonAPI.react_with_emoji(activity.id, user3, "🎅")
652 User.mute(user, user3)
653
654 {:ok, list} = Pleroma.List.create("name", user)
655 {:ok, list} = Pleroma.List.follow(list, user2)
656
657 result =
658 conn
659 |> get("/api/v1/timelines/list/#{list.id}")
660 |> json_response_and_validate_schema(200)
661
662 assert [
663 %{
664 "pleroma" => %{
665 "emoji_reactions" => []
666 }
667 }
668 ] = result
669
670 result =
671 conn
672 |> get("/api/v1/timelines/list/#{list.id}?with_muted=true")
673 |> json_response_and_validate_schema(200)
674
675 assert [
676 %{
677 "pleroma" => %{
678 "emoji_reactions" => [%{"count" => 1, "me" => false, "name" => "🎅"}]
679 }
680 }
681 ] = result
682 end
683 end
684
685 describe "hashtag" do
686 setup do: oauth_access(["n/a"])
687
688 @tag capture_log: true
689 test "hashtag timeline", %{conn: conn} do
690 following = insert(:user)
691
692 {:ok, activity} = CommonAPI.post(following, %{status: "test #2hu"})
693
694 nconn = get(conn, "/api/v1/timelines/tag/2hu")
695
696 assert [%{"id" => id}] = json_response_and_validate_schema(nconn, :ok)
697
698 assert id == to_string(activity.id)
699
700 # works for different capitalization too
701 nconn = get(conn, "/api/v1/timelines/tag/2HU")
702
703 assert [%{"id" => id}] = json_response_and_validate_schema(nconn, :ok)
704
705 assert id == to_string(activity.id)
706 end
707
708 test "multi-hashtag timeline", %{conn: conn} do
709 user = insert(:user)
710
711 {:ok, activity_test} = CommonAPI.post(user, %{status: "#test"})
712 {:ok, activity_test1} = CommonAPI.post(user, %{status: "#test #test1"})
713 {:ok, activity_none} = CommonAPI.post(user, %{status: "#test #none"})
714
715 any_test = get(conn, "/api/v1/timelines/tag/test?any[]=test1")
716
717 [status_none, status_test1, status_test] = json_response_and_validate_schema(any_test, :ok)
718
719 assert to_string(activity_test.id) == status_test["id"]
720 assert to_string(activity_test1.id) == status_test1["id"]
721 assert to_string(activity_none.id) == status_none["id"]
722
723 restricted_test = get(conn, "/api/v1/timelines/tag/test?all[]=test1&none[]=none")
724
725 assert [status_test1] == json_response_and_validate_schema(restricted_test, :ok)
726
727 all_test = get(conn, "/api/v1/timelines/tag/test?all[]=none")
728
729 assert [status_none] == json_response_and_validate_schema(all_test, :ok)
730 end
731
732 test "muted emotions", %{conn: conn} do
733 user = insert(:user)
734 token = insert(:oauth_token, user: user, scopes: ["read:statuses"])
735
736 conn =
737 conn
738 |> assign(:user, user)
739 |> assign(:token, token)
740
741 other_user = insert(:user)
742 {:ok, activity} = CommonAPI.post(user, %{status: "test #2hu"})
743
744 {:ok, _} = CommonAPI.react_with_emoji(activity.id, other_user, "🎅")
745 User.mute(user, other_user)
746
747 result =
748 conn
749 |> get("/api/v1/timelines/tag/2hu")
750 |> json_response_and_validate_schema(200)
751
752 assert [
753 %{
754 "pleroma" => %{
755 "emoji_reactions" => []
756 }
757 }
758 ] = result
759
760 result =
761 conn
762 |> get("/api/v1/timelines/tag/2hu?with_muted=true")
763 |> json_response_and_validate_schema(200)
764
765 assert [
766 %{
767 "pleroma" => %{
768 "emoji_reactions" => [%{"count" => 1, "me" => false, "name" => "🎅"}]
769 }
770 }
771 ] = result
772 end
773 end
774
775 describe "hashtag timeline handling of :restrict_unauthenticated setting" do
776 setup do
777 user = insert(:user)
778 {:ok, activity1} = CommonAPI.post(user, %{status: "test #tag1"})
779 {:ok, _activity2} = CommonAPI.post(user, %{status: "test #tag1"})
780
781 activity1
782 |> Ecto.Changeset.change(%{local: false})
783 |> Pleroma.Repo.update()
784
785 base_uri = "/api/v1/timelines/tag/tag1"
786 error_response = %{"error" => "authorization required for timeline view"}
787
788 %{base_uri: base_uri, error_response: error_response}
789 end
790
791 defp ensure_authenticated_access(base_uri) do
792 %{conn: auth_conn} = oauth_access(["read:statuses"])
793
794 res_conn = get(auth_conn, "#{base_uri}?local=true")
795 assert length(json_response(res_conn, 200)) == 1
796
797 res_conn = get(auth_conn, "#{base_uri}?local=false")
798 assert length(json_response(res_conn, 200)) == 2
799 end
800
801 test "with default settings on private instances, returns 403 for unauthenticated users", %{
802 conn: conn,
803 base_uri: base_uri,
804 error_response: error_response
805 } do
806 clear_config([:instance, :public], false)
807 clear_config([:restrict_unauthenticated, :timelines])
808
809 for local <- [true, false] do
810 res_conn = get(conn, "#{base_uri}?local=#{local}")
811
812 assert json_response(res_conn, :unauthorized) == error_response
813 end
814
815 ensure_authenticated_access(base_uri)
816 end
817
818 test "with `%{local: true, federated: true}`, returns 403 for unauthenticated users", %{
819 conn: conn,
820 base_uri: base_uri,
821 error_response: error_response
822 } do
823 clear_config([:restrict_unauthenticated, :timelines, :local], true)
824 clear_config([:restrict_unauthenticated, :timelines, :federated], true)
825
826 for local <- [true, false] do
827 res_conn = get(conn, "#{base_uri}?local=#{local}")
828
829 assert json_response(res_conn, :unauthorized) == error_response
830 end
831
832 ensure_authenticated_access(base_uri)
833 end
834
835 test "with `%{local: false, federated: true}`, forbids unauthenticated access to federated timeline",
836 %{conn: conn, base_uri: base_uri, error_response: error_response} do
837 clear_config([:restrict_unauthenticated, :timelines, :local], false)
838 clear_config([:restrict_unauthenticated, :timelines, :federated], true)
839
840 res_conn = get(conn, "#{base_uri}?local=true")
841 assert length(json_response(res_conn, 200)) == 1
842
843 res_conn = get(conn, "#{base_uri}?local=false")
844 assert json_response(res_conn, :unauthorized) == error_response
845
846 ensure_authenticated_access(base_uri)
847 end
848
849 test "with `%{local: true, federated: false}`, forbids unauthenticated access to public timeline" <>
850 "(but not to local public activities which are delivered as part of federated timeline)",
851 %{conn: conn, base_uri: base_uri, error_response: error_response} do
852 clear_config([:restrict_unauthenticated, :timelines, :local], true)
853 clear_config([:restrict_unauthenticated, :timelines, :federated], false)
854
855 res_conn = get(conn, "#{base_uri}?local=true")
856 assert json_response(res_conn, :unauthorized) == error_response
857
858 # Note: local activities get delivered as part of federated timeline
859 res_conn = get(conn, "#{base_uri}?local=false")
860 assert length(json_response(res_conn, 200)) == 2
861
862 ensure_authenticated_access(base_uri)
863 end
864 end
865 end