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.

1072 line
34KB

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