1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
6 use Pleroma.Web.ConnCase
13 alias Pleroma.Web.CommonAPI
16 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
21 setup do: oauth_access(["read:statuses"])
23 test "does NOT render account/pleroma/relationship if this is disabled by default", %{
27 clear_config([:extensions, :output_relationships_in_statuses_by_default], false)
29 other_user = insert(:user)
31 {:ok, _} = CommonAPI.post(other_user, %{status: "hi @#{user.nickname}"})
35 |> assign(:user, user)
36 |> get("/api/v1/timelines/home")
37 |> json_response_and_validate_schema(200)
39 assert Enum.all?(response, fn n ->
40 get_in(n, ["account", "pleroma", "relationship"]) == %{}
44 test "the home timeline", %{user: user, conn: conn} do
45 uri = "/api/v1/timelines/home?with_relationships=1"
47 following = insert(:user, nickname: "followed")
48 third_user = insert(:user, nickname: "repeated")
50 {:ok, _activity} = CommonAPI.post(following, %{status: "post"})
51 {:ok, activity} = CommonAPI.post(third_user, %{status: "repeated post"})
52 {:ok, _, _} = CommonAPI.repeat(activity.id, following)
54 # This one should not show up in the TL
55 {:ok, _activity} = CommonAPI.post_chat_message(third_user, user, ":gun:")
57 ret_conn = get(conn, uri)
59 assert Enum.empty?(json_response_and_validate_schema(ret_conn, :ok))
61 {:ok, _user} = User.follow(user, following)
63 ret_conn = get(conn, uri)
68 "content" => "repeated post",
71 "relationship" => %{"following" => false, "followed_by" => false}
75 "account" => %{"pleroma" => %{"relationship" => %{"following" => true}}}
81 "pleroma" => %{"relationship" => %{"following" => true}}
84 ] = json_response_and_validate_schema(ret_conn, :ok)
86 {:ok, _user} = User.follow(third_user, user)
88 ret_conn = get(conn, uri)
93 "content" => "repeated post",
97 "relationship" => %{"following" => false, "followed_by" => true}
101 "account" => %{"pleroma" => %{"relationship" => %{"following" => true}}}
106 "acct" => "followed",
107 "pleroma" => %{"relationship" => %{"following" => true}}
110 ] = json_response_and_validate_schema(ret_conn, :ok)
113 test "the home timeline when the direct messages are excluded", %{user: user, conn: conn} do
114 {:ok, public_activity} = CommonAPI.post(user, %{status: ".", visibility: "public"})
115 {:ok, direct_activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
117 {:ok, unlisted_activity} = CommonAPI.post(user, %{status: ".", visibility: "unlisted"})
119 {:ok, private_activity} = CommonAPI.post(user, %{status: ".", visibility: "private"})
121 conn = get(conn, "/api/v1/timelines/home?exclude_visibilities[]=direct")
123 assert status_ids = json_response_and_validate_schema(conn, :ok) |> Enum.map(& &1["id"])
124 assert public_activity.id in status_ids
125 assert unlisted_activity.id in status_ids
126 assert private_activity.id in status_ids
127 refute direct_activity.id in status_ids
132 @tag capture_log: true
133 test "the public timeline", %{conn: conn} do
134 following = insert(:user)
136 {:ok, _activity} = CommonAPI.post(following, %{status: "test"})
138 _activity = insert(:note_activity, local: false)
140 conn = get(conn, "/api/v1/timelines/public?local=False")
142 assert length(json_response_and_validate_schema(conn, :ok)) == 2
144 conn = get(build_conn(), "/api/v1/timelines/public?local=True")
146 assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok)
148 conn = get(build_conn(), "/api/v1/timelines/public?local=1")
150 assert [%{"content" => "test"}] = json_response_and_validate_schema(conn, :ok)
153 test "the public timeline includes only public statuses for an authenticated user" do
154 %{user: user, conn: conn} = oauth_access(["read:statuses"])
156 {:ok, _activity} = CommonAPI.post(user, %{status: "test"})
157 {:ok, _activity} = CommonAPI.post(user, %{status: "test", visibility: "private"})
158 {:ok, _activity} = CommonAPI.post(user, %{status: "test", visibility: "unlisted"})
159 {:ok, _activity} = CommonAPI.post(user, %{status: "test", visibility: "direct"})
161 res_conn = get(conn, "/api/v1/timelines/public")
162 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
166 defp local_and_remote_activities do
167 insert(:note_activity)
168 insert(:note_activity, local: false)
172 describe "public with restrict unauthenticated timeline for local and federated timelines" do
173 setup do: local_and_remote_activities()
175 setup do: clear_config([:restrict_unauthenticated, :timelines, :local], true)
177 setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], true)
179 test "if user is unauthenticated", %{conn: conn} do
180 res_conn = get(conn, "/api/v1/timelines/public?local=true")
182 assert json_response_and_validate_schema(res_conn, :unauthorized) == %{
183 "error" => "authorization required for timeline view"
186 res_conn = get(conn, "/api/v1/timelines/public?local=false")
188 assert json_response_and_validate_schema(res_conn, :unauthorized) == %{
189 "error" => "authorization required for timeline view"
193 test "if user is authenticated" do
194 %{conn: conn} = oauth_access(["read:statuses"])
196 res_conn = get(conn, "/api/v1/timelines/public?local=true")
197 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
199 res_conn = get(conn, "/api/v1/timelines/public?local=false")
200 assert length(json_response_and_validate_schema(res_conn, 200)) == 2
204 describe "public with restrict unauthenticated timeline for local" do
205 setup do: local_and_remote_activities()
207 setup do: clear_config([:restrict_unauthenticated, :timelines, :local], true)
209 test "if user is unauthenticated", %{conn: conn} do
210 res_conn = get(conn, "/api/v1/timelines/public?local=true")
212 assert json_response_and_validate_schema(res_conn, :unauthorized) == %{
213 "error" => "authorization required for timeline view"
216 res_conn = get(conn, "/api/v1/timelines/public?local=false")
217 assert length(json_response_and_validate_schema(res_conn, 200)) == 2
220 test "if user is authenticated", %{conn: _conn} do
221 %{conn: conn} = oauth_access(["read:statuses"])
223 res_conn = get(conn, "/api/v1/timelines/public?local=true")
224 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
226 res_conn = get(conn, "/api/v1/timelines/public?local=false")
227 assert length(json_response_and_validate_schema(res_conn, 200)) == 2
231 describe "public with restrict unauthenticated timeline for remote" do
232 setup do: local_and_remote_activities()
234 setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], true)
236 test "if user is unauthenticated", %{conn: conn} do
237 res_conn = get(conn, "/api/v1/timelines/public?local=true")
238 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
240 res_conn = get(conn, "/api/v1/timelines/public?local=false")
242 assert json_response_and_validate_schema(res_conn, :unauthorized) == %{
243 "error" => "authorization required for timeline view"
247 test "if user is authenticated", %{conn: _conn} do
248 %{conn: conn} = oauth_access(["read:statuses"])
250 res_conn = get(conn, "/api/v1/timelines/public?local=true")
251 assert length(json_response_and_validate_schema(res_conn, 200)) == 1
253 res_conn = get(conn, "/api/v1/timelines/public?local=false")
254 assert length(json_response_and_validate_schema(res_conn, 200)) == 2
259 test "direct timeline", %{conn: conn} do
260 user_one = insert(:user)
261 user_two = insert(:user)
263 {:ok, user_two} = User.follow(user_two, user_one)
266 CommonAPI.post(user_one, %{
267 status: "Hi @#{user_two.nickname}!",
271 {:ok, _follower_only} =
272 CommonAPI.post(user_one, %{
273 status: "Hi @#{user_two.nickname}!",
274 visibility: "private"
279 |> assign(:user, user_two)
280 |> assign(:token, insert(:oauth_token, user: user_two, scopes: ["read:statuses"]))
282 # Only direct should be visible here
283 res_conn = get(conn_user_two, "api/v1/timelines/direct")
285 assert [status] = json_response_and_validate_schema(res_conn, :ok)
287 assert %{"visibility" => "direct"} = status
288 assert status["url"] != direct.data["id"]
290 # User should be able to see their own direct message
293 |> assign(:user, user_one)
294 |> assign(:token, insert(:oauth_token, user: user_one, scopes: ["read:statuses"]))
295 |> get("api/v1/timelines/direct")
297 [status] = json_response_and_validate_schema(res_conn, :ok)
299 assert %{"visibility" => "direct"} = status
301 # Both should be visible here
302 res_conn = get(conn_user_two, "api/v1/timelines/home")
304 [_s1, _s2] = json_response_and_validate_schema(res_conn, :ok)
307 Enum.each(1..20, fn _ ->
309 CommonAPI.post(user_one, %{
310 status: "Hi @#{user_two.nickname}!",
315 res_conn = get(conn_user_two, "api/v1/timelines/direct")
317 statuses = json_response_and_validate_schema(res_conn, :ok)
318 assert length(statuses) == 20
320 max_id = List.last(statuses)["id"]
322 res_conn = get(conn_user_two, "api/v1/timelines/direct?max_id=#{max_id}")
324 assert [status] = json_response_and_validate_schema(res_conn, :ok)
326 assert status["url"] != direct.data["id"]
329 test "doesn't include DMs from blocked users" do
330 %{user: blocker, conn: conn} = oauth_access(["read:statuses"])
331 blocked = insert(:user)
332 other_user = insert(:user)
333 {:ok, _user_relationship} = User.block(blocker, blocked)
335 {:ok, _blocked_direct} =
336 CommonAPI.post(blocked, %{
337 status: "Hi @#{blocker.nickname}!",
342 CommonAPI.post(other_user, %{
343 status: "Hi @#{blocker.nickname}!",
347 res_conn = get(conn, "api/v1/timelines/direct")
349 [status] = json_response_and_validate_schema(res_conn, :ok)
350 assert status["id"] == direct.id
355 setup do: oauth_access(["read:lists"])
357 test "list timeline", %{user: user, conn: conn} do
358 other_user = insert(:user)
359 {:ok, _activity_one} = CommonAPI.post(user, %{status: "Marisa is cute."})
360 {:ok, activity_two} = CommonAPI.post(other_user, %{status: "Marisa is cute."})
361 {:ok, list} = Pleroma.List.create("name", user)
362 {:ok, list} = Pleroma.List.follow(list, other_user)
364 conn = get(conn, "/api/v1/timelines/list/#{list.id}")
366 assert [%{"id" => id}] = json_response_and_validate_schema(conn, :ok)
368 assert id == to_string(activity_two.id)
371 test "list timeline does not leak non-public statuses for unfollowed users", %{
375 other_user = insert(:user)
376 {:ok, activity_one} = CommonAPI.post(other_user, %{status: "Marisa is cute."})
378 {:ok, _activity_two} =
379 CommonAPI.post(other_user, %{
380 status: "Marisa is cute.",
381 visibility: "private"
384 {:ok, list} = Pleroma.List.create("name", user)
385 {:ok, list} = Pleroma.List.follow(list, other_user)
387 conn = get(conn, "/api/v1/timelines/list/#{list.id}")
389 assert [%{"id" => id}] = json_response_and_validate_schema(conn, :ok)
391 assert id == to_string(activity_one.id)
395 describe "hashtag" do
396 setup do: oauth_access(["n/a"])
398 @tag capture_log: true
399 test "hashtag timeline", %{conn: conn} do
400 following = insert(:user)
402 {:ok, activity} = CommonAPI.post(following, %{status: "test #2hu"})
404 nconn = get(conn, "/api/v1/timelines/tag/2hu")
406 assert [%{"id" => id}] = json_response_and_validate_schema(nconn, :ok)
408 assert id == to_string(activity.id)
410 # works for different capitalization too
411 nconn = get(conn, "/api/v1/timelines/tag/2HU")
413 assert [%{"id" => id}] = json_response_and_validate_schema(nconn, :ok)
415 assert id == to_string(activity.id)
418 test "multi-hashtag timeline", %{conn: conn} do
421 {:ok, activity_test} = CommonAPI.post(user, %{status: "#test"})
422 {:ok, activity_test1} = CommonAPI.post(user, %{status: "#test #test1"})
423 {:ok, activity_none} = CommonAPI.post(user, %{status: "#test #none"})
425 any_test = get(conn, "/api/v1/timelines/tag/test?any[]=test1")
427 [status_none, status_test1, status_test] = json_response_and_validate_schema(any_test, :ok)
429 assert to_string(activity_test.id) == status_test["id"]
430 assert to_string(activity_test1.id) == status_test1["id"]
431 assert to_string(activity_none.id) == status_none["id"]
433 restricted_test = get(conn, "/api/v1/timelines/tag/test?all[]=test1&none[]=none")
435 assert [status_test1] == json_response_and_validate_schema(restricted_test, :ok)
437 all_test = get(conn, "/api/v1/timelines/tag/test?all[]=none")
439 assert [status_none] == json_response_and_validate_schema(all_test, :ok)