Merge branch 'remake-remodel' into develop
[akkoma] / test / web / mastodon_api / controllers / notification_controller_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.MastodonAPI.NotificationControllerTest do
6 use Pleroma.Web.ConnCase
7
8 alias Pleroma.Notification
9 alias Pleroma.Repo
10 alias Pleroma.User
11 alias Pleroma.Web.CommonAPI
12
13 import Pleroma.Factory
14
15 test "list of notifications" do
16 %{user: user, conn: conn} = oauth_access(["read:notifications"])
17 other_user = insert(:user)
18
19 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
20
21 {:ok, [_notification]} = Notification.create_notifications(activity)
22
23 conn =
24 conn
25 |> assign(:user, user)
26 |> get("/api/v1/notifications")
27
28 expected_response =
29 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
30 user.ap_id
31 }\" rel=\"ugc\">@<span>#{user.nickname}</span></a></span>"
32
33 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
34 assert response == expected_response
35 end
36
37 test "getting a single notification" do
38 %{user: user, conn: conn} = oauth_access(["read:notifications"])
39 other_user = insert(:user)
40
41 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
42
43 {:ok, [notification]} = Notification.create_notifications(activity)
44
45 conn = get(conn, "/api/v1/notifications/#{notification.id}")
46
47 expected_response =
48 "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
49 user.ap_id
50 }\" rel=\"ugc\">@<span>#{user.nickname}</span></a></span>"
51
52 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
53 assert response == expected_response
54 end
55
56 test "dismissing a single notification" do
57 %{user: user, conn: conn} = oauth_access(["write:notifications"])
58 other_user = insert(:user)
59
60 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
61
62 {:ok, [notification]} = Notification.create_notifications(activity)
63
64 conn =
65 conn
66 |> assign(:user, user)
67 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
68
69 assert %{} = json_response(conn, 200)
70 end
71
72 test "clearing all notifications" do
73 %{user: user, conn: conn} = oauth_access(["write:notifications", "read:notifications"])
74 other_user = insert(:user)
75
76 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
77
78 {:ok, [_notification]} = Notification.create_notifications(activity)
79
80 ret_conn = post(conn, "/api/v1/notifications/clear")
81
82 assert %{} = json_response(ret_conn, 200)
83
84 ret_conn = get(conn, "/api/v1/notifications")
85
86 assert all = json_response(ret_conn, 200)
87 assert all == []
88 end
89
90 test "paginates notifications using min_id, since_id, max_id, and limit" do
91 %{user: user, conn: conn} = oauth_access(["read:notifications"])
92 other_user = insert(:user)
93
94 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
95 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
96 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
97 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
98
99 notification1_id = get_notification_id_by_activity(activity1)
100 notification2_id = get_notification_id_by_activity(activity2)
101 notification3_id = get_notification_id_by_activity(activity3)
102 notification4_id = get_notification_id_by_activity(activity4)
103
104 conn = assign(conn, :user, user)
105
106 # min_id
107 result =
108 conn
109 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
110 |> json_response(:ok)
111
112 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
113
114 # since_id
115 result =
116 conn
117 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
118 |> json_response(:ok)
119
120 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
121
122 # max_id
123 result =
124 conn
125 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
126 |> json_response(:ok)
127
128 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
129 end
130
131 describe "exclude_visibilities" do
132 test "filters notifications for mentions" do
133 %{user: user, conn: conn} = oauth_access(["read:notifications"])
134 other_user = insert(:user)
135
136 {:ok, public_activity} =
137 CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "public"})
138
139 {:ok, direct_activity} =
140 CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "direct"})
141
142 {:ok, unlisted_activity} =
143 CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "unlisted"})
144
145 {:ok, private_activity} =
146 CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "private"})
147
148 conn_res =
149 get(conn, "/api/v1/notifications", %{
150 exclude_visibilities: ["public", "unlisted", "private"]
151 })
152
153 assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
154 assert id == direct_activity.id
155
156 conn_res =
157 get(conn, "/api/v1/notifications", %{
158 exclude_visibilities: ["public", "unlisted", "direct"]
159 })
160
161 assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
162 assert id == private_activity.id
163
164 conn_res =
165 get(conn, "/api/v1/notifications", %{
166 exclude_visibilities: ["public", "private", "direct"]
167 })
168
169 assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
170 assert id == unlisted_activity.id
171
172 conn_res =
173 get(conn, "/api/v1/notifications", %{
174 exclude_visibilities: ["unlisted", "private", "direct"]
175 })
176
177 assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
178 assert id == public_activity.id
179 end
180
181 test "filters notifications for Like activities" do
182 user = insert(:user)
183 %{user: other_user, conn: conn} = oauth_access(["read:notifications"])
184
185 {:ok, public_activity} =
186 CommonAPI.post(other_user, %{"status" => ".", "visibility" => "public"})
187
188 {:ok, direct_activity} =
189 CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "direct"})
190
191 {:ok, unlisted_activity} =
192 CommonAPI.post(other_user, %{"status" => ".", "visibility" => "unlisted"})
193
194 {:ok, private_activity} =
195 CommonAPI.post(other_user, %{"status" => ".", "visibility" => "private"})
196
197 {:ok, _} = CommonAPI.favorite(user, public_activity.id)
198 {:ok, _} = CommonAPI.favorite(user, direct_activity.id)
199 {:ok, _} = CommonAPI.favorite(user, unlisted_activity.id)
200 {:ok, _} = CommonAPI.favorite(user, private_activity.id)
201
202 activity_ids =
203 conn
204 |> get("/api/v1/notifications", %{exclude_visibilities: ["direct"]})
205 |> json_response(200)
206 |> Enum.map(& &1["status"]["id"])
207
208 assert public_activity.id in activity_ids
209 assert unlisted_activity.id in activity_ids
210 assert private_activity.id in activity_ids
211 refute direct_activity.id in activity_ids
212
213 activity_ids =
214 conn
215 |> get("/api/v1/notifications", %{exclude_visibilities: ["unlisted"]})
216 |> json_response(200)
217 |> Enum.map(& &1["status"]["id"])
218
219 assert public_activity.id in activity_ids
220 refute unlisted_activity.id in activity_ids
221 assert private_activity.id in activity_ids
222 assert direct_activity.id in activity_ids
223
224 activity_ids =
225 conn
226 |> get("/api/v1/notifications", %{exclude_visibilities: ["private"]})
227 |> json_response(200)
228 |> Enum.map(& &1["status"]["id"])
229
230 assert public_activity.id in activity_ids
231 assert unlisted_activity.id in activity_ids
232 refute private_activity.id in activity_ids
233 assert direct_activity.id in activity_ids
234
235 activity_ids =
236 conn
237 |> get("/api/v1/notifications", %{exclude_visibilities: ["public"]})
238 |> json_response(200)
239 |> Enum.map(& &1["status"]["id"])
240
241 refute public_activity.id in activity_ids
242 assert unlisted_activity.id in activity_ids
243 assert private_activity.id in activity_ids
244 assert direct_activity.id in activity_ids
245 end
246
247 test "filters notifications for Announce activities" do
248 user = insert(:user)
249 %{user: other_user, conn: conn} = oauth_access(["read:notifications"])
250
251 {:ok, public_activity} =
252 CommonAPI.post(other_user, %{"status" => ".", "visibility" => "public"})
253
254 {:ok, unlisted_activity} =
255 CommonAPI.post(other_user, %{"status" => ".", "visibility" => "unlisted"})
256
257 {:ok, _, _} = CommonAPI.repeat(public_activity.id, user)
258 {:ok, _, _} = CommonAPI.repeat(unlisted_activity.id, user)
259
260 activity_ids =
261 conn
262 |> get("/api/v1/notifications", %{exclude_visibilities: ["unlisted"]})
263 |> json_response(200)
264 |> Enum.map(& &1["status"]["id"])
265
266 assert public_activity.id in activity_ids
267 refute unlisted_activity.id in activity_ids
268 end
269 end
270
271 test "filters notifications using exclude_types" do
272 %{user: user, conn: conn} = oauth_access(["read:notifications"])
273 other_user = insert(:user)
274
275 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
276 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
277 {:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id)
278 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
279 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
280
281 mention_notification_id = get_notification_id_by_activity(mention_activity)
282 favorite_notification_id = get_notification_id_by_activity(favorite_activity)
283 reblog_notification_id = get_notification_id_by_activity(reblog_activity)
284 follow_notification_id = get_notification_id_by_activity(follow_activity)
285
286 conn_res =
287 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
288
289 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
290
291 conn_res =
292 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
293
294 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
295
296 conn_res =
297 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
298
299 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
300
301 conn_res =
302 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
303
304 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
305 end
306
307 test "destroy multiple" do
308 %{user: user, conn: conn} = oauth_access(["read:notifications", "write:notifications"])
309 other_user = insert(:user)
310
311 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
312 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
313 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
314 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
315
316 notification1_id = get_notification_id_by_activity(activity1)
317 notification2_id = get_notification_id_by_activity(activity2)
318 notification3_id = get_notification_id_by_activity(activity3)
319 notification4_id = get_notification_id_by_activity(activity4)
320
321 result =
322 conn
323 |> get("/api/v1/notifications")
324 |> json_response(:ok)
325
326 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
327
328 conn2 =
329 conn
330 |> assign(:user, other_user)
331 |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:notifications"]))
332
333 result =
334 conn2
335 |> get("/api/v1/notifications")
336 |> json_response(:ok)
337
338 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
339
340 conn_destroy =
341 conn
342 |> delete("/api/v1/notifications/destroy_multiple", %{
343 "ids" => [notification1_id, notification2_id]
344 })
345
346 assert json_response(conn_destroy, 200) == %{}
347
348 result =
349 conn2
350 |> get("/api/v1/notifications")
351 |> json_response(:ok)
352
353 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
354 end
355
356 test "doesn't see notifications after muting user with notifications" do
357 %{user: user, conn: conn} = oauth_access(["read:notifications"])
358 user2 = insert(:user)
359
360 {:ok, _, _, _} = CommonAPI.follow(user, user2)
361 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
362
363 ret_conn = get(conn, "/api/v1/notifications")
364
365 assert length(json_response(ret_conn, 200)) == 1
366
367 {:ok, _user_relationships} = User.mute(user, user2)
368
369 conn = get(conn, "/api/v1/notifications")
370
371 assert json_response(conn, 200) == []
372 end
373
374 test "see notifications after muting user without notifications" do
375 %{user: user, conn: conn} = oauth_access(["read:notifications"])
376 user2 = insert(:user)
377
378 {:ok, _, _, _} = CommonAPI.follow(user, user2)
379 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
380
381 ret_conn = get(conn, "/api/v1/notifications")
382
383 assert length(json_response(ret_conn, 200)) == 1
384
385 {:ok, _user_relationships} = User.mute(user, user2, false)
386
387 conn = get(conn, "/api/v1/notifications")
388
389 assert length(json_response(conn, 200)) == 1
390 end
391
392 test "see notifications after muting user with notifications and with_muted parameter" do
393 %{user: user, conn: conn} = oauth_access(["read:notifications"])
394 user2 = insert(:user)
395
396 {:ok, _, _, _} = CommonAPI.follow(user, user2)
397 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
398
399 ret_conn = get(conn, "/api/v1/notifications")
400
401 assert length(json_response(ret_conn, 200)) == 1
402
403 {:ok, _user_relationships} = User.mute(user, user2)
404
405 conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
406
407 assert length(json_response(conn, 200)) == 1
408 end
409
410 test "see move notifications with `with_move` parameter" do
411 old_user = insert(:user)
412 new_user = insert(:user, also_known_as: [old_user.ap_id])
413 %{user: follower, conn: conn} = oauth_access(["read:notifications"])
414
415 User.follow(follower, old_user)
416 Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user)
417 Pleroma.Tests.ObanHelpers.perform_all()
418
419 ret_conn = get(conn, "/api/v1/notifications")
420
421 assert json_response(ret_conn, 200) == []
422
423 conn = get(conn, "/api/v1/notifications", %{"with_move" => "true"})
424
425 assert length(json_response(conn, 200)) == 1
426 end
427
428 describe "link headers" do
429 test "preserves parameters in link headers" do
430 %{user: user, conn: conn} = oauth_access(["read:notifications"])
431 other_user = insert(:user)
432
433 {:ok, activity1} =
434 CommonAPI.post(other_user, %{
435 "status" => "hi @#{user.nickname}",
436 "visibility" => "public"
437 })
438
439 {:ok, activity2} =
440 CommonAPI.post(other_user, %{
441 "status" => "hi @#{user.nickname}",
442 "visibility" => "public"
443 })
444
445 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
446 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
447
448 conn =
449 conn
450 |> assign(:user, user)
451 |> get("/api/v1/notifications", %{media_only: true})
452
453 assert [link_header] = get_resp_header(conn, "link")
454 assert link_header =~ ~r/media_only=true/
455 assert link_header =~ ~r/min_id=#{notification2.id}/
456 assert link_header =~ ~r/max_id=#{notification1.id}/
457 end
458 end
459
460 describe "from specified user" do
461 test "account_id" do
462 %{user: user, conn: conn} = oauth_access(["read:notifications"])
463
464 %{id: account_id} = other_user1 = insert(:user)
465 other_user2 = insert(:user)
466
467 {:ok, _activity} = CommonAPI.post(other_user1, %{"status" => "hi @#{user.nickname}"})
468 {:ok, _activity} = CommonAPI.post(other_user2, %{"status" => "bye @#{user.nickname}"})
469
470 assert [%{"account" => %{"id" => ^account_id}}] =
471 conn
472 |> assign(:user, user)
473 |> get("/api/v1/notifications", %{account_id: account_id})
474 |> json_response(200)
475
476 assert %{"error" => "Account is not found"} =
477 conn
478 |> assign(:user, user)
479 |> get("/api/v1/notifications", %{account_id: "cofe"})
480 |> json_response(404)
481 end
482 end
483
484 defp get_notification_id_by_activity(%{id: id}) do
485 Notification
486 |> Repo.get_by(activity_id: id)
487 |> Map.get(:id)
488 |> to_string()
489 end
490 end