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.

1089 lines
35KB

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