Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma 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 "does NOT render account/pleroma/relationship if this is disabled by default" do
16 clear_config([:extensions, :output_relationships_in_statuses_by_default], false)
17
18 %{user: user, conn: conn} = oauth_access(["read:notifications"])
19 other_user = insert(:user)
20
21 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
22 {:ok, [_notification]} = Notification.create_notifications(activity)
23
24 response =
25 conn
26 |> assign(:user, user)
27 |> get("/api/v1/notifications")
28 |> json_response(200)
29
30 assert Enum.all?(response, fn n ->
31 get_in(n, ["account", "pleroma", "relationship"]) == %{}
32 end)
33 end
34
35 test "list of notifications" do
36 %{user: user, conn: conn} = oauth_access(["read:notifications"])
37 other_user = insert(:user)
38
39 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
40
41 {:ok, [_notification]} = Notification.create_notifications(activity)
42
43 conn =
44 conn
45 |> assign(:user, user)
46 |> get("/api/v1/notifications")
47
48 expected_response =
49 "hi <span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{user.id}\" href=\"#{
50 user.ap_id
51 }\" rel=\"ugc\">@<span>#{user.nickname}</span></a></span>"
52
53 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
54 assert response == expected_response
55 end
56
57 test "getting a single notification" do
58 %{user: user, conn: conn} = oauth_access(["read:notifications"])
59 other_user = insert(:user)
60
61 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
62
63 {:ok, [notification]} = Notification.create_notifications(activity)
64
65 conn = get(conn, "/api/v1/notifications/#{notification.id}")
66
67 expected_response =
68 "hi <span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{user.id}\" href=\"#{
69 user.ap_id
70 }\" rel=\"ugc\">@<span>#{user.nickname}</span></a></span>"
71
72 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
73 assert response == expected_response
74 end
75
76 test "dismissing a single notification (deprecated endpoint)" do
77 %{user: user, conn: conn} = oauth_access(["write:notifications"])
78 other_user = insert(:user)
79
80 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
81
82 {:ok, [notification]} = Notification.create_notifications(activity)
83
84 conn =
85 conn
86 |> assign(:user, user)
87 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
88
89 assert %{} = json_response(conn, 200)
90 end
91
92 test "dismissing a single notification" do
93 %{user: user, conn: conn} = oauth_access(["write:notifications"])
94 other_user = insert(:user)
95
96 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
97
98 {:ok, [notification]} = Notification.create_notifications(activity)
99
100 conn =
101 conn
102 |> assign(:user, user)
103 |> post("/api/v1/notifications/#{notification.id}/dismiss")
104
105 assert %{} = json_response(conn, 200)
106 end
107
108 test "clearing all notifications" do
109 %{user: user, conn: conn} = oauth_access(["write:notifications", "read:notifications"])
110 other_user = insert(:user)
111
112 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
113
114 {:ok, [_notification]} = Notification.create_notifications(activity)
115
116 ret_conn = post(conn, "/api/v1/notifications/clear")
117
118 assert %{} = json_response(ret_conn, 200)
119
120 ret_conn = get(conn, "/api/v1/notifications")
121
122 assert all = json_response(ret_conn, 200)
123 assert all == []
124 end
125
126 test "paginates notifications using min_id, since_id, max_id, and limit" do
127 %{user: user, conn: conn} = oauth_access(["read:notifications"])
128 other_user = insert(:user)
129
130 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
131 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
132 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
133 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
134
135 notification1_id = get_notification_id_by_activity(activity1)
136 notification2_id = get_notification_id_by_activity(activity2)
137 notification3_id = get_notification_id_by_activity(activity3)
138 notification4_id = get_notification_id_by_activity(activity4)
139
140 conn = assign(conn, :user, user)
141
142 # min_id
143 result =
144 conn
145 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
146 |> json_response(:ok)
147
148 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
149
150 # since_id
151 result =
152 conn
153 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
154 |> json_response(:ok)
155
156 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
157
158 # max_id
159 result =
160 conn
161 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
162 |> json_response(:ok)
163
164 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
165 end
166
167 describe "exclude_visibilities" do
168 test "filters notifications for mentions" do
169 %{user: user, conn: conn} = oauth_access(["read:notifications"])
170 other_user = insert(:user)
171
172 {:ok, public_activity} =
173 CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "public"})
174
175 {:ok, direct_activity} =
176 CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "direct"})
177
178 {:ok, unlisted_activity} =
179 CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "unlisted"})
180
181 {:ok, private_activity} =
182 CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "private"})
183
184 conn_res =
185 get(conn, "/api/v1/notifications", %{
186 exclude_visibilities: ["public", "unlisted", "private"]
187 })
188
189 assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
190 assert id == direct_activity.id
191
192 conn_res =
193 get(conn, "/api/v1/notifications", %{
194 exclude_visibilities: ["public", "unlisted", "direct"]
195 })
196
197 assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
198 assert id == private_activity.id
199
200 conn_res =
201 get(conn, "/api/v1/notifications", %{
202 exclude_visibilities: ["public", "private", "direct"]
203 })
204
205 assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
206 assert id == unlisted_activity.id
207
208 conn_res =
209 get(conn, "/api/v1/notifications", %{
210 exclude_visibilities: ["unlisted", "private", "direct"]
211 })
212
213 assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
214 assert id == public_activity.id
215 end
216
217 test "filters notifications for Like activities" do
218 user = insert(:user)
219 %{user: other_user, conn: conn} = oauth_access(["read:notifications"])
220
221 {:ok, public_activity} =
222 CommonAPI.post(other_user, %{"status" => ".", "visibility" => "public"})
223
224 {:ok, direct_activity} =
225 CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "direct"})
226
227 {:ok, unlisted_activity} =
228 CommonAPI.post(other_user, %{"status" => ".", "visibility" => "unlisted"})
229
230 {:ok, private_activity} =
231 CommonAPI.post(other_user, %{"status" => ".", "visibility" => "private"})
232
233 {:ok, _} = CommonAPI.favorite(user, public_activity.id)
234 {:ok, _} = CommonAPI.favorite(user, direct_activity.id)
235 {:ok, _} = CommonAPI.favorite(user, unlisted_activity.id)
236 {:ok, _} = CommonAPI.favorite(user, private_activity.id)
237
238 activity_ids =
239 conn
240 |> get("/api/v1/notifications", %{exclude_visibilities: ["direct"]})
241 |> json_response(200)
242 |> Enum.map(& &1["status"]["id"])
243
244 assert public_activity.id in activity_ids
245 assert unlisted_activity.id in activity_ids
246 assert private_activity.id in activity_ids
247 refute direct_activity.id in activity_ids
248
249 activity_ids =
250 conn
251 |> get("/api/v1/notifications", %{exclude_visibilities: ["unlisted"]})
252 |> json_response(200)
253 |> Enum.map(& &1["status"]["id"])
254
255 assert public_activity.id in activity_ids
256 refute unlisted_activity.id in activity_ids
257 assert private_activity.id in activity_ids
258 assert direct_activity.id in activity_ids
259
260 activity_ids =
261 conn
262 |> get("/api/v1/notifications", %{exclude_visibilities: ["private"]})
263 |> json_response(200)
264 |> Enum.map(& &1["status"]["id"])
265
266 assert public_activity.id in activity_ids
267 assert unlisted_activity.id in activity_ids
268 refute private_activity.id in activity_ids
269 assert direct_activity.id in activity_ids
270
271 activity_ids =
272 conn
273 |> get("/api/v1/notifications", %{exclude_visibilities: ["public"]})
274 |> json_response(200)
275 |> Enum.map(& &1["status"]["id"])
276
277 refute public_activity.id in activity_ids
278 assert unlisted_activity.id in activity_ids
279 assert private_activity.id in activity_ids
280 assert direct_activity.id in activity_ids
281 end
282
283 test "filters notifications for Announce activities" do
284 user = insert(:user)
285 %{user: other_user, conn: conn} = oauth_access(["read:notifications"])
286
287 {:ok, public_activity} =
288 CommonAPI.post(other_user, %{"status" => ".", "visibility" => "public"})
289
290 {:ok, unlisted_activity} =
291 CommonAPI.post(other_user, %{"status" => ".", "visibility" => "unlisted"})
292
293 {:ok, _, _} = CommonAPI.repeat(public_activity.id, user)
294 {:ok, _, _} = CommonAPI.repeat(unlisted_activity.id, user)
295
296 activity_ids =
297 conn
298 |> get("/api/v1/notifications", %{exclude_visibilities: ["unlisted"]})
299 |> json_response(200)
300 |> Enum.map(& &1["status"]["id"])
301
302 assert public_activity.id in activity_ids
303 refute unlisted_activity.id in activity_ids
304 end
305 end
306
307 test "filters notifications using exclude_types" do
308 %{user: user, conn: conn} = oauth_access(["read:notifications"])
309 other_user = insert(:user)
310
311 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
312 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
313 {:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id)
314 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
315 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
316
317 mention_notification_id = get_notification_id_by_activity(mention_activity)
318 favorite_notification_id = get_notification_id_by_activity(favorite_activity)
319 reblog_notification_id = get_notification_id_by_activity(reblog_activity)
320 follow_notification_id = get_notification_id_by_activity(follow_activity)
321
322 conn_res =
323 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
324
325 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
326
327 conn_res =
328 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
329
330 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
331
332 conn_res =
333 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
334
335 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
336
337 conn_res =
338 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
339
340 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
341 end
342
343 test "filters notifications using include_types" do
344 %{user: user, conn: conn} = oauth_access(["read:notifications"])
345 other_user = insert(:user)
346
347 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
348 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
349 {:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id)
350 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
351 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
352
353 mention_notification_id = get_notification_id_by_activity(mention_activity)
354 favorite_notification_id = get_notification_id_by_activity(favorite_activity)
355 reblog_notification_id = get_notification_id_by_activity(reblog_activity)
356 follow_notification_id = get_notification_id_by_activity(follow_activity)
357
358 conn_res = get(conn, "/api/v1/notifications", %{include_types: ["follow"]})
359
360 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
361
362 conn_res = get(conn, "/api/v1/notifications", %{include_types: ["mention"]})
363
364 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
365
366 conn_res = get(conn, "/api/v1/notifications", %{include_types: ["favourite"]})
367
368 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
369
370 conn_res = get(conn, "/api/v1/notifications", %{include_types: ["reblog"]})
371
372 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
373
374 result = conn |> get("/api/v1/notifications") |> json_response(200)
375
376 assert length(result) == 4
377
378 result =
379 conn
380 |> get("/api/v1/notifications", %{
381 include_types: ["follow", "mention", "favourite", "reblog"]
382 })
383 |> json_response(200)
384
385 assert length(result) == 4
386 end
387
388 test "destroy multiple" do
389 %{user: user, conn: conn} = oauth_access(["read:notifications", "write:notifications"])
390 other_user = insert(:user)
391
392 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
393 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
394 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
395 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
396
397 notification1_id = get_notification_id_by_activity(activity1)
398 notification2_id = get_notification_id_by_activity(activity2)
399 notification3_id = get_notification_id_by_activity(activity3)
400 notification4_id = get_notification_id_by_activity(activity4)
401
402 result =
403 conn
404 |> get("/api/v1/notifications")
405 |> json_response(:ok)
406
407 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
408
409 conn2 =
410 conn
411 |> assign(:user, other_user)
412 |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:notifications"]))
413
414 result =
415 conn2
416 |> get("/api/v1/notifications")
417 |> json_response(:ok)
418
419 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
420
421 conn_destroy =
422 conn
423 |> delete("/api/v1/notifications/destroy_multiple", %{
424 "ids" => [notification1_id, notification2_id]
425 })
426
427 assert json_response(conn_destroy, 200) == %{}
428
429 result =
430 conn2
431 |> get("/api/v1/notifications")
432 |> json_response(:ok)
433
434 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
435 end
436
437 test "doesn't see notifications after muting user with notifications" do
438 %{user: user, conn: conn} = oauth_access(["read:notifications"])
439 user2 = insert(:user)
440
441 {:ok, _, _, _} = CommonAPI.follow(user, user2)
442 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
443
444 ret_conn = get(conn, "/api/v1/notifications")
445
446 assert length(json_response(ret_conn, 200)) == 1
447
448 {:ok, _user_relationships} = User.mute(user, user2)
449
450 conn = get(conn, "/api/v1/notifications")
451
452 assert json_response(conn, 200) == []
453 end
454
455 test "see notifications after muting user without notifications" do
456 %{user: user, conn: conn} = oauth_access(["read:notifications"])
457 user2 = insert(:user)
458
459 {:ok, _, _, _} = CommonAPI.follow(user, user2)
460 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
461
462 ret_conn = get(conn, "/api/v1/notifications")
463
464 assert length(json_response(ret_conn, 200)) == 1
465
466 {:ok, _user_relationships} = User.mute(user, user2, false)
467
468 conn = get(conn, "/api/v1/notifications")
469
470 assert length(json_response(conn, 200)) == 1
471 end
472
473 test "see notifications after muting user with notifications and with_muted parameter" do
474 %{user: user, conn: conn} = oauth_access(["read:notifications"])
475 user2 = insert(:user)
476
477 {:ok, _, _, _} = CommonAPI.follow(user, user2)
478 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
479
480 ret_conn = get(conn, "/api/v1/notifications")
481
482 assert length(json_response(ret_conn, 200)) == 1
483
484 {:ok, _user_relationships} = User.mute(user, user2)
485
486 conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
487
488 assert length(json_response(conn, 200)) == 1
489 end
490
491 @tag capture_log: true
492 test "see move notifications" do
493 old_user = insert(:user)
494 new_user = insert(:user, also_known_as: [old_user.ap_id])
495 %{user: follower, conn: conn} = oauth_access(["read:notifications"])
496
497 old_user_url = old_user.ap_id
498
499 body =
500 File.read!("test/fixtures/users_mock/localhost.json")
501 |> String.replace("{{nickname}}", old_user.nickname)
502 |> Jason.encode!()
503
504 Tesla.Mock.mock(fn
505 %{method: :get, url: ^old_user_url} ->
506 %Tesla.Env{status: 200, body: body}
507 end)
508
509 User.follow(follower, old_user)
510 Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user)
511 Pleroma.Tests.ObanHelpers.perform_all()
512
513 conn = get(conn, "/api/v1/notifications")
514
515 assert length(json_response(conn, 200)) == 1
516 end
517
518 describe "link headers" do
519 test "preserves parameters in link headers" do
520 %{user: user, conn: conn} = oauth_access(["read:notifications"])
521 other_user = insert(:user)
522
523 {:ok, activity1} =
524 CommonAPI.post(other_user, %{
525 "status" => "hi @#{user.nickname}",
526 "visibility" => "public"
527 })
528
529 {:ok, activity2} =
530 CommonAPI.post(other_user, %{
531 "status" => "hi @#{user.nickname}",
532 "visibility" => "public"
533 })
534
535 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
536 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
537
538 conn =
539 conn
540 |> assign(:user, user)
541 |> get("/api/v1/notifications", %{media_only: true})
542
543 assert [link_header] = get_resp_header(conn, "link")
544 assert link_header =~ ~r/media_only=true/
545 assert link_header =~ ~r/min_id=#{notification2.id}/
546 assert link_header =~ ~r/max_id=#{notification1.id}/
547 end
548 end
549
550 describe "from specified user" do
551 test "account_id" do
552 %{user: user, conn: conn} = oauth_access(["read:notifications"])
553
554 %{id: account_id} = other_user1 = insert(:user)
555 other_user2 = insert(:user)
556
557 {:ok, _activity} = CommonAPI.post(other_user1, %{"status" => "hi @#{user.nickname}"})
558 {:ok, _activity} = CommonAPI.post(other_user2, %{"status" => "bye @#{user.nickname}"})
559
560 assert [%{"account" => %{"id" => ^account_id}}] =
561 conn
562 |> assign(:user, user)
563 |> get("/api/v1/notifications", %{account_id: account_id})
564 |> json_response(200)
565
566 assert %{"error" => "Account is not found"} =
567 conn
568 |> assign(:user, user)
569 |> get("/api/v1/notifications", %{account_id: "cofe"})
570 |> json_response(404)
571 end
572 end
573
574 defp get_notification_id_by_activity(%{id: id}) do
575 Notification
576 |> Repo.get_by(activity_id: id)
577 |> Map.get(:id)
578 |> to_string()
579 end
580 end