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