tests: add tests for User + cache interactions
[akkoma] / test / user_test.exs
1 defmodule Pleroma.UserTest do
2 alias Pleroma.Builders.UserBuilder
3 alias Pleroma.{User, Repo, Activity}
4 alias Pleroma.Web.OStatus
5 alias Pleroma.Web.Websub.WebsubClientSubscription
6 alias Pleroma.Web.CommonAPI
7 use Pleroma.DataCase
8
9 import Pleroma.Factory
10 import Ecto.Query
11
12 test "ap_id returns the activity pub id for the user" do
13 user = UserBuilder.build()
14
15 expected_ap_id = "#{Pleroma.Web.base_url()}/users/#{user.nickname}"
16
17 assert expected_ap_id == User.ap_id(user)
18 end
19
20 test "ap_followers returns the followers collection for the user" do
21 user = UserBuilder.build()
22
23 expected_followers_collection = "#{User.ap_id(user)}/followers"
24
25 assert expected_followers_collection == User.ap_followers(user)
26 end
27
28 test "follow takes a user and another user" do
29 user = insert(:user)
30 followed = insert(:user)
31
32 {:ok, user} = User.follow(user, followed)
33
34 user = Repo.get(User, user.id)
35
36 followed = User.get_by_ap_id(followed.ap_id)
37 assert followed.info["follower_count"] == 1
38
39 assert User.ap_followers(followed) in user.following
40 end
41
42 test "can't follow a deactivated users" do
43 user = insert(:user)
44 followed = insert(:user, info: %{"deactivated" => true})
45
46 {:error, _} = User.follow(user, followed)
47 end
48
49 test "can't follow a user who blocked us" do
50 blocker = insert(:user)
51 blockee = insert(:user)
52
53 {:ok, blocker} = User.block(blocker, blockee)
54
55 {:error, _} = User.follow(blockee, blocker)
56 end
57
58 test "local users do not automatically follow local locked accounts" do
59 follower = insert(:user, info: %{"locked" => true})
60 followed = insert(:user, info: %{"locked" => true})
61
62 {:ok, follower} = User.maybe_direct_follow(follower, followed)
63
64 refute User.following?(follower, followed)
65 end
66
67 # This is a somewhat useless test.
68 # test "following a remote user will ensure a websub subscription is present" do
69 # user = insert(:user)
70 # {:ok, followed} = OStatus.make_user("shp@social.heldscal.la")
71
72 # assert followed.local == false
73
74 # {:ok, user} = User.follow(user, followed)
75 # assert User.ap_followers(followed) in user.following
76
77 # query = from w in WebsubClientSubscription,
78 # where: w.topic == ^followed.info["topic"]
79 # websub = Repo.one(query)
80
81 # assert websub
82 # end
83
84 test "unfollow takes a user and another user" do
85 followed = insert(:user)
86 user = insert(:user, %{following: [User.ap_followers(followed)]})
87
88 {:ok, user, _activity} = User.unfollow(user, followed)
89
90 user = Repo.get(User, user.id)
91
92 assert user.following == []
93 end
94
95 test "unfollow doesn't unfollow yourself" do
96 user = insert(:user)
97
98 {:error, _} = User.unfollow(user, user)
99
100 user = Repo.get(User, user.id)
101 assert user.following == [user.ap_id]
102 end
103
104 test "test if a user is following another user" do
105 followed = insert(:user)
106 user = insert(:user, %{following: [User.ap_followers(followed)]})
107
108 assert User.following?(user, followed)
109 refute User.following?(followed, user)
110 end
111
112 describe "user registration" do
113 @full_user_data %{
114 bio: "A guy",
115 name: "my name",
116 nickname: "nick",
117 password: "test",
118 password_confirmation: "test",
119 email: "email@example.com"
120 }
121
122 test "it requires an email, name, nickname and password, bio is optional" do
123 @full_user_data
124 |> Map.keys()
125 |> Enum.each(fn key ->
126 params = Map.delete(@full_user_data, key)
127 changeset = User.register_changeset(%User{}, params)
128
129 assert if key == :bio, do: changeset.valid?, else: not changeset.valid?
130 end)
131 end
132
133 test "it sets the password_hash, ap_id and following fields" do
134 changeset = User.register_changeset(%User{}, @full_user_data)
135
136 assert changeset.valid?
137
138 assert is_binary(changeset.changes[:password_hash])
139 assert changeset.changes[:ap_id] == User.ap_id(%User{nickname: @full_user_data.nickname})
140
141 assert changeset.changes[:following] == [
142 User.ap_followers(%User{nickname: @full_user_data.nickname})
143 ]
144
145 assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers"
146 end
147 end
148
149 describe "fetching a user from nickname or trying to build one" do
150 test "gets an existing user" do
151 user = insert(:user)
152 fetched_user = User.get_or_fetch_by_nickname(user.nickname)
153
154 assert user == fetched_user
155 end
156
157 test "gets an existing user, case insensitive" do
158 user = insert(:user, nickname: "nick")
159 fetched_user = User.get_or_fetch_by_nickname("NICK")
160
161 assert user == fetched_user
162 end
163
164 test "fetches an external user via ostatus if no user exists" do
165 fetched_user = User.get_or_fetch_by_nickname("shp@social.heldscal.la")
166 assert fetched_user.nickname == "shp@social.heldscal.la"
167 end
168
169 test "returns nil if no user could be fetched" do
170 fetched_user = User.get_or_fetch_by_nickname("nonexistant@social.heldscal.la")
171 assert fetched_user == nil
172 end
173
174 test "returns nil for nonexistant local user" do
175 fetched_user = User.get_or_fetch_by_nickname("nonexistant")
176 assert fetched_user == nil
177 end
178
179 test "updates an existing user, if stale" do
180 a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800)
181
182 orig_user =
183 insert(
184 :user,
185 local: false,
186 nickname: "admin@mastodon.example.org",
187 ap_id: "http://mastodon.example.org/users/admin",
188 last_refreshed_at: a_week_ago
189 )
190
191 assert orig_user.last_refreshed_at == a_week_ago
192
193 user = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/admin")
194
195 refute user.last_refreshed_at == orig_user.last_refreshed_at
196 end
197 end
198
199 test "returns an ap_id for a user" do
200 user = insert(:user)
201
202 assert User.ap_id(user) ==
203 Pleroma.Web.Router.Helpers.o_status_url(
204 Pleroma.Web.Endpoint,
205 :feed_redirect,
206 user.nickname
207 )
208 end
209
210 test "returns an ap_followers link for a user" do
211 user = insert(:user)
212
213 assert User.ap_followers(user) ==
214 Pleroma.Web.Router.Helpers.o_status_url(
215 Pleroma.Web.Endpoint,
216 :feed_redirect,
217 user.nickname
218 ) <> "/followers"
219 end
220
221 describe "remote user creation changeset" do
222 @valid_remote %{
223 bio: "hello",
224 name: "Someone",
225 nickname: "a@b.de",
226 ap_id: "http...",
227 info: %{some: "info"},
228 avatar: %{some: "avatar"}
229 }
230
231 test "it confirms validity" do
232 cs = User.remote_user_creation(@valid_remote)
233 assert cs.valid?
234 end
235
236 test "it sets the follower_adress" do
237 cs = User.remote_user_creation(@valid_remote)
238 # remote users get a fake local follower address
239 assert cs.changes.follower_address ==
240 User.ap_followers(%User{nickname: @valid_remote[:nickname]})
241 end
242
243 test "it enforces the fqn format for nicknames" do
244 cs = User.remote_user_creation(%{@valid_remote | nickname: "bla"})
245 assert cs.changes.local == false
246 assert cs.changes.avatar
247 refute cs.valid?
248 end
249
250 test "it has required fields" do
251 [:name, :ap_id]
252 |> Enum.each(fn field ->
253 cs = User.remote_user_creation(Map.delete(@valid_remote, field))
254 refute cs.valid?
255 end)
256 end
257
258 test "it restricts some sizes" do
259 [bio: 5000, name: 100]
260 |> Enum.each(fn {field, size} ->
261 string = String.pad_leading(".", size)
262 cs = User.remote_user_creation(Map.put(@valid_remote, field, string))
263 assert cs.valid?
264
265 string = String.pad_leading(".", size + 1)
266 cs = User.remote_user_creation(Map.put(@valid_remote, field, string))
267 refute cs.valid?
268 end)
269 end
270 end
271
272 describe "followers and friends" do
273 test "gets all followers for a given user" do
274 user = insert(:user)
275 follower_one = insert(:user)
276 follower_two = insert(:user)
277 not_follower = insert(:user)
278
279 {:ok, follower_one} = User.follow(follower_one, user)
280 {:ok, follower_two} = User.follow(follower_two, user)
281
282 {:ok, res} = User.get_followers(user)
283
284 assert Enum.member?(res, follower_one)
285 assert Enum.member?(res, follower_two)
286 refute Enum.member?(res, not_follower)
287 end
288
289 test "gets all friends (followed users) for a given user" do
290 user = insert(:user)
291 followed_one = insert(:user)
292 followed_two = insert(:user)
293 not_followed = insert(:user)
294
295 {:ok, user} = User.follow(user, followed_one)
296 {:ok, user} = User.follow(user, followed_two)
297
298 {:ok, res} = User.get_friends(user)
299
300 followed_one = User.get_by_ap_id(followed_one.ap_id)
301 followed_two = User.get_by_ap_id(followed_two.ap_id)
302 assert Enum.member?(res, followed_one)
303 assert Enum.member?(res, followed_two)
304 refute Enum.member?(res, not_followed)
305 end
306 end
307
308 describe "updating note and follower count" do
309 test "it sets the info->note_count property" do
310 note = insert(:note)
311
312 user = User.get_by_ap_id(note.data["actor"])
313
314 assert user.info["note_count"] == nil
315
316 {:ok, user} = User.update_note_count(user)
317
318 assert user.info["note_count"] == 1
319 end
320
321 test "it increases the info->note_count property" do
322 note = insert(:note)
323 user = User.get_by_ap_id(note.data["actor"])
324
325 assert user.info["note_count"] == nil
326
327 {:ok, user} = User.increase_note_count(user)
328
329 assert user.info["note_count"] == 1
330
331 {:ok, user} = User.increase_note_count(user)
332
333 assert user.info["note_count"] == 2
334 end
335
336 test "it decreases the info->note_count property" do
337 note = insert(:note)
338 user = User.get_by_ap_id(note.data["actor"])
339
340 assert user.info["note_count"] == nil
341
342 {:ok, user} = User.increase_note_count(user)
343
344 assert user.info["note_count"] == 1
345
346 {:ok, user} = User.decrease_note_count(user)
347
348 assert user.info["note_count"] == 0
349
350 {:ok, user} = User.decrease_note_count(user)
351
352 assert user.info["note_count"] == 0
353 end
354
355 test "it sets the info->follower_count property" do
356 user = insert(:user)
357 follower = insert(:user)
358
359 User.follow(follower, user)
360
361 assert user.info["follower_count"] == nil
362
363 {:ok, user} = User.update_follower_count(user)
364
365 assert user.info["follower_count"] == 1
366 end
367 end
368
369 describe "blocks" do
370 test "it blocks people" do
371 user = insert(:user)
372 blocked_user = insert(:user)
373
374 refute User.blocks?(user, blocked_user)
375
376 {:ok, user} = User.block(user, blocked_user)
377
378 assert User.blocks?(user, blocked_user)
379 end
380
381 test "it unblocks users" do
382 user = insert(:user)
383 blocked_user = insert(:user)
384
385 {:ok, user} = User.block(user, blocked_user)
386 {:ok, user} = User.unblock(user, blocked_user)
387
388 refute User.blocks?(user, blocked_user)
389 end
390
391 test "blocks tear down cyclical follow relationships" do
392 blocker = insert(:user)
393 blocked = insert(:user)
394
395 {:ok, blocker} = User.follow(blocker, blocked)
396 {:ok, blocked} = User.follow(blocked, blocker)
397
398 assert User.following?(blocker, blocked)
399 assert User.following?(blocked, blocker)
400
401 {:ok, blocker} = User.block(blocker, blocked)
402 blocked = Repo.get(User, blocked.id)
403
404 assert User.blocks?(blocker, blocked)
405
406 refute User.following?(blocker, blocked)
407 refute User.following?(blocked, blocker)
408 end
409
410 test "blocks tear down blocker->blocked follow relationships" do
411 blocker = insert(:user)
412 blocked = insert(:user)
413
414 {:ok, blocker} = User.follow(blocker, blocked)
415
416 assert User.following?(blocker, blocked)
417 refute User.following?(blocked, blocker)
418
419 {:ok, blocker} = User.block(blocker, blocked)
420 blocked = Repo.get(User, blocked.id)
421
422 assert User.blocks?(blocker, blocked)
423
424 refute User.following?(blocker, blocked)
425 refute User.following?(blocked, blocker)
426 end
427
428 test "blocks tear down blocked->blocker follow relationships" do
429 blocker = insert(:user)
430 blocked = insert(:user)
431
432 {:ok, blocked} = User.follow(blocked, blocker)
433
434 refute User.following?(blocker, blocked)
435 assert User.following?(blocked, blocker)
436
437 {:ok, blocker} = User.block(blocker, blocked)
438 blocked = Repo.get(User, blocked.id)
439
440 assert User.blocks?(blocker, blocked)
441
442 refute User.following?(blocker, blocked)
443 refute User.following?(blocked, blocker)
444 end
445 end
446
447 describe "domain blocking" do
448 test "blocks domains" do
449 user = insert(:user)
450 collateral_user = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
451
452 {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
453
454 assert User.blocks?(user, collateral_user)
455 end
456
457 test "unblocks domains" do
458 user = insert(:user)
459 collateral_user = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
460
461 {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
462 {:ok, user} = User.unblock_domain(user, "awful-and-rude-instance.com")
463
464 refute User.blocks?(user, collateral_user)
465 end
466 end
467
468 test "get recipients from activity" do
469 actor = insert(:user)
470 user = insert(:user, local: true)
471 user_two = insert(:user, local: false)
472 addressed = insert(:user, local: true)
473 addressed_remote = insert(:user, local: false)
474
475 {:ok, activity} =
476 CommonAPI.post(actor, %{
477 "status" => "hey @#{addressed.nickname} @#{addressed_remote.nickname}"
478 })
479
480 assert [addressed] == User.get_recipients_from_activity(activity)
481
482 {:ok, user} = User.follow(user, actor)
483 {:ok, _user_two} = User.follow(user_two, actor)
484 recipients = User.get_recipients_from_activity(activity)
485 assert length(recipients) == 2
486 assert user in recipients
487 assert addressed in recipients
488 end
489
490 test ".deactivate can de-activate then re-activate a user" do
491 user = insert(:user)
492 assert false == !!user.info["deactivated"]
493 {:ok, user} = User.deactivate(user)
494 assert true == user.info["deactivated"]
495 {:ok, user} = User.deactivate(user, false)
496 assert false == !!user.info["deactivated"]
497 end
498
499 test ".delete deactivates a user, all follow relationships and all create activities" do
500 user = insert(:user)
501 followed = insert(:user)
502 follower = insert(:user)
503
504 {:ok, user} = User.follow(user, followed)
505 {:ok, follower} = User.follow(follower, user)
506
507 {:ok, activity} = CommonAPI.post(user, %{"status" => "2hu"})
508 {:ok, activity_two} = CommonAPI.post(follower, %{"status" => "3hu"})
509
510 {:ok, _, _} = CommonAPI.favorite(activity_two.id, user)
511 {:ok, _, _} = CommonAPI.favorite(activity.id, follower)
512 {:ok, _, _} = CommonAPI.repeat(activity.id, follower)
513
514 :ok = User.delete(user)
515
516 followed = Repo.get(User, followed.id)
517 follower = Repo.get(User, follower.id)
518 user = Repo.get(User, user.id)
519
520 assert user.info["deactivated"]
521
522 refute User.following?(user, followed)
523 refute User.following?(followed, follower)
524
525 # TODO: Remove favorites, repeats, delete activities.
526
527 refute Repo.get(Activity, activity.id)
528 end
529
530 test "get_public_key_for_ap_id fetches a user that's not in the db" do
531 assert {:ok, _key} = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin")
532 end
533
534 test "insert or update a user from given data" do
535 user = insert(:user, %{nickname: "nick@name.de"})
536 data = %{ap_id: user.ap_id <> "xxx", name: user.name, nickname: user.nickname}
537
538 assert {:ok, %User{}} = User.insert_or_update_user(data)
539 end
540
541 describe "per-user rich-text filtering" do
542 test "html_filter_policy returns nil when rich-text is enabled" do
543 user = insert(:user)
544
545 assert nil == User.html_filter_policy(user)
546 end
547
548 test "html_filter_policy returns TwitterText scrubber when rich-text is disabled" do
549 user = insert(:user, %{info: %{"no_rich_text" => true}})
550
551 assert Pleroma.HTML.Scrubber.TwitterText == User.html_filter_policy(user)
552 end
553 end
554
555 describe "caching" do
556 test "invalidate_cache works" do
557 user = insert(:user)
558 user_info = User.get_cached_user_info(user)
559
560 User.invalidate_cache(user)
561
562 {:ok, nil} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
563 {:ok, nil} = Cachex.get(:user_cache, "nickname:#{user.nickname}")
564 {:ok, nil} = Cachex.get(:user_cache, "user_info:#{user.id}")
565 end
566
567 test "User.delete() plugs any possible zombie objects" do
568 user = insert(:user)
569
570 {:ok, _} = User.delete(user)
571
572 {:ok, cached_user} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
573
574 assert cached_user != user
575
576 {:ok, cached_user} = Cachex.get(:user_cache, "nickname:#{user.ap_id}")
577
578 assert cached_user != user
579 end
580 end
581 end