1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Conversation.ParticipationTest do
8 alias Pleroma.Conversation
9 alias Pleroma.Conversation.Participation
12 alias Pleroma.Web.CommonAPI
14 test "getting a participation will also preload things" do
16 other_user = insert(:user)
19 CommonAPI.post(user, %{"status" => "Hey @#{other_user.nickname}.", "visibility" => "direct"})
21 [participation] = Participation.for_user(user)
23 participation = Participation.get(participation.id, preload: [:conversation])
25 assert %Pleroma.Conversation{} = participation.conversation
28 test "for a new conversation or a reply, it doesn't mark the author's participation as unread" do
30 other_user = insert(:user)
33 CommonAPI.post(user, %{"status" => "Hey @#{other_user.nickname}.", "visibility" => "direct"})
35 user = User.get_cached_by_id(user.id)
36 other_user = User.get_cached_by_id(other_user.id)
38 [%{read: true}] = Participation.for_user(user)
39 [%{read: false} = participation] = Participation.for_user(other_user)
41 assert User.get_cached_by_id(user.id).unread_conversation_count == 0
42 assert User.get_cached_by_id(other_user.id).unread_conversation_count == 1
45 CommonAPI.post(other_user, %{
46 "status" => "Hey @#{user.nickname}.",
47 "visibility" => "direct",
48 "in_reply_to_conversation_id" => participation.id
51 user = User.get_cached_by_id(user.id)
52 other_user = User.get_cached_by_id(other_user.id)
54 [%{read: false}] = Participation.for_user(user)
55 [%{read: true}] = Participation.for_user(other_user)
57 assert User.get_cached_by_id(user.id).unread_conversation_count == 1
58 assert User.get_cached_by_id(other_user.id).unread_conversation_count == 0
61 test "for a new conversation, it sets the recipents of the participation" do
63 other_user = insert(:user)
64 third_user = insert(:user)
67 CommonAPI.post(user, %{"status" => "Hey @#{other_user.nickname}.", "visibility" => "direct"})
69 user = User.get_cached_by_id(user.id)
70 other_user = User.get_cached_by_id(other_user.id)
71 [participation] = Participation.for_user(user)
72 participation = Pleroma.Repo.preload(participation, :recipients)
74 assert length(participation.recipients) == 2
75 assert user in participation.recipients
76 assert other_user in participation.recipients
78 # Mentioning another user in the same conversation will not add a new recipients.
81 CommonAPI.post(user, %{
82 "in_reply_to_status_id" => activity.id,
83 "status" => "Hey @#{third_user.nickname}.",
84 "visibility" => "direct"
87 [participation] = Participation.for_user(user)
88 participation = Pleroma.Repo.preload(participation, :recipients)
90 assert length(participation.recipients) == 2
93 test "it creates a participation for a conversation and a user" do
95 conversation = insert(:conversation)
97 {:ok, %Participation{} = participation} =
98 Participation.create_for_user_and_conversation(user, conversation)
100 assert participation.user_id == user.id
101 assert participation.conversation_id == conversation.id
103 # Needed because updated_at is accurate down to a second
106 # Creating again returns the same participation
107 {:ok, %Participation{} = participation_two} =
108 Participation.create_for_user_and_conversation(user, conversation)
110 assert participation.id == participation_two.id
111 refute participation.updated_at == participation_two.updated_at
114 test "recreating an existing participations sets it to unread" do
115 participation = insert(:participation, %{read: true})
117 {:ok, participation} =
118 Participation.create_for_user_and_conversation(
120 participation.conversation
123 refute participation.read
126 test "it marks a participation as read" do
127 participation = insert(:participation, %{read: false})
128 {:ok, participation} = Participation.mark_as_read(participation)
130 assert participation.read
133 test "it marks a participation as unread" do
134 participation = insert(:participation, %{read: true})
135 {:ok, participation} = Participation.mark_as_unread(participation)
137 refute participation.read
140 test "it marks all the user's participations as read" do
142 other_user = insert(:user)
143 participation1 = insert(:participation, %{read: false, user: user})
144 participation2 = insert(:participation, %{read: false, user: user})
145 participation3 = insert(:participation, %{read: false, user: other_user})
147 {:ok, _, [%{read: true}, %{read: true}]} = Participation.mark_all_as_read(user)
149 assert Participation.get(participation1.id).read == true
150 assert Participation.get(participation2.id).read == true
151 assert Participation.get(participation3.id).read == false
154 test "gets all the participations for a user, ordered by updated at descending" do
156 {:ok, activity_one} = CommonAPI.post(user, %{"status" => "x", "visibility" => "direct"})
157 {:ok, activity_two} = CommonAPI.post(user, %{"status" => "x", "visibility" => "direct"})
159 {:ok, activity_three} =
160 CommonAPI.post(user, %{
162 "visibility" => "direct",
163 "in_reply_to_status_id" => activity_one.id
166 # Offset participations because the accuracy of updated_at is down to a second
168 for {activity, offset} <- [{activity_two, 1}, {activity_three, 2}] do
169 conversation = Conversation.get_for_ap_id(activity.data["context"])
170 participation = Participation.for_user_and_conversation(user, conversation)
171 updated_at = NaiveDateTime.add(Map.get(participation, :updated_at), offset)
173 Ecto.Changeset.change(participation, %{updated_at: updated_at})
177 assert [participation_one, participation_two] = Participation.for_user(user)
179 object2 = Pleroma.Object.normalize(activity_two)
180 object3 = Pleroma.Object.normalize(activity_three)
182 user = Repo.get(Pleroma.User, user.id)
184 assert participation_one.conversation.ap_id == object3.data["context"]
185 assert participation_two.conversation.ap_id == object2.data["context"]
186 assert participation_one.conversation.users == [user]
189 assert [participation_one] = Participation.for_user(user, %{"limit" => 1})
191 assert participation_one.conversation.ap_id == object3.data["context"]
193 # With last_activity_id
194 assert [participation_one] =
195 Participation.for_user_with_last_activity_id(user, %{"limit" => 1})
197 assert participation_one.last_activity_id == activity_three.id
200 test "Doesn't die when the conversation gets empty" do
203 {:ok, activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
204 [participation] = Participation.for_user_with_last_activity_id(user)
206 assert participation.last_activity_id == activity.id
208 {:ok, _} = CommonAPI.delete(activity.id, user)
210 [] = Participation.for_user_with_last_activity_id(user)
213 test "it sets recipients, always keeping the owner of the participation even when not explicitly set" do
215 other_user = insert(:user)
217 {:ok, _activity} = CommonAPI.post(user, %{"status" => ".", "visibility" => "direct"})
218 [participation] = Participation.for_user_with_last_activity_id(user)
220 participation = Repo.preload(participation, :recipients)
221 user = User.get_cached_by_id(user.id)
223 assert participation.recipients |> length() == 1
224 assert user in participation.recipients
226 {:ok, participation} = Participation.set_recipients(participation, [other_user.id])
228 assert participation.recipients |> length() == 2
229 assert user in participation.recipients
230 assert other_user in participation.recipients
233 describe "blocking" do
234 test "when the user blocks a recipient, the existing conversations with them are marked as read" do
235 blocker = insert(:user)
236 blocked = insert(:user)
237 third_user = insert(:user)
240 CommonAPI.post(third_user, %{
241 "status" => "Hi @#{blocker.nickname}",
242 "visibility" => "direct"
246 CommonAPI.post(third_user, %{
247 "status" => "Hi @#{blocker.nickname}, @#{blocked.nickname}",
248 "visibility" => "direct"
252 CommonAPI.post(blocked, %{
253 "status" => "Hi @#{blocker.nickname}",
254 "visibility" => "direct"
258 CommonAPI.post(blocked, %{
259 "status" => "Hi @#{blocker.nickname}, @#{third_user.nickname}",
260 "visibility" => "direct"
263 assert [%{read: false}, %{read: false}, %{read: false}, %{read: false}] =
264 Participation.for_user(blocker)
266 assert User.get_cached_by_id(blocker.id).unread_conversation_count == 4
268 {:ok, _user_relationship} = User.block(blocker, blocked)
270 # The conversations with the blocked user are marked as read
271 assert [%{read: true}, %{read: true}, %{read: true}, %{read: false}] =
272 Participation.for_user(blocker)
274 assert User.get_cached_by_id(blocker.id).unread_conversation_count == 1
276 # The conversation is not marked as read for the blocked user
277 assert [_, _, %{read: false}] = Participation.for_user(blocked)
278 assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1
280 # The conversation is not marked as read for the third user
281 assert [%{read: false}, _, _] = Participation.for_user(third_user)
282 assert User.get_cached_by_id(third_user.id).unread_conversation_count == 1
285 test "the new conversation with the blocked user is not marked as unread " do
286 blocker = insert(:user)
287 blocked = insert(:user)
288 third_user = insert(:user)
290 {:ok, _user_relationship} = User.block(blocker, blocked)
292 # When the blocked user is the author
294 CommonAPI.post(blocked, %{
295 "status" => "Hi @#{blocker.nickname}",
296 "visibility" => "direct"
299 assert [%{read: true}] = Participation.for_user(blocker)
300 assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0
302 # When the blocked user is a recipient
304 CommonAPI.post(third_user, %{
305 "status" => "Hi @#{blocker.nickname}, @#{blocked.nickname}",
306 "visibility" => "direct"
309 assert [%{read: true}, %{read: true}] = Participation.for_user(blocker)
310 assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0
312 assert [%{read: false}, _] = Participation.for_user(blocked)
313 assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1
316 test "the conversation with the blocked user is not marked as unread on a reply" do
317 blocker = insert(:user)
318 blocked = insert(:user)
319 third_user = insert(:user)
322 CommonAPI.post(blocker, %{
323 "status" => "Hi @#{third_user.nickname}, @#{blocked.nickname}",
324 "visibility" => "direct"
327 {:ok, _user_relationship} = User.block(blocker, blocked)
328 assert [%{read: true}] = Participation.for_user(blocker)
329 assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0
331 assert [blocked_participation] = Participation.for_user(blocked)
333 # When it's a reply from the blocked user
335 CommonAPI.post(blocked, %{
337 "visibility" => "direct",
338 "in_reply_to_conversation_id" => blocked_participation.id
341 assert [%{read: true}] = Participation.for_user(blocker)
342 assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0
344 assert [third_user_participation] = Participation.for_user(third_user)
346 # When it's a reply from the third user
348 CommonAPI.post(third_user, %{
350 "visibility" => "direct",
351 "in_reply_to_conversation_id" => third_user_participation.id
354 assert [%{read: true}] = Participation.for_user(blocker)
355 assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0
357 # Marked as unread for the blocked user
358 assert [%{read: false}] = Participation.for_user(blocked)
359 assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1