logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma

notification_test.exs (38024B)


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