Fork of Pleroma with site-specific changes and feature branches https://git.pleroma.social/pleroma/pleroma
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1165 lines
38KB

  1. # Pleroma: A lightweight social networking server
  2. # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
  3. # SPDX-License-Identifier: AGPL-3.0-only
  4. defmodule Pleroma.NotificationTest do
  5. use Pleroma.DataCase
  6. import Pleroma.Factory
  7. import Mock
  8. alias Pleroma.FollowingRelationship
  9. alias Pleroma.Notification
  10. alias Pleroma.Repo
  11. alias Pleroma.Tests.ObanHelpers
  12. alias Pleroma.User
  13. alias Pleroma.Web.ActivityPub.ActivityPub
  14. alias Pleroma.Web.ActivityPub.Builder
  15. alias Pleroma.Web.ActivityPub.Transmogrifier
  16. alias Pleroma.Web.CommonAPI
  17. alias Pleroma.Web.MastodonAPI.NotificationView
  18. alias Pleroma.Web.Push
  19. alias Pleroma.Web.Streamer
  20. describe "create_notifications" do
  21. test "never returns nil" do
  22. user = insert(:user)
  23. other_user = insert(:user, %{invisible: true})
  24. {:ok, activity} = CommonAPI.post(user, %{status: "yeah"})
  25. {:ok, activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
  26. refute {:ok, [nil]} == Notification.create_notifications(activity)
  27. end
  28. test "creates a notification for a report" do
  29. reporting_user = insert(:user)
  30. reported_user = insert(:user)
  31. {:ok, moderator_user} = insert(:user) |> User.admin_api_update(%{is_moderator: true})
  32. {:ok, activity} = CommonAPI.report(reporting_user, %{account_id: reported_user.id})
  33. {:ok, [notification]} = Notification.create_notifications(activity)
  34. assert notification.user_id == moderator_user.id
  35. assert notification.type == "pleroma:report"
  36. end
  37. test "suppresses notification to reporter if reporter is an admin" do
  38. reporting_admin = insert(:user, is_admin: true)
  39. reported_user = insert(:user)
  40. other_admin = insert(:user, is_admin: true)
  41. {:ok, activity} = CommonAPI.report(reporting_admin, %{account_id: reported_user.id})
  42. {:ok, [notification]} = Notification.create_notifications(activity)
  43. refute notification.user_id == reporting_admin.id
  44. assert notification.user_id == other_admin.id
  45. assert notification.type == "pleroma:report"
  46. end
  47. test "creates a notification for an emoji reaction" do
  48. user = insert(:user)
  49. other_user = insert(:user)
  50. {:ok, activity} = CommonAPI.post(user, %{status: "yeah"})
  51. {:ok, activity} = CommonAPI.react_with_emoji(activity.id, other_user, "☕")
  52. {:ok, [notification]} = Notification.create_notifications(activity)
  53. assert notification.user_id == user.id
  54. assert notification.type == "pleroma:emoji_reaction"
  55. end
  56. test "notifies someone when they are directly addressed" do
  57. user = insert(:user)
  58. other_user = insert(:user)
  59. third_user = insert(:user)
  60. {:ok, activity} =
  61. CommonAPI.post(user, %{
  62. status: "hey @#{other_user.nickname} and @#{third_user.nickname}"
  63. })
  64. {:ok, [notification, other_notification]} = Notification.create_notifications(activity)
  65. notified_ids = Enum.sort([notification.user_id, other_notification.user_id])
  66. assert notified_ids == [other_user.id, third_user.id]
  67. assert notification.activity_id == activity.id
  68. assert notification.type == "mention"
  69. assert other_notification.activity_id == activity.id
  70. assert [%Pleroma.Marker{unread_count: 2}] =
  71. Pleroma.Marker.get_markers(other_user, ["notifications"])
  72. end
  73. test "it creates a notification for subscribed users" do
  74. user = insert(:user)
  75. subscriber = insert(:user)
  76. User.subscribe(subscriber, user)
  77. {:ok, status} = CommonAPI.post(user, %{status: "Akariiiin"})
  78. {:ok, [notification]} = Notification.create_notifications(status)
  79. assert notification.user_id == subscriber.id
  80. end
  81. test "does not create a notification for subscribed users if status is a reply" do
  82. user = insert(:user)
  83. other_user = insert(:user)
  84. subscriber = insert(:user)
  85. User.subscribe(subscriber, other_user)
  86. {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
  87. {:ok, _reply_activity} =
  88. CommonAPI.post(other_user, %{
  89. status: "test reply",
  90. in_reply_to_status_id: activity.id
  91. })
  92. user_notifications = Notification.for_user(user)
  93. assert length(user_notifications) == 1
  94. subscriber_notifications = Notification.for_user(subscriber)
  95. assert Enum.empty?(subscriber_notifications)
  96. end
  97. end
  98. describe "CommonApi.post/2 notification-related functionality" do
  99. test_with_mock "creates but does NOT send notification to blocker user",
  100. Push,
  101. [:passthrough],
  102. [] do
  103. user = insert(:user)
  104. blocker = insert(:user)
  105. {:ok, _user_relationship} = User.block(blocker, user)
  106. {:ok, _activity} = CommonAPI.post(user, %{status: "hey @#{blocker.nickname}!"})
  107. blocker_id = blocker.id
  108. assert [%Notification{user_id: ^blocker_id}] = Repo.all(Notification)
  109. refute called(Push.send(:_))
  110. end
  111. test_with_mock "creates but does NOT send notification to notification-muter user",
  112. Push,
  113. [:passthrough],
  114. [] do
  115. user = insert(:user)
  116. muter = insert(:user)
  117. {:ok, _user_relationships} = User.mute(muter, user)
  118. {:ok, _activity} = CommonAPI.post(user, %{status: "hey @#{muter.nickname}!"})
  119. muter_id = muter.id
  120. assert [%Notification{user_id: ^muter_id}] = Repo.all(Notification)
  121. refute called(Push.send(:_))
  122. end
  123. test_with_mock "creates but does NOT send notification to thread-muter user",
  124. Push,
  125. [:passthrough],
  126. [] do
  127. user = insert(:user)
  128. thread_muter = insert(:user)
  129. {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{thread_muter.nickname}!"})
  130. {:ok, _} = CommonAPI.add_mute(thread_muter, activity)
  131. {:ok, _same_context_activity} =
  132. CommonAPI.post(user, %{
  133. status: "hey-hey-hey @#{thread_muter.nickname}!",
  134. in_reply_to_status_id: activity.id
  135. })
  136. [pre_mute_notification, post_mute_notification] =
  137. Repo.all(from(n in Notification, where: n.user_id == ^thread_muter.id, order_by: n.id))
  138. pre_mute_notification_id = pre_mute_notification.id
  139. post_mute_notification_id = post_mute_notification.id
  140. assert called(
  141. Push.send(
  142. :meck.is(fn
  143. %Notification{id: ^pre_mute_notification_id} -> true
  144. _ -> false
  145. end)
  146. )
  147. )
  148. refute called(
  149. Push.send(
  150. :meck.is(fn
  151. %Notification{id: ^post_mute_notification_id} -> true
  152. _ -> false
  153. end)
  154. )
  155. )
  156. end
  157. end
  158. describe "create_notification" do
  159. @tag needs_streamer: true
  160. test "it creates a notification for user and send to the 'user' and the 'user:notification' stream" do
  161. %{user: user, token: oauth_token} = oauth_access(["read"])
  162. task =
  163. Task.async(fn ->
  164. {:ok, _topic} = Streamer.get_topic_and_add_socket("user", user, oauth_token)
  165. assert_receive {:render_with_user, _, _, _}, 4_000
  166. end)
  167. task_user_notification =
  168. Task.async(fn ->
  169. {:ok, _topic} =
  170. Streamer.get_topic_and_add_socket("user:notification", user, oauth_token)
  171. assert_receive {:render_with_user, _, _, _}, 4_000
  172. end)
  173. activity = insert(:note_activity)
  174. notify = Notification.create_notification(activity, user)
  175. assert notify.user_id == user.id
  176. Task.await(task)
  177. Task.await(task_user_notification)
  178. end
  179. test "it creates a notification for user if the user blocks the activity author" do
  180. activity = insert(:note_activity)
  181. author = User.get_cached_by_ap_id(activity.data["actor"])
  182. user = insert(:user)
  183. {:ok, _user_relationship} = User.block(user, author)
  184. assert Notification.create_notification(activity, user)
  185. end
  186. test "it creates a notification for the user if the user mutes the activity author" do
  187. muter = insert(:user)
  188. muted = insert(:user)
  189. {:ok, _} = User.mute(muter, muted)
  190. muter = Repo.get(User, muter.id)
  191. {:ok, activity} = CommonAPI.post(muted, %{status: "Hi @#{muter.nickname}"})
  192. notification = Notification.create_notification(activity, muter)
  193. assert notification.id
  194. assert notification.seen
  195. end
  196. test "notification created if user is muted without notifications" do
  197. muter = insert(:user)
  198. muted = insert(:user)
  199. {:ok, _user_relationships} = User.mute(muter, muted, %{notifications: false})
  200. {:ok, activity} = CommonAPI.post(muted, %{status: "Hi @#{muter.nickname}"})
  201. assert Notification.create_notification(activity, muter)
  202. end
  203. test "it creates a notification for an activity from a muted thread" do
  204. muter = insert(:user)
  205. other_user = insert(:user)
  206. {:ok, activity} = CommonAPI.post(muter, %{status: "hey"})
  207. CommonAPI.add_mute(muter, activity)
  208. {:ok, activity} =
  209. CommonAPI.post(other_user, %{
  210. status: "Hi @#{muter.nickname}",
  211. in_reply_to_status_id: activity.id
  212. })
  213. notification = Notification.create_notification(activity, muter)
  214. assert notification.id
  215. assert notification.seen
  216. end
  217. test "it disables notifications from strangers" do
  218. follower = insert(:user)
  219. followed =
  220. insert(:user,
  221. notification_settings: %Pleroma.User.NotificationSetting{block_from_strangers: true}
  222. )
  223. {:ok, activity} = CommonAPI.post(follower, %{status: "hey @#{followed.nickname}"})
  224. refute Notification.create_notification(activity, followed)
  225. end
  226. test "it doesn't create a notification for user if he is the activity author" do
  227. activity = insert(:note_activity)
  228. author = User.get_cached_by_ap_id(activity.data["actor"])
  229. refute Notification.create_notification(activity, author)
  230. end
  231. test "it doesn't create duplicate notifications for follow+subscribed users" do
  232. user = insert(:user)
  233. subscriber = insert(:user)
  234. {:ok, _, _, _} = CommonAPI.follow(subscriber, user)
  235. User.subscribe(subscriber, user)
  236. {:ok, status} = CommonAPI.post(user, %{status: "Akariiiin"})
  237. {:ok, [_notif]} = Notification.create_notifications(status)
  238. end
  239. test "it doesn't create subscription notifications if the recipient cannot see the status" do
  240. user = insert(:user)
  241. subscriber = insert(:user)
  242. User.subscribe(subscriber, user)
  243. {:ok, status} = CommonAPI.post(user, %{status: "inwisible", visibility: "direct"})
  244. assert {:ok, []} == Notification.create_notifications(status)
  245. end
  246. test "it disables notifications from people who are invisible" do
  247. author = insert(:user, invisible: true)
  248. user = insert(:user)
  249. {:ok, status} = CommonAPI.post(author, %{status: "hey @#{user.nickname}"})
  250. refute Notification.create_notification(status, user)
  251. end
  252. test "it doesn't create notifications if content matches with an irreversible filter" do
  253. user = insert(:user)
  254. subscriber = insert(:user)
  255. User.subscribe(subscriber, user)
  256. insert(:filter, user: subscriber, phrase: "cofe", hide: true)
  257. {:ok, status} = CommonAPI.post(user, %{status: "got cofe?"})
  258. assert {:ok, []} == Notification.create_notifications(status)
  259. end
  260. test "it creates notifications if content matches with a not irreversible filter" do
  261. user = insert(:user)
  262. subscriber = insert(:user)
  263. User.subscribe(subscriber, user)
  264. insert(:filter, user: subscriber, phrase: "cofe", hide: false)
  265. {:ok, status} = CommonAPI.post(user, %{status: "got cofe?"})
  266. {:ok, [notification]} = Notification.create_notifications(status)
  267. assert notification
  268. refute notification.seen
  269. end
  270. test "it creates notifications when someone likes user's status with a filtered word" do
  271. user = insert(:user)
  272. other_user = insert(:user)
  273. insert(:filter, user: user, phrase: "tesla", hide: true)
  274. {:ok, activity_one} = CommonAPI.post(user, %{status: "wow tesla"})
  275. {:ok, activity_two} = CommonAPI.favorite(other_user, activity_one.id)
  276. {:ok, [notification]} = Notification.create_notifications(activity_two)
  277. assert notification
  278. refute notification.seen
  279. end
  280. end
  281. describe "follow / follow_request notifications" do
  282. test "it creates `follow` notification for approved Follow activity" do
  283. user = insert(:user)
  284. followed_user = insert(:user, is_locked: false)
  285. {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
  286. assert FollowingRelationship.following?(user, followed_user)
  287. assert [notification] = Notification.for_user(followed_user)
  288. assert %{type: "follow"} =
  289. NotificationView.render("show.json", %{
  290. notification: notification,
  291. for: followed_user
  292. })
  293. end
  294. test "it creates `follow_request` notification for pending Follow activity" do
  295. user = insert(:user)
  296. followed_user = insert(:user, is_locked: true)
  297. {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
  298. refute FollowingRelationship.following?(user, followed_user)
  299. assert [notification] = Notification.for_user(followed_user)
  300. render_opts = %{notification: notification, for: followed_user}
  301. assert %{type: "follow_request"} = NotificationView.render("show.json", render_opts)
  302. # After request is accepted, the same notification is rendered with type "follow":
  303. assert {:ok, _} = CommonAPI.accept_follow_request(user, followed_user)
  304. notification =
  305. Repo.get(Notification, notification.id)
  306. |> Repo.preload(:activity)
  307. assert %{type: "follow"} =
  308. NotificationView.render("show.json", notification: notification, for: followed_user)
  309. end
  310. test "it doesn't create a notification for follow-unfollow-follow chains" do
  311. user = insert(:user)
  312. followed_user = insert(:user, is_locked: false)
  313. {:ok, _, _, _activity} = CommonAPI.follow(user, followed_user)
  314. assert FollowingRelationship.following?(user, followed_user)
  315. assert [notification] = Notification.for_user(followed_user)
  316. CommonAPI.unfollow(user, followed_user)
  317. {:ok, _, _, _activity_dupe} = CommonAPI.follow(user, followed_user)
  318. notification_id = notification.id
  319. assert [%{id: ^notification_id}] = Notification.for_user(followed_user)
  320. end
  321. test "dismisses the notification on follow request rejection" do
  322. user = insert(:user, is_locked: true)
  323. follower = insert(:user)
  324. {:ok, _, _, _follow_activity} = CommonAPI.follow(follower, user)
  325. assert [_notification] = Notification.for_user(user)
  326. {:ok, _follower} = CommonAPI.reject_follow_request(follower, user)
  327. assert [] = Notification.for_user(user)
  328. end
  329. end
  330. describe "get notification" do
  331. test "it gets a notification that belongs to the user" do
  332. user = insert(:user)
  333. other_user = insert(:user)
  334. {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
  335. {:ok, [notification]} = Notification.create_notifications(activity)
  336. {:ok, notification} = Notification.get(other_user, notification.id)
  337. assert notification.user_id == other_user.id
  338. end
  339. test "it returns error if the notification doesn't belong to the user" do
  340. user = insert(:user)
  341. other_user = insert(:user)
  342. {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
  343. {:ok, [notification]} = Notification.create_notifications(activity)
  344. {:error, _notification} = Notification.get(user, notification.id)
  345. end
  346. end
  347. describe "dismiss notification" do
  348. test "it dismisses a notification that belongs to the user" do
  349. user = insert(:user)
  350. other_user = insert(:user)
  351. {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
  352. {:ok, [notification]} = Notification.create_notifications(activity)
  353. {:ok, notification} = Notification.dismiss(other_user, notification.id)
  354. assert notification.user_id == other_user.id
  355. end
  356. test "it returns error if the notification doesn't belong to the user" do
  357. user = insert(:user)
  358. other_user = insert(:user)
  359. {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}"})
  360. {:ok, [notification]} = Notification.create_notifications(activity)
  361. {:error, _notification} = Notification.dismiss(user, notification.id)
  362. end
  363. end
  364. describe "clear notification" do
  365. test "it clears all notifications belonging to the user" do
  366. user = insert(:user)
  367. other_user = insert(:user)
  368. third_user = insert(:user)
  369. {:ok, activity} =
  370. CommonAPI.post(user, %{
  371. status: "hey @#{other_user.nickname} and @#{third_user.nickname} !"
  372. })
  373. {:ok, _notifs} = Notification.create_notifications(activity)
  374. {:ok, activity} =
  375. CommonAPI.post(user, %{
  376. status: "hey again @#{other_user.nickname} and @#{third_user.nickname} !"
  377. })
  378. {:ok, _notifs} = Notification.create_notifications(activity)
  379. Notification.clear(other_user)
  380. assert Notification.for_user(other_user) == []
  381. assert Notification.for_user(third_user) != []
  382. end
  383. end
  384. describe "set_read_up_to()" do
  385. test "it sets all notifications as read up to a specified notification ID" do
  386. user = insert(:user)
  387. other_user = insert(:user)
  388. {:ok, _activity} =
  389. CommonAPI.post(user, %{
  390. status: "hey @#{other_user.nickname}!"
  391. })
  392. {:ok, _activity} =
  393. CommonAPI.post(user, %{
  394. status: "hey again @#{other_user.nickname}!"
  395. })
  396. [n2, n1] = Notification.for_user(other_user)
  397. assert n2.id > n1.id
  398. {:ok, _activity} =
  399. CommonAPI.post(user, %{
  400. status: "hey yet again @#{other_user.nickname}!"
  401. })
  402. [_, read_notification] = Notification.set_read_up_to(other_user, n2.id)
  403. assert read_notification.activity.object
  404. [n3, n2, n1] = Notification.for_user(other_user)
  405. assert n1.seen == true
  406. assert n2.seen == true
  407. assert n3.seen == false
  408. assert %Pleroma.Marker{} =
  409. m =
  410. Pleroma.Repo.get_by(
  411. Pleroma.Marker,
  412. user_id: other_user.id,
  413. timeline: "notifications"
  414. )
  415. assert m.last_read_id == to_string(n2.id)
  416. end
  417. end
  418. describe "for_user_since/2" do
  419. defp days_ago(days) do
  420. NaiveDateTime.add(
  421. NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second),
  422. -days * 60 * 60 * 24,
  423. :second
  424. )
  425. end
  426. test "Returns recent notifications" do
  427. user1 = insert(:user)
  428. user2 = insert(:user)
  429. Enum.each(0..10, fn i ->
  430. {:ok, _activity} =
  431. CommonAPI.post(user1, %{
  432. status: "hey ##{i} @#{user2.nickname}!"
  433. })
  434. end)
  435. {old, new} = Enum.split(Notification.for_user(user2), 5)
  436. Enum.each(old, fn notification ->
  437. notification
  438. |> cast(%{updated_at: days_ago(10)}, [:updated_at])
  439. |> Pleroma.Repo.update!()
  440. end)
  441. recent_notifications_ids =
  442. user2
  443. |> Notification.for_user_since(
  444. NaiveDateTime.add(NaiveDateTime.utc_now(), -5 * 86_400, :second)
  445. )
  446. |> Enum.map(& &1.id)
  447. Enum.each(old, fn %{id: id} ->
  448. refute id in recent_notifications_ids
  449. end)
  450. Enum.each(new, fn %{id: id} ->
  451. assert id in recent_notifications_ids
  452. end)
  453. end
  454. end
  455. describe "notification target determination / get_notified_from_activity/2" do
  456. test "it sends notifications to addressed users in new messages" do
  457. user = insert(:user)
  458. other_user = insert(:user)
  459. {:ok, activity} =
  460. CommonAPI.post(user, %{
  461. status: "hey @#{other_user.nickname}!"
  462. })
  463. {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
  464. assert other_user in enabled_receivers
  465. end
  466. test "it sends notifications to mentioned users in new messages" do
  467. user = insert(:user)
  468. other_user = insert(:user)
  469. create_activity = %{
  470. "@context" => "https://www.w3.org/ns/activitystreams",
  471. "type" => "Create",
  472. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  473. "actor" => user.ap_id,
  474. "object" => %{
  475. "type" => "Note",
  476. "id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
  477. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  478. "content" => "message with a Mention tag, but no explicit tagging",
  479. "tag" => [
  480. %{
  481. "type" => "Mention",
  482. "href" => other_user.ap_id,
  483. "name" => other_user.nickname
  484. }
  485. ],
  486. "attributedTo" => user.ap_id
  487. }
  488. }
  489. {:ok, activity} = Transmogrifier.handle_incoming(create_activity)
  490. {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
  491. assert other_user in enabled_receivers
  492. end
  493. test "it does not send notifications to users who are only cc in new messages" do
  494. user = insert(:user)
  495. other_user = insert(:user)
  496. create_activity = %{
  497. "@context" => "https://www.w3.org/ns/activitystreams",
  498. "type" => "Create",
  499. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  500. "cc" => [other_user.ap_id],
  501. "actor" => user.ap_id,
  502. "object" => %{
  503. "type" => "Note",
  504. "id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
  505. "to" => ["https://www.w3.org/ns/activitystreams#Public"],
  506. "cc" => [other_user.ap_id],
  507. "content" => "hi everyone",
  508. "attributedTo" => user.ap_id
  509. }
  510. }
  511. {:ok, activity} = Transmogrifier.handle_incoming(create_activity)
  512. {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(activity)
  513. assert other_user not in enabled_receivers
  514. end
  515. test "it does not send notification to mentioned users in likes" do
  516. user = insert(:user)
  517. other_user = insert(:user)
  518. third_user = insert(:user)
  519. {:ok, activity_one} =
  520. CommonAPI.post(user, %{
  521. status: "hey @#{other_user.nickname}!"
  522. })
  523. {:ok, activity_two} = CommonAPI.favorite(third_user, activity_one.id)
  524. {enabled_receivers, _disabled_receivers} =
  525. Notification.get_notified_from_activity(activity_two)
  526. assert other_user not in enabled_receivers
  527. end
  528. test "it only notifies the post's author in likes" do
  529. user = insert(:user)
  530. other_user = insert(:user)
  531. third_user = insert(:user)
  532. {:ok, activity_one} =
  533. CommonAPI.post(user, %{
  534. status: "hey @#{other_user.nickname}!"
  535. })
  536. {:ok, like_data, _} = Builder.like(third_user, activity_one.object)
  537. {:ok, like, _} =
  538. like_data
  539. |> Map.put("to", [other_user.ap_id | like_data["to"]])
  540. |> ActivityPub.persist(local: true)
  541. {enabled_receivers, _disabled_receivers} = Notification.get_notified_from_activity(like)
  542. assert other_user not in enabled_receivers
  543. end
  544. test "it does not send notification to mentioned users in announces" do
  545. user = insert(:user)
  546. other_user = insert(:user)
  547. third_user = insert(:user)
  548. {:ok, activity_one} =
  549. CommonAPI.post(user, %{
  550. status: "hey @#{other_user.nickname}!"
  551. })
  552. {:ok, activity_two} = CommonAPI.repeat(activity_one.id, third_user)
  553. {enabled_receivers, _disabled_receivers} =
  554. Notification.get_notified_from_activity(activity_two)
  555. assert other_user not in enabled_receivers
  556. end
  557. test "it returns blocking recipient in disabled recipients list" do
  558. user = insert(:user)
  559. other_user = insert(:user)
  560. {:ok, _user_relationship} = User.block(other_user, user)
  561. {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
  562. {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
  563. assert [] == enabled_receivers
  564. assert [other_user] == disabled_receivers
  565. end
  566. test "it returns notification-muting recipient in disabled recipients list" do
  567. user = insert(:user)
  568. other_user = insert(:user)
  569. {:ok, _user_relationships} = User.mute(other_user, user)
  570. {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
  571. {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
  572. assert [] == enabled_receivers
  573. assert [other_user] == disabled_receivers
  574. end
  575. test "it returns thread-muting recipient in disabled recipients list" do
  576. user = insert(:user)
  577. other_user = insert(:user)
  578. {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
  579. {:ok, _} = CommonAPI.add_mute(other_user, activity)
  580. {:ok, same_context_activity} =
  581. CommonAPI.post(user, %{
  582. status: "hey-hey-hey @#{other_user.nickname}!",
  583. in_reply_to_status_id: activity.id
  584. })
  585. {enabled_receivers, disabled_receivers} =
  586. Notification.get_notified_from_activity(same_context_activity)
  587. assert [other_user] == disabled_receivers
  588. refute other_user in enabled_receivers
  589. end
  590. test "it returns non-following domain-blocking recipient in disabled recipients list" do
  591. blocked_domain = "blocked.domain"
  592. user = insert(:user, %{ap_id: "https://#{blocked_domain}/@actor"})
  593. other_user = insert(:user)
  594. {:ok, other_user} = User.block_domain(other_user, blocked_domain)
  595. {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
  596. {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
  597. assert [] == enabled_receivers
  598. assert [other_user] == disabled_receivers
  599. end
  600. test "it returns following domain-blocking recipient in enabled recipients list" do
  601. blocked_domain = "blocked.domain"
  602. user = insert(:user, %{ap_id: "https://#{blocked_domain}/@actor"})
  603. other_user = insert(:user)
  604. {:ok, other_user} = User.block_domain(other_user, blocked_domain)
  605. {:ok, other_user, user} = User.follow(other_user, user)
  606. {:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
  607. {enabled_receivers, disabled_receivers} = Notification.get_notified_from_activity(activity)
  608. assert [other_user] == enabled_receivers
  609. assert [] == disabled_receivers
  610. end
  611. end
  612. describe "notification lifecycle" do
  613. test "liking an activity results in 1 notification, then 0 if the activity is deleted" do
  614. user = insert(:user)
  615. other_user = insert(:user)
  616. {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
  617. assert Enum.empty?(Notification.for_user(user))
  618. {:ok, _} = CommonAPI.favorite(other_user, activity.id)
  619. assert length(Notification.for_user(user)) == 1
  620. {:ok, _} = CommonAPI.delete(activity.id, user)
  621. assert Enum.empty?(Notification.for_user(user))
  622. end
  623. test "liking an activity results in 1 notification, then 0 if the activity is unliked" do
  624. user = insert(:user)
  625. other_user = insert(:user)
  626. {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
  627. assert Enum.empty?(Notification.for_user(user))
  628. {:ok, _} = CommonAPI.favorite(other_user, activity.id)
  629. assert length(Notification.for_user(user)) == 1
  630. {:ok, _} = CommonAPI.unfavorite(activity.id, other_user)
  631. assert Enum.empty?(Notification.for_user(user))
  632. end
  633. test "repeating an activity results in 1 notification, then 0 if the activity is deleted" do
  634. user = insert(:user)
  635. other_user = insert(:user)
  636. {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
  637. assert Enum.empty?(Notification.for_user(user))
  638. {:ok, _} = CommonAPI.repeat(activity.id, other_user)
  639. assert length(Notification.for_user(user)) == 1
  640. {:ok, _} = CommonAPI.delete(activity.id, user)
  641. assert Enum.empty?(Notification.for_user(user))
  642. end
  643. test "repeating an activity results in 1 notification, then 0 if the activity is unrepeated" do
  644. user = insert(:user)
  645. other_user = insert(:user)
  646. {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
  647. assert Enum.empty?(Notification.for_user(user))
  648. {:ok, _} = CommonAPI.repeat(activity.id, other_user)
  649. assert length(Notification.for_user(user)) == 1
  650. {:ok, _} = CommonAPI.unrepeat(activity.id, other_user)
  651. assert Enum.empty?(Notification.for_user(user))
  652. end
  653. test "liking an activity which is already deleted does not generate a notification" do
  654. user = insert(:user)
  655. other_user = insert(:user)
  656. {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
  657. assert Enum.empty?(Notification.for_user(user))
  658. {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
  659. assert Enum.empty?(Notification.for_user(user))
  660. {:error, :not_found} = CommonAPI.favorite(other_user, activity.id)
  661. assert Enum.empty?(Notification.for_user(user))
  662. end
  663. test "repeating an activity which is already deleted does not generate a notification" do
  664. user = insert(:user)
  665. other_user = insert(:user)
  666. {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
  667. assert Enum.empty?(Notification.for_user(user))
  668. {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
  669. assert Enum.empty?(Notification.for_user(user))
  670. {:error, _} = CommonAPI.repeat(activity.id, other_user)
  671. assert Enum.empty?(Notification.for_user(user))
  672. end
  673. test "replying to a deleted post without tagging does not generate a notification" do
  674. user = insert(:user)
  675. other_user = insert(:user)
  676. {:ok, activity} = CommonAPI.post(user, %{status: "test post"})
  677. {:ok, _deletion_activity} = CommonAPI.delete(activity.id, user)
  678. {:ok, _reply_activity} =
  679. CommonAPI.post(other_user, %{
  680. status: "test reply",
  681. in_reply_to_status_id: activity.id
  682. })
  683. assert Enum.empty?(Notification.for_user(user))
  684. end
  685. test "notifications are deleted if a local user is deleted" do
  686. user = insert(:user)
  687. other_user = insert(:user)
  688. {:ok, _activity} =
  689. CommonAPI.post(user, %{status: "hi @#{other_user.nickname}", visibility: "direct"})
  690. refute Enum.empty?(Notification.for_user(other_user))
  691. {:ok, job} = User.delete(user)
  692. ObanHelpers.perform(job)
  693. assert Enum.empty?(Notification.for_user(other_user))
  694. end
  695. test "notifications are deleted if a remote user is deleted" do
  696. remote_user = insert(:user)
  697. local_user = insert(:user)
  698. dm_message = %{
  699. "@context" => "https://www.w3.org/ns/activitystreams",
  700. "type" => "Create",
  701. "actor" => remote_user.ap_id,
  702. "id" => remote_user.ap_id <> "/activities/test",
  703. "to" => [local_user.ap_id],
  704. "cc" => [],
  705. "object" => %{
  706. "type" => "Note",
  707. "id" => remote_user.ap_id <> "/objects/test",
  708. "content" => "Hello!",
  709. "tag" => [
  710. %{
  711. "type" => "Mention",
  712. "href" => local_user.ap_id,
  713. "name" => "@#{local_user.nickname}"
  714. }
  715. ],
  716. "to" => [local_user.ap_id],
  717. "cc" => [],
  718. "attributedTo" => remote_user.ap_id
  719. }
  720. }
  721. {:ok, _dm_activity} = Transmogrifier.handle_incoming(dm_message)
  722. refute Enum.empty?(Notification.for_user(local_user))
  723. delete_user_message = %{
  724. "@context" => "https://www.w3.org/ns/activitystreams",
  725. "id" => remote_user.ap_id <> "/activities/delete",
  726. "actor" => remote_user.ap_id,
  727. "type" => "Delete",
  728. "object" => remote_user.ap_id
  729. }
  730. remote_user_url = remote_user.ap_id
  731. Tesla.Mock.mock(fn
  732. %{method: :get, url: ^remote_user_url} ->
  733. %Tesla.Env{status: 404, body: ""}
  734. end)
  735. {:ok, _delete_activity} = Transmogrifier.handle_incoming(delete_user_message)
  736. ObanHelpers.perform_all()
  737. assert Enum.empty?(Notification.for_user(local_user))
  738. end
  739. test "move activity generates a notification" do
  740. %{ap_id: old_ap_id} = old_user = insert(:user)
  741. %{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
  742. follower = insert(:user)
  743. other_follower = insert(:user, %{allow_following_move: false})
  744. User.follow(follower, old_user)
  745. User.follow(other_follower, old_user)
  746. Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user)
  747. ObanHelpers.perform_all()
  748. assert [
  749. %{
  750. activity: %{
  751. data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
  752. }
  753. }
  754. ] = Notification.for_user(follower)
  755. assert [
  756. %{
  757. activity: %{
  758. data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
  759. }
  760. }
  761. ] = Notification.for_user(other_follower)
  762. end
  763. end
  764. describe "for_user" do
  765. setup do
  766. user = insert(:user)
  767. {:ok, %{user: user}}
  768. end
  769. test "it returns notifications for muted user without notifications", %{user: user} do
  770. muted = insert(:user)
  771. {:ok, _user_relationships} = User.mute(user, muted, %{notifications: false})
  772. {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
  773. [notification] = Notification.for_user(user)
  774. assert notification.activity.object
  775. assert notification.seen
  776. end
  777. test "it doesn't return notifications for muted user with notifications", %{user: user} do
  778. muted = insert(:user)
  779. {:ok, _user_relationships} = User.mute(user, muted)
  780. {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
  781. assert Notification.for_user(user) == []
  782. end
  783. test "it doesn't return notifications for blocked user", %{user: user} do
  784. blocked = insert(:user)
  785. {:ok, _user_relationship} = User.block(user, blocked)
  786. {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
  787. assert Notification.for_user(user) == []
  788. end
  789. test "it doesn't return notifications for domain-blocked non-followed user", %{user: user} do
  790. blocked = insert(:user, ap_id: "http://some-domain.com")
  791. {:ok, user} = User.block_domain(user, "some-domain.com")
  792. {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
  793. assert Notification.for_user(user) == []
  794. end
  795. test "it returns notifications for domain-blocked but followed user" do
  796. user = insert(:user)
  797. blocked = insert(:user, ap_id: "http://some-domain.com")
  798. {:ok, user} = User.block_domain(user, "some-domain.com")
  799. {:ok, _, _} = User.follow(user, blocked)
  800. {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
  801. assert length(Notification.for_user(user)) == 1
  802. end
  803. test "it doesn't return notifications for muted thread", %{user: user} do
  804. another_user = insert(:user)
  805. {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"})
  806. {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"])
  807. assert Notification.for_user(user) == []
  808. end
  809. test "it returns notifications from a muted user when with_muted is set", %{user: user} do
  810. muted = insert(:user)
  811. {:ok, _user_relationships} = User.mute(user, muted)
  812. {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})
  813. assert length(Notification.for_user(user, %{with_muted: true})) == 1
  814. end
  815. test "it doesn't return notifications from a blocked user when with_muted is set", %{
  816. user: user
  817. } do
  818. blocked = insert(:user)
  819. {:ok, _user_relationship} = User.block(user, blocked)
  820. {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
  821. assert Enum.empty?(Notification.for_user(user, %{with_muted: true}))
  822. end
  823. test "when with_muted is set, " <>
  824. "it doesn't return notifications from a domain-blocked non-followed user",
  825. %{user: user} do
  826. blocked = insert(:user, ap_id: "http://some-domain.com")
  827. {:ok, user} = User.block_domain(user, "some-domain.com")
  828. {:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
  829. assert Enum.empty?(Notification.for_user(user, %{with_muted: true}))
  830. end
  831. test "it returns notifications from muted threads when with_muted is set", %{user: user} do
  832. another_user = insert(:user)
  833. {:ok, activity} = CommonAPI.post(another_user, %{status: "hey @#{user.nickname}"})
  834. {:ok, _} = Pleroma.ThreadMute.add_mute(user.id, activity.data["context"])
  835. assert length(Notification.for_user(user, %{with_muted: true})) == 1
  836. end
  837. test "it doesn't return notifications about mentions with filtered word", %{user: user} do
  838. insert(:filter, user: user, phrase: "cofe", hide: true)
  839. another_user = insert(:user)
  840. {:ok, _activity} = CommonAPI.post(another_user, %{status: "@#{user.nickname} got cofe?"})
  841. assert Enum.empty?(Notification.for_user(user))
  842. end
  843. test "it returns notifications about mentions with not hidden filtered word", %{user: user} do
  844. insert(:filter, user: user, phrase: "test", hide: false)
  845. another_user = insert(:user)
  846. {:ok, _} = CommonAPI.post(another_user, %{status: "@#{user.nickname} test"})
  847. assert length(Notification.for_user(user)) == 1
  848. end
  849. test "it returns notifications about favorites with filtered word", %{user: user} do
  850. insert(:filter, user: user, phrase: "cofe", hide: true)
  851. another_user = insert(:user)
  852. {:ok, activity} = CommonAPI.post(user, %{status: "Give me my cofe!"})
  853. {:ok, _} = CommonAPI.favorite(another_user, activity.id)
  854. assert length(Notification.for_user(user)) == 1
  855. end
  856. end
  857. end