59a1b6492d75df3e0924e69a1c5183c1522686dc
[akkoma] / test / pleroma / conversation / participation_test.exs
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
5 defmodule Pleroma.Conversation.ParticipationTest do
6 use Pleroma.DataCase
7 import Pleroma.Factory
8 alias Pleroma.Conversation
9 alias Pleroma.Conversation.Participation
10 alias Pleroma.Repo
11 alias Pleroma.User
12 alias Pleroma.Web.CommonAPI
13
14 test "getting a participation will also preload things" do
15 user = insert(:user)
16 other_user = insert(:user)
17
18 {:ok, _activity} =
19 CommonAPI.post(user, %{status: "Hey @#{other_user.nickname}.", visibility: "direct"})
20
21 [participation] = Participation.for_user(user)
22
23 participation = Participation.get(participation.id, preload: [:conversation])
24
25 assert %Pleroma.Conversation{} = participation.conversation
26 end
27
28 test "for a new conversation or a reply, it doesn't mark the author's participation as unread" do
29 user = insert(:user)
30 other_user = insert(:user)
31
32 {:ok, _} =
33 CommonAPI.post(user, %{status: "Hey @#{other_user.nickname}.", visibility: "direct"})
34
35 user = User.get_cached_by_id(user.id)
36 other_user = User.get_cached_by_id(other_user.id)
37
38 [%{read: true}] = Participation.for_user(user)
39 [%{read: false} = participation] = Participation.for_user(other_user)
40
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
43
44 {:ok, _} =
45 CommonAPI.post(other_user, %{
46 status: "Hey @#{user.nickname}.",
47 visibility: "direct",
48 in_reply_to_conversation_id: participation.id
49 })
50
51 user = User.get_cached_by_id(user.id)
52 other_user = User.get_cached_by_id(other_user.id)
53
54 [%{read: false}] = Participation.for_user(user)
55 [%{read: true}] = Participation.for_user(other_user)
56
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
59 end
60
61 test "for a new conversation, it sets the recipents of the participation" do
62 user = insert(:user)
63 other_user = insert(:user)
64 third_user = insert(:user)
65
66 {:ok, activity} =
67 CommonAPI.post(user, %{status: "Hey @#{other_user.nickname}.", visibility: "direct"})
68
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)
73
74 assert length(participation.recipients) == 2
75 assert user in participation.recipients
76 assert other_user in participation.recipients
77
78 # Mentioning another user in the same conversation will not add a new recipients.
79
80 {:ok, _activity} =
81 CommonAPI.post(user, %{
82 in_reply_to_status_id: activity.id,
83 status: "Hey @#{third_user.nickname}.",
84 visibility: "direct"
85 })
86
87 [participation] = Participation.for_user(user)
88 participation = Pleroma.Repo.preload(participation, :recipients)
89
90 assert length(participation.recipients) == 2
91 end
92
93 test "it creates a participation for a conversation and a user" do
94 user = insert(:user)
95 conversation = insert(:conversation)
96
97 {:ok, %Participation{} = participation} =
98 Participation.create_for_user_and_conversation(user, conversation)
99
100 assert participation.user_id == user.id
101 assert participation.conversation_id == conversation.id
102
103 # Needed because updated_at is accurate down to a second
104 :timer.sleep(1000)
105
106 # Creating again returns the same participation
107 {:ok, %Participation{} = participation_two} =
108 Participation.create_for_user_and_conversation(user, conversation)
109
110 assert participation.id == participation_two.id
111 refute participation.updated_at == participation_two.updated_at
112 end
113
114 test "recreating an existing participations sets it to unread" do
115 participation = insert(:participation, %{read: true})
116
117 {:ok, participation} =
118 Participation.create_for_user_and_conversation(
119 participation.user,
120 participation.conversation
121 )
122
123 refute participation.read
124 end
125
126 test "it marks a participation as read" do
127 participation = insert(:participation, %{read: false})
128 {:ok, updated_participation} = Participation.mark_as_read(participation)
129
130 assert updated_participation.read
131 assert updated_participation.updated_at == participation.updated_at
132 end
133
134 test "it marks a participation as unread" do
135 participation = insert(:participation, %{read: true})
136 {:ok, participation} = Participation.mark_as_unread(participation)
137
138 refute participation.read
139 end
140
141 test "it marks all the user's participations as read" do
142 user = insert(:user)
143 other_user = insert(:user)
144 participation1 = insert(:participation, %{read: false, user: user})
145 participation2 = insert(:participation, %{read: false, user: user})
146 participation3 = insert(:participation, %{read: false, user: other_user})
147
148 {:ok, _, [%{read: true}, %{read: true}]} = Participation.mark_all_as_read(user)
149
150 assert Participation.get(participation1.id).read == true
151 assert Participation.get(participation2.id).read == true
152 assert Participation.get(participation3.id).read == false
153 end
154
155 test "gets all the participations for a user, ordered by updated at descending" do
156 user = insert(:user)
157 {:ok, activity_one} = CommonAPI.post(user, %{status: "x", visibility: "direct"})
158 {:ok, activity_two} = CommonAPI.post(user, %{status: "x", visibility: "direct"})
159
160 {:ok, activity_three} =
161 CommonAPI.post(user, %{
162 status: "x",
163 visibility: "direct",
164 in_reply_to_status_id: activity_one.id
165 })
166
167 # Offset participations because the accuracy of updated_at is down to a second
168
169 for {activity, offset} <- [{activity_two, 1}, {activity_three, 2}] do
170 conversation = Conversation.get_for_ap_id(activity.data["context"])
171 participation = Participation.for_user_and_conversation(user, conversation)
172 updated_at = NaiveDateTime.add(Map.get(participation, :updated_at), offset)
173
174 Ecto.Changeset.change(participation, %{updated_at: updated_at})
175 |> Repo.update!()
176 end
177
178 assert [participation_one, participation_two] = Participation.for_user(user)
179
180 object2 = Pleroma.Object.normalize(activity_two)
181 object3 = Pleroma.Object.normalize(activity_three)
182
183 user = Repo.get(Pleroma.User, user.id)
184
185 assert participation_one.conversation.ap_id == object3.data["context"]
186 assert participation_two.conversation.ap_id == object2.data["context"]
187 assert participation_one.conversation.users == [user]
188
189 # Pagination
190 assert [participation_one] = Participation.for_user(user, %{"limit" => 1})
191
192 assert participation_one.conversation.ap_id == object3.data["context"]
193
194 # With last_activity_id
195 assert [participation_one] =
196 Participation.for_user_with_last_activity_id(user, %{"limit" => 1})
197
198 assert participation_one.last_activity_id == activity_three.id
199 end
200
201 test "Doesn't die when the conversation gets empty" do
202 user = insert(:user)
203
204 {:ok, activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
205 [participation] = Participation.for_user_with_last_activity_id(user)
206
207 assert participation.last_activity_id == activity.id
208
209 {:ok, _} = CommonAPI.delete(activity.id, user)
210
211 [] = Participation.for_user_with_last_activity_id(user)
212 end
213
214 test "it sets recipients, always keeping the owner of the participation even when not explicitly set" do
215 user = insert(:user)
216 other_user = insert(:user)
217
218 {:ok, _activity} = CommonAPI.post(user, %{status: ".", visibility: "direct"})
219 [participation] = Participation.for_user_with_last_activity_id(user)
220
221 participation = Repo.preload(participation, :recipients)
222 user = User.get_cached_by_id(user.id)
223
224 assert participation.recipients |> length() == 1
225 assert user in participation.recipients
226
227 {:ok, participation} = Participation.set_recipients(participation, [other_user.id])
228
229 assert participation.recipients |> length() == 2
230 assert user in participation.recipients
231 assert other_user in participation.recipients
232 end
233
234 describe "blocking" do
235 test "when the user blocks a recipient, the existing conversations with them are marked as read" do
236 blocker = insert(:user)
237 blocked = insert(:user)
238 third_user = insert(:user)
239
240 {:ok, _direct1} =
241 CommonAPI.post(third_user, %{
242 status: "Hi @#{blocker.nickname}",
243 visibility: "direct"
244 })
245
246 {:ok, _direct2} =
247 CommonAPI.post(third_user, %{
248 status: "Hi @#{blocker.nickname}, @#{blocked.nickname}",
249 visibility: "direct"
250 })
251
252 {:ok, _direct3} =
253 CommonAPI.post(blocked, %{
254 status: "Hi @#{blocker.nickname}",
255 visibility: "direct"
256 })
257
258 {:ok, _direct4} =
259 CommonAPI.post(blocked, %{
260 status: "Hi @#{blocker.nickname}, @#{third_user.nickname}",
261 visibility: "direct"
262 })
263
264 assert [%{read: false}, %{read: false}, %{read: false}, %{read: false}] =
265 Participation.for_user(blocker)
266
267 assert User.get_cached_by_id(blocker.id).unread_conversation_count == 4
268
269 {:ok, _user_relationship} = User.block(blocker, blocked)
270
271 # The conversations with the blocked user are marked as read
272 assert [%{read: true}, %{read: true}, %{read: true}, %{read: false}] =
273 Participation.for_user(blocker)
274
275 assert User.get_cached_by_id(blocker.id).unread_conversation_count == 1
276
277 # The conversation is not marked as read for the blocked user
278 assert [_, _, %{read: false}] = Participation.for_user(blocked)
279 assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1
280
281 # The conversation is not marked as read for the third user
282 assert [%{read: false}, _, _] = Participation.for_user(third_user)
283 assert User.get_cached_by_id(third_user.id).unread_conversation_count == 1
284 end
285
286 test "the new conversation with the blocked user is not marked as unread " do
287 blocker = insert(:user)
288 blocked = insert(:user)
289 third_user = insert(:user)
290
291 {:ok, _user_relationship} = User.block(blocker, blocked)
292
293 # When the blocked user is the author
294 {:ok, _direct1} =
295 CommonAPI.post(blocked, %{
296 status: "Hi @#{blocker.nickname}",
297 visibility: "direct"
298 })
299
300 assert [%{read: true}] = Participation.for_user(blocker)
301 assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0
302
303 # When the blocked user is a recipient
304 {:ok, _direct2} =
305 CommonAPI.post(third_user, %{
306 status: "Hi @#{blocker.nickname}, @#{blocked.nickname}",
307 visibility: "direct"
308 })
309
310 assert [%{read: true}, %{read: true}] = Participation.for_user(blocker)
311 assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0
312
313 assert [%{read: false}, _] = Participation.for_user(blocked)
314 assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1
315 end
316
317 test "the conversation with the blocked user is not marked as unread on a reply" do
318 blocker = insert(:user)
319 blocked = insert(:user)
320 third_user = insert(:user)
321
322 {:ok, _direct1} =
323 CommonAPI.post(blocker, %{
324 status: "Hi @#{third_user.nickname}, @#{blocked.nickname}",
325 visibility: "direct"
326 })
327
328 {:ok, _user_relationship} = User.block(blocker, blocked)
329 assert [%{read: true}] = Participation.for_user(blocker)
330 assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0
331
332 assert [blocked_participation] = Participation.for_user(blocked)
333
334 # When it's a reply from the blocked user
335 {:ok, _direct2} =
336 CommonAPI.post(blocked, %{
337 status: "reply",
338 visibility: "direct",
339 in_reply_to_conversation_id: blocked_participation.id
340 })
341
342 assert [%{read: true}] = Participation.for_user(blocker)
343 assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0
344
345 assert [third_user_participation] = Participation.for_user(third_user)
346
347 # When it's a reply from the third user
348 {:ok, _direct3} =
349 CommonAPI.post(third_user, %{
350 status: "reply",
351 visibility: "direct",
352 in_reply_to_conversation_id: third_user_participation.id
353 })
354
355 assert [%{read: true}] = Participation.for_user(blocker)
356 assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0
357
358 # Marked as unread for the blocked user
359 assert [%{read: false}] = Participation.for_user(blocked)
360 assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1
361 end
362 end
363 end