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.

1108 lines
36KB

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