[#483] User.get_by_nickname/1: ensured case-insensitive matching for local FQN. Added...
[akkoma] / test / user_test.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.UserTest do
6 alias Pleroma.Builders.UserBuilder
7 alias Pleroma.{User, Repo, Activity}
8 alias Pleroma.Web.CommonAPI
9 use Pleroma.DataCase
10
11 import Pleroma.Factory
12
13 setup_all do
14 Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
15 :ok
16 end
17
18 describe "when tags are nil" do
19 test "tagging a user" do
20 user = insert(:user, %{tags: nil})
21 user = User.tag(user, ["cool", "dude"])
22
23 assert "cool" in user.tags
24 assert "dude" in user.tags
25 end
26
27 test "untagging a user" do
28 user = insert(:user, %{tags: nil})
29 user = User.untag(user, ["cool", "dude"])
30
31 assert user.tags == []
32 end
33 end
34
35 test "ap_id returns the activity pub id for the user" do
36 user = UserBuilder.build()
37
38 expected_ap_id = "#{Pleroma.Web.base_url()}/users/#{user.nickname}"
39
40 assert expected_ap_id == User.ap_id(user)
41 end
42
43 test "ap_followers returns the followers collection for the user" do
44 user = UserBuilder.build()
45
46 expected_followers_collection = "#{User.ap_id(user)}/followers"
47
48 assert expected_followers_collection == User.ap_followers(user)
49 end
50
51 test "follow takes a user and another user" do
52 user = insert(:user)
53 followed = insert(:user)
54
55 {:ok, user} = User.follow(user, followed)
56
57 user = Repo.get(User, user.id)
58
59 followed = User.get_by_ap_id(followed.ap_id)
60 assert followed.info.follower_count == 1
61
62 assert User.ap_followers(followed) in user.following
63 end
64
65 test "can't follow a deactivated users" do
66 user = insert(:user)
67 followed = insert(:user, info: %{deactivated: true})
68
69 {:error, _} = User.follow(user, followed)
70 end
71
72 test "can't follow a user who blocked us" do
73 blocker = insert(:user)
74 blockee = insert(:user)
75
76 {:ok, blocker} = User.block(blocker, blockee)
77
78 {:error, _} = User.follow(blockee, blocker)
79 end
80
81 test "local users do not automatically follow local locked accounts" do
82 follower = insert(:user, info: %{locked: true})
83 followed = insert(:user, info: %{locked: true})
84
85 {:ok, follower} = User.maybe_direct_follow(follower, followed)
86
87 refute User.following?(follower, followed)
88 end
89
90 # This is a somewhat useless test.
91 # test "following a remote user will ensure a websub subscription is present" do
92 # user = insert(:user)
93 # {:ok, followed} = OStatus.make_user("shp@social.heldscal.la")
94
95 # assert followed.local == false
96
97 # {:ok, user} = User.follow(user, followed)
98 # assert User.ap_followers(followed) in user.following
99
100 # query = from w in WebsubClientSubscription,
101 # where: w.topic == ^followed.info["topic"]
102 # websub = Repo.one(query)
103
104 # assert websub
105 # end
106
107 test "unfollow takes a user and another user" do
108 followed = insert(:user)
109 user = insert(:user, %{following: [User.ap_followers(followed)]})
110
111 {:ok, user, _activity} = User.unfollow(user, followed)
112
113 user = Repo.get(User, user.id)
114
115 assert user.following == []
116 end
117
118 test "unfollow doesn't unfollow yourself" do
119 user = insert(:user)
120
121 {:error, _} = User.unfollow(user, user)
122
123 user = Repo.get(User, user.id)
124 assert user.following == [user.ap_id]
125 end
126
127 test "test if a user is following another user" do
128 followed = insert(:user)
129 user = insert(:user, %{following: [User.ap_followers(followed)]})
130
131 assert User.following?(user, followed)
132 refute User.following?(followed, user)
133 end
134
135 describe "user registration" do
136 @full_user_data %{
137 bio: "A guy",
138 name: "my name",
139 nickname: "nick",
140 password: "test",
141 password_confirmation: "test",
142 email: "email@example.com"
143 }
144
145 test "it requires an email, name, nickname and password, bio is optional" do
146 @full_user_data
147 |> Map.keys()
148 |> Enum.each(fn key ->
149 params = Map.delete(@full_user_data, key)
150 changeset = User.register_changeset(%User{}, params)
151
152 assert if key == :bio, do: changeset.valid?, else: not changeset.valid?
153 end)
154 end
155
156 test "it restricts certain nicknames" do
157 [restricted_name | _] = Pleroma.Config.get([Pleroma.User, :restricted_nicknames])
158
159 assert is_bitstring(restricted_name)
160
161 params =
162 @full_user_data
163 |> Map.put(:nickname, restricted_name)
164
165 changeset = User.register_changeset(%User{}, params)
166
167 refute changeset.valid?
168 end
169
170 test "it sets the password_hash, ap_id and following fields" do
171 changeset = User.register_changeset(%User{}, @full_user_data)
172
173 assert changeset.valid?
174
175 assert is_binary(changeset.changes[:password_hash])
176 assert changeset.changes[:ap_id] == User.ap_id(%User{nickname: @full_user_data.nickname})
177
178 assert changeset.changes[:following] == [
179 User.ap_followers(%User{nickname: @full_user_data.nickname})
180 ]
181
182 assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers"
183 end
184
185 test "it ensures info is not nil" do
186 changeset = User.register_changeset(%User{}, @full_user_data)
187
188 assert changeset.valid?
189
190 {:ok, user} =
191 changeset
192 |> Repo.insert()
193
194 refute is_nil(user.info)
195 end
196 end
197
198 describe "user registration, with :account_activation_required" do
199 @full_user_data %{
200 bio: "A guy",
201 name: "my name",
202 nickname: "nick",
203 password: "test",
204 password_confirmation: "test",
205 email: "email@example.com"
206 }
207
208 setup do
209 setting = Pleroma.Config.get([:instance, :account_activation_required])
210
211 unless setting do
212 Pleroma.Config.put([:instance, :account_activation_required], true)
213 on_exit(fn -> Pleroma.Config.put([:instance, :account_activation_required], setting) end)
214 end
215
216 :ok
217 end
218
219 test "it creates unconfirmed user" do
220 changeset = User.register_changeset(%User{}, @full_user_data)
221 assert changeset.valid?
222
223 {:ok, user} = Repo.insert(changeset)
224
225 assert user.info.confirmation_pending
226 assert user.info.confirmation_token
227 end
228
229 test "it creates confirmed user if :confirmed option is given" do
230 changeset = User.register_changeset(%User{}, @full_user_data, confirmed: true)
231 assert changeset.valid?
232
233 {:ok, user} = Repo.insert(changeset)
234
235 refute user.info.confirmation_pending
236 refute user.info.confirmation_token
237 end
238 end
239
240 describe "get_or_fetch/1" do
241 test "gets an existing user by nickname" do
242 user = insert(:user)
243 fetched_user = User.get_or_fetch(user.nickname)
244
245 assert user == fetched_user
246 end
247
248 test "gets an existing user by ap_id" do
249 ap_id = "http://mastodon.example.org/users/admin"
250
251 user =
252 insert(
253 :user,
254 local: false,
255 nickname: "admin@mastodon.example.org",
256 ap_id: ap_id,
257 info: %{}
258 )
259
260 fetched_user = User.get_or_fetch(ap_id)
261 freshed_user = refresh_record(user)
262 assert freshed_user == fetched_user
263 end
264 end
265
266 describe "fetching a user from nickname or trying to build one" do
267 test "gets an existing user" do
268 user = insert(:user)
269 fetched_user = User.get_or_fetch_by_nickname(user.nickname)
270
271 assert user == fetched_user
272 end
273
274 test "gets an existing user, case insensitive" do
275 user = insert(:user, nickname: "nick")
276 fetched_user = User.get_or_fetch_by_nickname("NICK")
277
278 assert user == fetched_user
279 end
280
281
282 test "gets an existing user by fully qualified nickname" do
283 user = insert(:user)
284
285 fetched_user =
286 User.get_or_fetch_by_nickname(user.nickname <> "@" <> Pleroma.Web.Endpoint.host())
287
288 assert user == fetched_user
289 end
290
291 test "gets an existing user by fully qualified nickname, case insensitive" do
292 user = insert(:user, nickname: "nick")
293 casing_altered_fqn = String.upcase(user.nickname <> "@" <> Pleroma.Web.Endpoint.host())
294
295 fetched_user = User.get_or_fetch_by_nickname(casing_altered_fqn)
296
297 assert user == fetched_user
298 end
299
300 test "fetches an external user via ostatus if no user exists" do
301 fetched_user = User.get_or_fetch_by_nickname("shp@social.heldscal.la")
302 assert fetched_user.nickname == "shp@social.heldscal.la"
303 end
304
305 test "returns nil if no user could be fetched" do
306 fetched_user = User.get_or_fetch_by_nickname("nonexistant@social.heldscal.la")
307 assert fetched_user == nil
308 end
309
310 test "returns nil for nonexistant local user" do
311 fetched_user = User.get_or_fetch_by_nickname("nonexistant")
312 assert fetched_user == nil
313 end
314
315 test "updates an existing user, if stale" do
316 a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800)
317
318 orig_user =
319 insert(
320 :user,
321 local: false,
322 nickname: "admin@mastodon.example.org",
323 ap_id: "http://mastodon.example.org/users/admin",
324 last_refreshed_at: a_week_ago,
325 info: %{}
326 )
327
328 assert orig_user.last_refreshed_at == a_week_ago
329
330 user = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/admin")
331 assert user.info.source_data["endpoints"]
332
333 refute user.last_refreshed_at == orig_user.last_refreshed_at
334 end
335 end
336
337 test "returns an ap_id for a user" do
338 user = insert(:user)
339
340 assert User.ap_id(user) ==
341 Pleroma.Web.Router.Helpers.o_status_url(
342 Pleroma.Web.Endpoint,
343 :feed_redirect,
344 user.nickname
345 )
346 end
347
348 test "returns an ap_followers link for a user" do
349 user = insert(:user)
350
351 assert User.ap_followers(user) ==
352 Pleroma.Web.Router.Helpers.o_status_url(
353 Pleroma.Web.Endpoint,
354 :feed_redirect,
355 user.nickname
356 ) <> "/followers"
357 end
358
359 describe "remote user creation changeset" do
360 @valid_remote %{
361 bio: "hello",
362 name: "Someone",
363 nickname: "a@b.de",
364 ap_id: "http...",
365 info: %{some: "info"},
366 avatar: %{some: "avatar"}
367 }
368
369 test "it confirms validity" do
370 cs = User.remote_user_creation(@valid_remote)
371 assert cs.valid?
372 end
373
374 test "it sets the follower_adress" do
375 cs = User.remote_user_creation(@valid_remote)
376 # remote users get a fake local follower address
377 assert cs.changes.follower_address ==
378 User.ap_followers(%User{nickname: @valid_remote[:nickname]})
379 end
380
381 test "it enforces the fqn format for nicknames" do
382 cs = User.remote_user_creation(%{@valid_remote | nickname: "bla"})
383 assert cs.changes.local == false
384 assert cs.changes.avatar
385 refute cs.valid?
386 end
387
388 test "it has required fields" do
389 [:name, :ap_id]
390 |> Enum.each(fn field ->
391 cs = User.remote_user_creation(Map.delete(@valid_remote, field))
392 refute cs.valid?
393 end)
394 end
395
396 test "it restricts some sizes" do
397 [bio: 5000, name: 100]
398 |> Enum.each(fn {field, size} ->
399 string = String.pad_leading(".", size)
400 cs = User.remote_user_creation(Map.put(@valid_remote, field, string))
401 assert cs.valid?
402
403 string = String.pad_leading(".", size + 1)
404 cs = User.remote_user_creation(Map.put(@valid_remote, field, string))
405 refute cs.valid?
406 end)
407 end
408 end
409
410 describe "followers and friends" do
411 test "gets all followers for a given user" do
412 user = insert(:user)
413 follower_one = insert(:user)
414 follower_two = insert(:user)
415 not_follower = insert(:user)
416
417 {:ok, follower_one} = User.follow(follower_one, user)
418 {:ok, follower_two} = User.follow(follower_two, user)
419
420 {:ok, res} = User.get_followers(user)
421
422 assert Enum.member?(res, follower_one)
423 assert Enum.member?(res, follower_two)
424 refute Enum.member?(res, not_follower)
425 end
426
427 test "gets all friends (followed users) for a given user" do
428 user = insert(:user)
429 followed_one = insert(:user)
430 followed_two = insert(:user)
431 not_followed = insert(:user)
432
433 {:ok, user} = User.follow(user, followed_one)
434 {:ok, user} = User.follow(user, followed_two)
435
436 {:ok, res} = User.get_friends(user)
437
438 followed_one = User.get_by_ap_id(followed_one.ap_id)
439 followed_two = User.get_by_ap_id(followed_two.ap_id)
440 assert Enum.member?(res, followed_one)
441 assert Enum.member?(res, followed_two)
442 refute Enum.member?(res, not_followed)
443 end
444 end
445
446 describe "updating note and follower count" do
447 test "it sets the info->note_count property" do
448 note = insert(:note)
449
450 user = User.get_by_ap_id(note.data["actor"])
451
452 assert user.info.note_count == 0
453
454 {:ok, user} = User.update_note_count(user)
455
456 assert user.info.note_count == 1
457 end
458
459 test "it increases the info->note_count property" do
460 note = insert(:note)
461 user = User.get_by_ap_id(note.data["actor"])
462
463 assert user.info.note_count == 0
464
465 {:ok, user} = User.increase_note_count(user)
466
467 assert user.info.note_count == 1
468
469 {:ok, user} = User.increase_note_count(user)
470
471 assert user.info.note_count == 2
472 end
473
474 test "it decreases the info->note_count property" do
475 note = insert(:note)
476 user = User.get_by_ap_id(note.data["actor"])
477
478 assert user.info.note_count == 0
479
480 {:ok, user} = User.increase_note_count(user)
481
482 assert user.info.note_count == 1
483
484 {:ok, user} = User.decrease_note_count(user)
485
486 assert user.info.note_count == 0
487
488 {:ok, user} = User.decrease_note_count(user)
489
490 assert user.info.note_count == 0
491 end
492
493 test "it sets the info->follower_count property" do
494 user = insert(:user)
495 follower = insert(:user)
496
497 User.follow(follower, user)
498
499 assert user.info.follower_count == 0
500
501 {:ok, user} = User.update_follower_count(user)
502
503 assert user.info.follower_count == 1
504 end
505 end
506
507 describe "follow_import" do
508 test "it imports user followings from list" do
509 [user1, user2, user3] = insert_list(3, :user)
510
511 identifiers = [
512 user2.ap_id,
513 user3.nickname
514 ]
515
516 result = User.follow_import(user1, identifiers)
517 assert is_list(result)
518 assert result == [user2, user3]
519 end
520 end
521
522 describe "blocks" do
523 test "it blocks people" do
524 user = insert(:user)
525 blocked_user = insert(:user)
526
527 refute User.blocks?(user, blocked_user)
528
529 {:ok, user} = User.block(user, blocked_user)
530
531 assert User.blocks?(user, blocked_user)
532 end
533
534 test "it unblocks users" do
535 user = insert(:user)
536 blocked_user = insert(:user)
537
538 {:ok, user} = User.block(user, blocked_user)
539 {:ok, user} = User.unblock(user, blocked_user)
540
541 refute User.blocks?(user, blocked_user)
542 end
543
544 test "blocks tear down cyclical follow relationships" do
545 blocker = insert(:user)
546 blocked = insert(:user)
547
548 {:ok, blocker} = User.follow(blocker, blocked)
549 {:ok, blocked} = User.follow(blocked, blocker)
550
551 assert User.following?(blocker, blocked)
552 assert User.following?(blocked, blocker)
553
554 {:ok, blocker} = User.block(blocker, blocked)
555 blocked = Repo.get(User, blocked.id)
556
557 assert User.blocks?(blocker, blocked)
558
559 refute User.following?(blocker, blocked)
560 refute User.following?(blocked, blocker)
561 end
562
563 test "blocks tear down blocker->blocked follow relationships" do
564 blocker = insert(:user)
565 blocked = insert(:user)
566
567 {:ok, blocker} = User.follow(blocker, blocked)
568
569 assert User.following?(blocker, blocked)
570 refute User.following?(blocked, blocker)
571
572 {:ok, blocker} = User.block(blocker, blocked)
573 blocked = Repo.get(User, blocked.id)
574
575 assert User.blocks?(blocker, blocked)
576
577 refute User.following?(blocker, blocked)
578 refute User.following?(blocked, blocker)
579 end
580
581 test "blocks tear down blocked->blocker follow relationships" do
582 blocker = insert(:user)
583 blocked = insert(:user)
584
585 {:ok, blocked} = User.follow(blocked, blocker)
586
587 refute User.following?(blocker, blocked)
588 assert User.following?(blocked, blocker)
589
590 {:ok, blocker} = User.block(blocker, blocked)
591 blocked = Repo.get(User, blocked.id)
592
593 assert User.blocks?(blocker, blocked)
594
595 refute User.following?(blocker, blocked)
596 refute User.following?(blocked, blocker)
597 end
598 end
599
600 describe "domain blocking" do
601 test "blocks domains" do
602 user = insert(:user)
603 collateral_user = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
604
605 {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
606
607 assert User.blocks?(user, collateral_user)
608 end
609
610 test "unblocks domains" do
611 user = insert(:user)
612 collateral_user = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
613
614 {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
615 {:ok, user} = User.unblock_domain(user, "awful-and-rude-instance.com")
616
617 refute User.blocks?(user, collateral_user)
618 end
619 end
620
621 describe "blocks_import" do
622 test "it imports user blocks from list" do
623 [user1, user2, user3] = insert_list(3, :user)
624
625 identifiers = [
626 user2.ap_id,
627 user3.nickname
628 ]
629
630 result = User.blocks_import(user1, identifiers)
631 assert is_list(result)
632 assert result == [user2, user3]
633 end
634 end
635
636 test "get recipients from activity" do
637 actor = insert(:user)
638 user = insert(:user, local: true)
639 user_two = insert(:user, local: false)
640 addressed = insert(:user, local: true)
641 addressed_remote = insert(:user, local: false)
642
643 {:ok, activity} =
644 CommonAPI.post(actor, %{
645 "status" => "hey @#{addressed.nickname} @#{addressed_remote.nickname}"
646 })
647
648 assert [addressed] == User.get_recipients_from_activity(activity)
649
650 {:ok, user} = User.follow(user, actor)
651 {:ok, _user_two} = User.follow(user_two, actor)
652 recipients = User.get_recipients_from_activity(activity)
653 assert length(recipients) == 2
654 assert user in recipients
655 assert addressed in recipients
656 end
657
658 test ".deactivate can de-activate then re-activate a user" do
659 user = insert(:user)
660 assert false == user.info.deactivated
661 {:ok, user} = User.deactivate(user)
662 assert true == user.info.deactivated
663 {:ok, user} = User.deactivate(user, false)
664 assert false == user.info.deactivated
665 end
666
667 test ".delete deactivates a user, all follow relationships and all create activities" do
668 user = insert(:user)
669 followed = insert(:user)
670 follower = insert(:user)
671
672 {:ok, user} = User.follow(user, followed)
673 {:ok, follower} = User.follow(follower, user)
674
675 {:ok, activity} = CommonAPI.post(user, %{"status" => "2hu"})
676 {:ok, activity_two} = CommonAPI.post(follower, %{"status" => "3hu"})
677
678 {:ok, _, _} = CommonAPI.favorite(activity_two.id, user)
679 {:ok, _, _} = CommonAPI.favorite(activity.id, follower)
680 {:ok, _, _} = CommonAPI.repeat(activity.id, follower)
681
682 {:ok, _} = User.delete(user)
683
684 followed = Repo.get(User, followed.id)
685 follower = Repo.get(User, follower.id)
686 user = Repo.get(User, user.id)
687
688 assert user.info.deactivated
689
690 refute User.following?(user, followed)
691 refute User.following?(followed, follower)
692
693 # TODO: Remove favorites, repeats, delete activities.
694
695 refute Repo.get(Activity, activity.id)
696 end
697
698 test "get_public_key_for_ap_id fetches a user that's not in the db" do
699 assert {:ok, _key} = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin")
700 end
701
702 test "insert or update a user from given data" do
703 user = insert(:user, %{nickname: "nick@name.de"})
704 data = %{ap_id: user.ap_id <> "xxx", name: user.name, nickname: user.nickname}
705
706 assert {:ok, %User{}} = User.insert_or_update_user(data)
707 end
708
709 describe "per-user rich-text filtering" do
710 test "html_filter_policy returns nil when rich-text is enabled" do
711 user = insert(:user)
712
713 assert nil == User.html_filter_policy(user)
714 end
715
716 test "html_filter_policy returns TwitterText scrubber when rich-text is disabled" do
717 user = insert(:user, %{info: %{no_rich_text: true}})
718
719 assert Pleroma.HTML.Scrubber.TwitterText == User.html_filter_policy(user)
720 end
721 end
722
723 describe "caching" do
724 test "invalidate_cache works" do
725 user = insert(:user)
726 _user_info = User.get_cached_user_info(user)
727
728 User.invalidate_cache(user)
729
730 {:ok, nil} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
731 {:ok, nil} = Cachex.get(:user_cache, "nickname:#{user.nickname}")
732 {:ok, nil} = Cachex.get(:user_cache, "user_info:#{user.id}")
733 end
734
735 test "User.delete() plugs any possible zombie objects" do
736 user = insert(:user)
737
738 {:ok, _} = User.delete(user)
739
740 {:ok, cached_user} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
741
742 assert cached_user != user
743
744 {:ok, cached_user} = Cachex.get(:user_cache, "nickname:#{user.ap_id}")
745
746 assert cached_user != user
747 end
748 end
749
750 describe "User.search" do
751 test "finds a user, ranking by similarity" do
752 _user = insert(:user, %{name: "lain"})
753 _user_two = insert(:user, %{name: "ean"})
754 _user_three = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social"})
755 user_four = insert(:user, %{nickname: "lain@pleroma.soykaf.com"})
756
757 assert user_four ==
758 User.search("lain@ple") |> List.first() |> Map.put(:search_distance, nil)
759 end
760 end
761 end