Merge branch 'exclude-visibilities-for-timelines' into 'develop'
[akkoma] / test / web / mastodon_api / controllers / timeline_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.TimelineControllerTest do
6 use Pleroma.Web.ConnCase
7
8 import Pleroma.Factory
9 import Tesla.Mock
10
11 alias Pleroma.Config
12 alias Pleroma.User
13 alias Pleroma.Web.CommonAPI
14 alias Pleroma.Web.OStatus
15
16 clear_config([:instance, :public])
17
18 setup do
19 mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
20 :ok
21 end
22
23 describe "home" do
24 test "the home timeline", %{conn: conn} do
25 user = insert(:user)
26 following = insert(:user)
27
28 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
29
30 conn =
31 conn
32 |> assign(:user, user)
33 |> get("/api/v1/timelines/home")
34
35 assert Enum.empty?(json_response(conn, :ok))
36
37 {:ok, user} = User.follow(user, following)
38
39 conn =
40 build_conn()
41 |> assign(:user, user)
42 |> get("/api/v1/timelines/home")
43
44 assert [%{"content" => "test"}] = json_response(conn, :ok)
45 end
46
47 test "the home timeline when the direct messages are excluded", %{conn: conn} do
48 user = insert(:user)
49 {:ok, public_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "public"})
50 {:ok, direct_activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
51
52 {:ok, unlisted_activity} =
53 CommonAPI.post(user, %{"status" => ".", "visibility" => "unlisted"})
54
55 {:ok, private_activity} =
56 CommonAPI.post(user, %{"status" => ".", "visibility" => "private"})
57
58 conn =
59 conn
60 |> assign(:user, user)
61 |> get("/api/v1/timelines/home", %{"exclude_visibilities" => ["direct"]})
62
63 assert status_ids = json_response(conn, :ok) |> Enum.map(& &1["id"])
64 assert public_activity.id in status_ids
65 assert unlisted_activity.id in status_ids
66 assert private_activity.id in status_ids
67 refute direct_activity.id in status_ids
68 end
69 end
70
71 describe "public" do
72 @tag capture_log: true
73 test "the public timeline", %{conn: conn} do
74 following = insert(:user)
75
76 {:ok, _activity} = CommonAPI.post(following, %{"status" => "test"})
77
78 {:ok, [_activity]} =
79 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
80
81 conn = get(conn, "/api/v1/timelines/public", %{"local" => "False"})
82
83 assert length(json_response(conn, :ok)) == 2
84
85 conn = get(build_conn(), "/api/v1/timelines/public", %{"local" => "True"})
86
87 assert [%{"content" => "test"}] = json_response(conn, :ok)
88
89 conn = get(build_conn(), "/api/v1/timelines/public", %{"local" => "1"})
90
91 assert [%{"content" => "test"}] = json_response(conn, :ok)
92 end
93
94 test "the public timeline when public is set to false", %{conn: conn} do
95 Config.put([:instance, :public], false)
96
97 assert %{"error" => "This resource requires authentication."} ==
98 conn
99 |> get("/api/v1/timelines/public", %{"local" => "False"})
100 |> json_response(:forbidden)
101 end
102
103 test "the public timeline includes only public statuses for an authenticated user" do
104 user = insert(:user)
105
106 conn =
107 build_conn()
108 |> assign(:user, user)
109
110 {:ok, _activity} = CommonAPI.post(user, %{"status" => "test"})
111 {:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "private"})
112 {:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "unlisted"})
113 {:ok, _activity} = CommonAPI.post(user, %{"status" => "test", "visibility" => "direct"})
114
115 res_conn = get(conn, "/api/v1/timelines/public")
116 assert length(json_response(res_conn, 200)) == 1
117 end
118 end
119
120 describe "direct" do
121 test "direct timeline", %{conn: conn} do
122 user_one = insert(:user)
123 user_two = insert(:user)
124
125 {:ok, user_two} = User.follow(user_two, user_one)
126
127 {:ok, direct} =
128 CommonAPI.post(user_one, %{
129 "status" => "Hi @#{user_two.nickname}!",
130 "visibility" => "direct"
131 })
132
133 {:ok, _follower_only} =
134 CommonAPI.post(user_one, %{
135 "status" => "Hi @#{user_two.nickname}!",
136 "visibility" => "private"
137 })
138
139 # Only direct should be visible here
140 res_conn =
141 conn
142 |> assign(:user, user_two)
143 |> get("api/v1/timelines/direct")
144
145 [status] = json_response(res_conn, :ok)
146
147 assert %{"visibility" => "direct"} = status
148 assert status["url"] != direct.data["id"]
149
150 # User should be able to see their own direct message
151 res_conn =
152 build_conn()
153 |> assign(:user, user_one)
154 |> get("api/v1/timelines/direct")
155
156 [status] = json_response(res_conn, :ok)
157
158 assert %{"visibility" => "direct"} = status
159
160 # Both should be visible here
161 res_conn =
162 conn
163 |> assign(:user, user_two)
164 |> get("api/v1/timelines/home")
165
166 [_s1, _s2] = json_response(res_conn, :ok)
167
168 # Test pagination
169 Enum.each(1..20, fn _ ->
170 {:ok, _} =
171 CommonAPI.post(user_one, %{
172 "status" => "Hi @#{user_two.nickname}!",
173 "visibility" => "direct"
174 })
175 end)
176
177 res_conn =
178 conn
179 |> assign(:user, user_two)
180 |> get("api/v1/timelines/direct")
181
182 statuses = json_response(res_conn, :ok)
183 assert length(statuses) == 20
184
185 res_conn =
186 conn
187 |> assign(:user, user_two)
188 |> get("api/v1/timelines/direct", %{max_id: List.last(statuses)["id"]})
189
190 [status] = json_response(res_conn, :ok)
191
192 assert status["url"] != direct.data["id"]
193 end
194
195 test "doesn't include DMs from blocked users", %{conn: conn} do
196 blocker = insert(:user)
197 blocked = insert(:user)
198 user = insert(:user)
199 {:ok, blocker} = User.block(blocker, blocked)
200
201 {:ok, _blocked_direct} =
202 CommonAPI.post(blocked, %{
203 "status" => "Hi @#{blocker.nickname}!",
204 "visibility" => "direct"
205 })
206
207 {:ok, direct} =
208 CommonAPI.post(user, %{
209 "status" => "Hi @#{blocker.nickname}!",
210 "visibility" => "direct"
211 })
212
213 res_conn =
214 conn
215 |> assign(:user, user)
216 |> get("api/v1/timelines/direct")
217
218 [status] = json_response(res_conn, :ok)
219 assert status["id"] == direct.id
220 end
221 end
222
223 describe "list" do
224 test "list timeline", %{conn: conn} do
225 user = insert(:user)
226 other_user = insert(:user)
227 {:ok, _activity_one} = CommonAPI.post(user, %{"status" => "Marisa is cute."})
228 {:ok, activity_two} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
229 {:ok, list} = Pleroma.List.create("name", user)
230 {:ok, list} = Pleroma.List.follow(list, other_user)
231
232 conn =
233 conn
234 |> assign(:user, user)
235 |> get("/api/v1/timelines/list/#{list.id}")
236
237 assert [%{"id" => id}] = json_response(conn, :ok)
238
239 assert id == to_string(activity_two.id)
240 end
241
242 test "list timeline does not leak non-public statuses for unfollowed users", %{conn: conn} do
243 user = insert(:user)
244 other_user = insert(:user)
245 {:ok, activity_one} = CommonAPI.post(other_user, %{"status" => "Marisa is cute."})
246
247 {:ok, _activity_two} =
248 CommonAPI.post(other_user, %{
249 "status" => "Marisa is cute.",
250 "visibility" => "private"
251 })
252
253 {:ok, list} = Pleroma.List.create("name", user)
254 {:ok, list} = Pleroma.List.follow(list, other_user)
255
256 conn =
257 conn
258 |> assign(:user, user)
259 |> get("/api/v1/timelines/list/#{list.id}")
260
261 assert [%{"id" => id}] = json_response(conn, :ok)
262
263 assert id == to_string(activity_one.id)
264 end
265 end
266
267 describe "hashtag" do
268 @tag capture_log: true
269 test "hashtag timeline", %{conn: conn} do
270 following = insert(:user)
271
272 {:ok, activity} = CommonAPI.post(following, %{"status" => "test #2hu"})
273
274 {:ok, [_activity]} =
275 OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
276
277 nconn = get(conn, "/api/v1/timelines/tag/2hu")
278
279 assert [%{"id" => id}] = json_response(nconn, :ok)
280
281 assert id == to_string(activity.id)
282
283 # works for different capitalization too
284 nconn = get(conn, "/api/v1/timelines/tag/2HU")
285
286 assert [%{"id" => id}] = json_response(nconn, :ok)
287
288 assert id == to_string(activity.id)
289 end
290
291 test "multi-hashtag timeline", %{conn: conn} do
292 user = insert(:user)
293
294 {:ok, activity_test} = CommonAPI.post(user, %{"status" => "#test"})
295 {:ok, activity_test1} = CommonAPI.post(user, %{"status" => "#test #test1"})
296 {:ok, activity_none} = CommonAPI.post(user, %{"status" => "#test #none"})
297
298 any_test = get(conn, "/api/v1/timelines/tag/test", %{"any" => ["test1"]})
299
300 [status_none, status_test1, status_test] = json_response(any_test, :ok)
301
302 assert to_string(activity_test.id) == status_test["id"]
303 assert to_string(activity_test1.id) == status_test1["id"]
304 assert to_string(activity_none.id) == status_none["id"]
305
306 restricted_test =
307 get(conn, "/api/v1/timelines/tag/test", %{"all" => ["test1"], "none" => ["none"]})
308
309 assert [status_test1] == json_response(restricted_test, :ok)
310
311 all_test = get(conn, "/api/v1/timelines/tag/test", %{"all" => ["none"]})
312
313 assert [status_none] == json_response(all_test, :ok)
314 end
315 end
316 end