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.NotificationControllerTest do
6 use Pleroma.Web.ConnCase
8 alias Pleroma.Notification
11 alias Pleroma.Web.CommonAPI
13 import Pleroma.Factory
15 test "list of notifications" do
16 %{user: user, conn: conn} = oauth_access(["read:notifications"])
17 other_user = insert(:user)
19 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
21 {:ok, [_notification]} = Notification.create_notifications(activity)
25 |> assign(:user, user)
26 |> get("/api/v1/notifications")
29 "hi <span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{user.id}\" href=\"#{
31 }\" rel=\"ugc\">@<span>#{user.nickname}</span></a></span>"
33 assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
34 assert response == expected_response
37 test "getting a single notification" do
38 %{user: user, conn: conn} = oauth_access(["read:notifications"])
39 other_user = insert(:user)
41 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
43 {:ok, [notification]} = Notification.create_notifications(activity)
45 conn = get(conn, "/api/v1/notifications/#{notification.id}")
48 "hi <span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{user.id}\" href=\"#{
50 }\" rel=\"ugc\">@<span>#{user.nickname}</span></a></span>"
52 assert %{"status" => %{"content" => response}} = json_response(conn, 200)
53 assert response == expected_response
56 test "dismissing a single notification (deprecated endpoint)" do
57 %{user: user, conn: conn} = oauth_access(["write:notifications"])
58 other_user = insert(:user)
60 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
62 {:ok, [notification]} = Notification.create_notifications(activity)
66 |> assign(:user, user)
67 |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
69 assert %{} = json_response(conn, 200)
72 test "dismissing a single notification" do
73 %{user: user, conn: conn} = oauth_access(["write:notifications"])
74 other_user = insert(:user)
76 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
78 {:ok, [notification]} = Notification.create_notifications(activity)
82 |> assign(:user, user)
83 |> post("/api/v1/notifications/#{notification.id}/dismiss")
85 assert %{} = json_response(conn, 200)
88 test "clearing all notifications" do
89 %{user: user, conn: conn} = oauth_access(["write:notifications", "read:notifications"])
90 other_user = insert(:user)
92 {:ok, activity} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
94 {:ok, [_notification]} = Notification.create_notifications(activity)
96 ret_conn = post(conn, "/api/v1/notifications/clear")
98 assert %{} = json_response(ret_conn, 200)
100 ret_conn = get(conn, "/api/v1/notifications")
102 assert all = json_response(ret_conn, 200)
106 test "paginates notifications using min_id, since_id, max_id, and limit" do
107 %{user: user, conn: conn} = oauth_access(["read:notifications"])
108 other_user = insert(:user)
110 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
111 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
112 {:ok, activity3} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
113 {:ok, activity4} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
115 notification1_id = get_notification_id_by_activity(activity1)
116 notification2_id = get_notification_id_by_activity(activity2)
117 notification3_id = get_notification_id_by_activity(activity3)
118 notification4_id = get_notification_id_by_activity(activity4)
120 conn = assign(conn, :user, user)
125 |> get("/api/v1/notifications?limit=2&min_id=#{notification1_id}")
126 |> json_response(:ok)
128 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
133 |> get("/api/v1/notifications?limit=2&since_id=#{notification1_id}")
134 |> json_response(:ok)
136 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
141 |> get("/api/v1/notifications?limit=2&max_id=#{notification4_id}")
142 |> json_response(:ok)
144 assert [%{"id" => ^notification3_id}, %{"id" => ^notification2_id}] = result
147 describe "exclude_visibilities" do
148 test "filters notifications for mentions" do
149 %{user: user, conn: conn} = oauth_access(["read:notifications"])
150 other_user = insert(:user)
152 {:ok, public_activity} =
153 CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "public"})
155 {:ok, direct_activity} =
156 CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "direct"})
158 {:ok, unlisted_activity} =
159 CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "unlisted"})
161 {:ok, private_activity} =
162 CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "private"})
165 get(conn, "/api/v1/notifications", %{
166 exclude_visibilities: ["public", "unlisted", "private"]
169 assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
170 assert id == direct_activity.id
173 get(conn, "/api/v1/notifications", %{
174 exclude_visibilities: ["public", "unlisted", "direct"]
177 assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
178 assert id == private_activity.id
181 get(conn, "/api/v1/notifications", %{
182 exclude_visibilities: ["public", "private", "direct"]
185 assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
186 assert id == unlisted_activity.id
189 get(conn, "/api/v1/notifications", %{
190 exclude_visibilities: ["unlisted", "private", "direct"]
193 assert [%{"status" => %{"id" => id}}] = json_response(conn_res, 200)
194 assert id == public_activity.id
197 test "filters notifications for Like activities" do
199 %{user: other_user, conn: conn} = oauth_access(["read:notifications"])
201 {:ok, public_activity} =
202 CommonAPI.post(other_user, %{"status" => ".", "visibility" => "public"})
204 {:ok, direct_activity} =
205 CommonAPI.post(other_user, %{"status" => "@#{user.nickname}", "visibility" => "direct"})
207 {:ok, unlisted_activity} =
208 CommonAPI.post(other_user, %{"status" => ".", "visibility" => "unlisted"})
210 {:ok, private_activity} =
211 CommonAPI.post(other_user, %{"status" => ".", "visibility" => "private"})
213 {:ok, _} = CommonAPI.favorite(user, public_activity.id)
214 {:ok, _} = CommonAPI.favorite(user, direct_activity.id)
215 {:ok, _} = CommonAPI.favorite(user, unlisted_activity.id)
216 {:ok, _} = CommonAPI.favorite(user, private_activity.id)
220 |> get("/api/v1/notifications", %{exclude_visibilities: ["direct"]})
221 |> json_response(200)
222 |> Enum.map(& &1["status"]["id"])
224 assert public_activity.id in activity_ids
225 assert unlisted_activity.id in activity_ids
226 assert private_activity.id in activity_ids
227 refute direct_activity.id in activity_ids
231 |> get("/api/v1/notifications", %{exclude_visibilities: ["unlisted"]})
232 |> json_response(200)
233 |> Enum.map(& &1["status"]["id"])
235 assert public_activity.id in activity_ids
236 refute unlisted_activity.id in activity_ids
237 assert private_activity.id in activity_ids
238 assert direct_activity.id in activity_ids
242 |> get("/api/v1/notifications", %{exclude_visibilities: ["private"]})
243 |> json_response(200)
244 |> Enum.map(& &1["status"]["id"])
246 assert public_activity.id in activity_ids
247 assert unlisted_activity.id in activity_ids
248 refute private_activity.id in activity_ids
249 assert direct_activity.id in activity_ids
253 |> get("/api/v1/notifications", %{exclude_visibilities: ["public"]})
254 |> json_response(200)
255 |> Enum.map(& &1["status"]["id"])
257 refute public_activity.id in activity_ids
258 assert unlisted_activity.id in activity_ids
259 assert private_activity.id in activity_ids
260 assert direct_activity.id in activity_ids
263 test "filters notifications for Announce activities" do
265 %{user: other_user, conn: conn} = oauth_access(["read:notifications"])
267 {:ok, public_activity} =
268 CommonAPI.post(other_user, %{"status" => ".", "visibility" => "public"})
270 {:ok, unlisted_activity} =
271 CommonAPI.post(other_user, %{"status" => ".", "visibility" => "unlisted"})
273 {:ok, _, _} = CommonAPI.repeat(public_activity.id, user)
274 {:ok, _, _} = CommonAPI.repeat(unlisted_activity.id, user)
278 |> get("/api/v1/notifications", %{exclude_visibilities: ["unlisted"]})
279 |> json_response(200)
280 |> Enum.map(& &1["status"]["id"])
282 assert public_activity.id in activity_ids
283 refute unlisted_activity.id in activity_ids
287 test "filters notifications using exclude_types" do
288 %{user: user, conn: conn} = oauth_access(["read:notifications"])
289 other_user = insert(:user)
291 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
292 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
293 {:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id)
294 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
295 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
297 mention_notification_id = get_notification_id_by_activity(mention_activity)
298 favorite_notification_id = get_notification_id_by_activity(favorite_activity)
299 reblog_notification_id = get_notification_id_by_activity(reblog_activity)
300 follow_notification_id = get_notification_id_by_activity(follow_activity)
303 get(conn, "/api/v1/notifications", %{exclude_types: ["mention", "favourite", "reblog"]})
305 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
308 get(conn, "/api/v1/notifications", %{exclude_types: ["favourite", "reblog", "follow"]})
310 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
313 get(conn, "/api/v1/notifications", %{exclude_types: ["reblog", "follow", "mention"]})
315 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
318 get(conn, "/api/v1/notifications", %{exclude_types: ["follow", "mention", "favourite"]})
320 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
323 test "filters notifications using include_types" do
324 %{user: user, conn: conn} = oauth_access(["read:notifications"])
325 other_user = insert(:user)
327 {:ok, mention_activity} = CommonAPI.post(other_user, %{"status" => "hey @#{user.nickname}"})
328 {:ok, create_activity} = CommonAPI.post(user, %{"status" => "hey"})
329 {:ok, favorite_activity} = CommonAPI.favorite(other_user, create_activity.id)
330 {:ok, reblog_activity, _} = CommonAPI.repeat(create_activity.id, other_user)
331 {:ok, _, _, follow_activity} = CommonAPI.follow(other_user, user)
333 mention_notification_id = get_notification_id_by_activity(mention_activity)
334 favorite_notification_id = get_notification_id_by_activity(favorite_activity)
335 reblog_notification_id = get_notification_id_by_activity(reblog_activity)
336 follow_notification_id = get_notification_id_by_activity(follow_activity)
338 conn_res = get(conn, "/api/v1/notifications", %{include_types: ["follow"]})
340 assert [%{"id" => ^follow_notification_id}] = json_response(conn_res, 200)
342 conn_res = get(conn, "/api/v1/notifications", %{include_types: ["mention"]})
344 assert [%{"id" => ^mention_notification_id}] = json_response(conn_res, 200)
346 conn_res = get(conn, "/api/v1/notifications", %{include_types: ["favourite"]})
348 assert [%{"id" => ^favorite_notification_id}] = json_response(conn_res, 200)
350 conn_res = get(conn, "/api/v1/notifications", %{include_types: ["reblog"]})
352 assert [%{"id" => ^reblog_notification_id}] = json_response(conn_res, 200)
354 result = conn |> get("/api/v1/notifications") |> json_response(200)
356 assert length(result) == 4
360 |> get("/api/v1/notifications", %{
361 include_types: ["follow", "mention", "favourite", "reblog"]
363 |> json_response(200)
365 assert length(result) == 4
368 test "destroy multiple" do
369 %{user: user, conn: conn} = oauth_access(["read:notifications", "write:notifications"])
370 other_user = insert(:user)
372 {:ok, activity1} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
373 {:ok, activity2} = CommonAPI.post(other_user, %{"status" => "hi @#{user.nickname}"})
374 {:ok, activity3} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
375 {:ok, activity4} = CommonAPI.post(user, %{"status" => "hi @#{other_user.nickname}"})
377 notification1_id = get_notification_id_by_activity(activity1)
378 notification2_id = get_notification_id_by_activity(activity2)
379 notification3_id = get_notification_id_by_activity(activity3)
380 notification4_id = get_notification_id_by_activity(activity4)
384 |> get("/api/v1/notifications")
385 |> json_response(:ok)
387 assert [%{"id" => ^notification2_id}, %{"id" => ^notification1_id}] = result
391 |> assign(:user, other_user)
392 |> assign(:token, insert(:oauth_token, user: other_user, scopes: ["read:notifications"]))
396 |> get("/api/v1/notifications")
397 |> json_response(:ok)
399 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
403 |> delete("/api/v1/notifications/destroy_multiple", %{
404 "ids" => [notification1_id, notification2_id]
407 assert json_response(conn_destroy, 200) == %{}
411 |> get("/api/v1/notifications")
412 |> json_response(:ok)
414 assert [%{"id" => ^notification4_id}, %{"id" => ^notification3_id}] = result
417 test "doesn't see notifications after muting user with notifications" do
418 %{user: user, conn: conn} = oauth_access(["read:notifications"])
419 user2 = insert(:user)
421 {:ok, _, _, _} = CommonAPI.follow(user, user2)
422 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
424 ret_conn = get(conn, "/api/v1/notifications")
426 assert length(json_response(ret_conn, 200)) == 1
428 {:ok, _user_relationships} = User.mute(user, user2)
430 conn = get(conn, "/api/v1/notifications")
432 assert json_response(conn, 200) == []
435 test "see notifications after muting user without notifications" do
436 %{user: user, conn: conn} = oauth_access(["read:notifications"])
437 user2 = insert(:user)
439 {:ok, _, _, _} = CommonAPI.follow(user, user2)
440 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
442 ret_conn = get(conn, "/api/v1/notifications")
444 assert length(json_response(ret_conn, 200)) == 1
446 {:ok, _user_relationships} = User.mute(user, user2, false)
448 conn = get(conn, "/api/v1/notifications")
450 assert length(json_response(conn, 200)) == 1
453 test "see notifications after muting user with notifications and with_muted parameter" do
454 %{user: user, conn: conn} = oauth_access(["read:notifications"])
455 user2 = insert(:user)
457 {:ok, _, _, _} = CommonAPI.follow(user, user2)
458 {:ok, _} = CommonAPI.post(user2, %{"status" => "hey @#{user.nickname}"})
460 ret_conn = get(conn, "/api/v1/notifications")
462 assert length(json_response(ret_conn, 200)) == 1
464 {:ok, _user_relationships} = User.mute(user, user2)
466 conn = get(conn, "/api/v1/notifications", %{"with_muted" => "true"})
468 assert length(json_response(conn, 200)) == 1
471 @tag capture_log: true
472 test "see move notifications" do
473 old_user = insert(:user)
474 new_user = insert(:user, also_known_as: [old_user.ap_id])
475 %{user: follower, conn: conn} = oauth_access(["read:notifications"])
477 old_user_url = old_user.ap_id
480 File.read!("test/fixtures/users_mock/localhost.json")
481 |> String.replace("{{nickname}}", old_user.nickname)
485 %{method: :get, url: ^old_user_url} ->
486 %Tesla.Env{status: 200, body: body}
489 User.follow(follower, old_user)
490 Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user)
491 Pleroma.Tests.ObanHelpers.perform_all()
493 conn = get(conn, "/api/v1/notifications")
495 assert length(json_response(conn, 200)) == 1
498 describe "link headers" do
499 test "preserves parameters in link headers" do
500 %{user: user, conn: conn} = oauth_access(["read:notifications"])
501 other_user = insert(:user)
504 CommonAPI.post(other_user, %{
505 "status" => "hi @#{user.nickname}",
506 "visibility" => "public"
510 CommonAPI.post(other_user, %{
511 "status" => "hi @#{user.nickname}",
512 "visibility" => "public"
515 notification1 = Repo.get_by(Notification, activity_id: activity1.id)
516 notification2 = Repo.get_by(Notification, activity_id: activity2.id)
520 |> assign(:user, user)
521 |> get("/api/v1/notifications", %{media_only: true})
523 assert [link_header] = get_resp_header(conn, "link")
524 assert link_header =~ ~r/media_only=true/
525 assert link_header =~ ~r/min_id=#{notification2.id}/
526 assert link_header =~ ~r/max_id=#{notification1.id}/
530 describe "from specified user" do
532 %{user: user, conn: conn} = oauth_access(["read:notifications"])
534 %{id: account_id} = other_user1 = insert(:user)
535 other_user2 = insert(:user)
537 {:ok, _activity} = CommonAPI.post(other_user1, %{"status" => "hi @#{user.nickname}"})
538 {:ok, _activity} = CommonAPI.post(other_user2, %{"status" => "bye @#{user.nickname}"})
540 assert [%{"account" => %{"id" => ^account_id}}] =
542 |> assign(:user, user)
543 |> get("/api/v1/notifications", %{account_id: account_id})
544 |> json_response(200)
546 assert %{"error" => "Account is not found"} =
548 |> assign(:user, user)
549 |> get("/api/v1/notifications", %{account_id: "cofe"})
550 |> json_response(404)
554 defp get_notification_id_by_activity(%{id: id}) do
556 |> Repo.get_by(activity_id: id)