Merge branch 'captcha' into 'develop'
[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 test "gets an existing user by fully qualified nickname" do
282 user = insert(:user)
283
284 fetched_user =
285 User.get_or_fetch_by_nickname(user.nickname <> "@" <> Pleroma.Web.Endpoint.host())
286
287 assert user == fetched_user
288 end
289
290 test "gets an existing user by fully qualified nickname, case insensitive" do
291 user = insert(:user, nickname: "nick")
292 casing_altered_fqn = String.upcase(user.nickname <> "@" <> Pleroma.Web.Endpoint.host())
293
294 fetched_user = User.get_or_fetch_by_nickname(casing_altered_fqn)
295
296 assert user == fetched_user
297 end
298
299 test "fetches an external user via ostatus if no user exists" do
300 fetched_user = User.get_or_fetch_by_nickname("shp@social.heldscal.la")
301 assert fetched_user.nickname == "shp@social.heldscal.la"
302 end
303
304 test "returns nil if no user could be fetched" do
305 fetched_user = User.get_or_fetch_by_nickname("nonexistant@social.heldscal.la")
306 assert fetched_user == nil
307 end
308
309 test "returns nil for nonexistant local user" do
310 fetched_user = User.get_or_fetch_by_nickname("nonexistant")
311 assert fetched_user == nil
312 end
313
314 test "updates an existing user, if stale" do
315 a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800)
316
317 orig_user =
318 insert(
319 :user,
320 local: false,
321 nickname: "admin@mastodon.example.org",
322 ap_id: "http://mastodon.example.org/users/admin",
323 last_refreshed_at: a_week_ago,
324 info: %{}
325 )
326
327 assert orig_user.last_refreshed_at == a_week_ago
328
329 user = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/admin")
330 assert user.info.source_data["endpoints"]
331
332 refute user.last_refreshed_at == orig_user.last_refreshed_at
333 end
334 end
335
336 test "returns an ap_id for a user" do
337 user = insert(:user)
338
339 assert User.ap_id(user) ==
340 Pleroma.Web.Router.Helpers.o_status_url(
341 Pleroma.Web.Endpoint,
342 :feed_redirect,
343 user.nickname
344 )
345 end
346
347 test "returns an ap_followers link for a user" do
348 user = insert(:user)
349
350 assert User.ap_followers(user) ==
351 Pleroma.Web.Router.Helpers.o_status_url(
352 Pleroma.Web.Endpoint,
353 :feed_redirect,
354 user.nickname
355 ) <> "/followers"
356 end
357
358 describe "remote user creation changeset" do
359 @valid_remote %{
360 bio: "hello",
361 name: "Someone",
362 nickname: "a@b.de",
363 ap_id: "http...",
364 info: %{some: "info"},
365 avatar: %{some: "avatar"}
366 }
367
368 test "it confirms validity" do
369 cs = User.remote_user_creation(@valid_remote)
370 assert cs.valid?
371 end
372
373 test "it sets the follower_adress" do
374 cs = User.remote_user_creation(@valid_remote)
375 # remote users get a fake local follower address
376 assert cs.changes.follower_address ==
377 User.ap_followers(%User{nickname: @valid_remote[:nickname]})
378 end
379
380 test "it enforces the fqn format for nicknames" do
381 cs = User.remote_user_creation(%{@valid_remote | nickname: "bla"})
382 assert cs.changes.local == false
383 assert cs.changes.avatar
384 refute cs.valid?
385 end
386
387 test "it has required fields" do
388 [:name, :ap_id]
389 |> Enum.each(fn field ->
390 cs = User.remote_user_creation(Map.delete(@valid_remote, field))
391 refute cs.valid?
392 end)
393 end
394
395 test "it restricts some sizes" do
396 [bio: 5000, name: 100]
397 |> Enum.each(fn {field, size} ->
398 string = String.pad_leading(".", size)
399 cs = User.remote_user_creation(Map.put(@valid_remote, field, string))
400 assert cs.valid?
401
402 string = String.pad_leading(".", size + 1)
403 cs = User.remote_user_creation(Map.put(@valid_remote, field, string))
404 refute cs.valid?
405 end)
406 end
407 end
408
409 describe "followers and friends" do
410 test "gets all followers for a given user" do
411 user = insert(:user)
412 follower_one = insert(:user)
413 follower_two = insert(:user)
414 not_follower = insert(:user)
415
416 {:ok, follower_one} = User.follow(follower_one, user)
417 {:ok, follower_two} = User.follow(follower_two, user)
418
419 {:ok, res} = User.get_followers(user)
420
421 assert Enum.member?(res, follower_one)
422 assert Enum.member?(res, follower_two)
423 refute Enum.member?(res, not_follower)
424 end
425
426 test "gets all friends (followed users) for a given user" do
427 user = insert(:user)
428 followed_one = insert(:user)
429 followed_two = insert(:user)
430 not_followed = insert(:user)
431
432 {:ok, user} = User.follow(user, followed_one)
433 {:ok, user} = User.follow(user, followed_two)
434
435 {:ok, res} = User.get_friends(user)
436
437 followed_one = User.get_by_ap_id(followed_one.ap_id)
438 followed_two = User.get_by_ap_id(followed_two.ap_id)
439 assert Enum.member?(res, followed_one)
440 assert Enum.member?(res, followed_two)
441 refute Enum.member?(res, not_followed)
442 end
443 end
444
445 describe "updating note and follower count" do
446 test "it sets the info->note_count property" do
447 note = insert(:note)
448
449 user = User.get_by_ap_id(note.data["actor"])
450
451 assert user.info.note_count == 0
452
453 {:ok, user} = User.update_note_count(user)
454
455 assert user.info.note_count == 1
456 end
457
458 test "it increases the info->note_count property" do
459 note = insert(:note)
460 user = User.get_by_ap_id(note.data["actor"])
461
462 assert user.info.note_count == 0
463
464 {:ok, user} = User.increase_note_count(user)
465
466 assert user.info.note_count == 1
467
468 {:ok, user} = User.increase_note_count(user)
469
470 assert user.info.note_count == 2
471 end
472
473 test "it decreases the info->note_count property" do
474 note = insert(:note)
475 user = User.get_by_ap_id(note.data["actor"])
476
477 assert user.info.note_count == 0
478
479 {:ok, user} = User.increase_note_count(user)
480
481 assert user.info.note_count == 1
482
483 {:ok, user} = User.decrease_note_count(user)
484
485 assert user.info.note_count == 0
486
487 {:ok, user} = User.decrease_note_count(user)
488
489 assert user.info.note_count == 0
490 end
491
492 test "it sets the info->follower_count property" do
493 user = insert(:user)
494 follower = insert(:user)
495
496 User.follow(follower, user)
497
498 assert user.info.follower_count == 0
499
500 {:ok, user} = User.update_follower_count(user)
501
502 assert user.info.follower_count == 1
503 end
504 end
505
506 describe "follow_import" do
507 test "it imports user followings from list" do
508 [user1, user2, user3] = insert_list(3, :user)
509
510 identifiers = [
511 user2.ap_id,
512 user3.nickname
513 ]
514
515 result = User.follow_import(user1, identifiers)
516 assert is_list(result)
517 assert result == [user2, user3]
518 end
519 end
520
521 describe "blocks" do
522 test "it blocks people" do
523 user = insert(:user)
524 blocked_user = insert(:user)
525
526 refute User.blocks?(user, blocked_user)
527
528 {:ok, user} = User.block(user, blocked_user)
529
530 assert User.blocks?(user, blocked_user)
531 end
532
533 test "it unblocks users" do
534 user = insert(:user)
535 blocked_user = insert(:user)
536
537 {:ok, user} = User.block(user, blocked_user)
538 {:ok, user} = User.unblock(user, blocked_user)
539
540 refute User.blocks?(user, blocked_user)
541 end
542
543 test "blocks tear down cyclical follow relationships" do
544 blocker = insert(:user)
545 blocked = insert(:user)
546
547 {:ok, blocker} = User.follow(blocker, blocked)
548 {:ok, blocked} = User.follow(blocked, blocker)
549
550 assert User.following?(blocker, blocked)
551 assert User.following?(blocked, blocker)
552
553 {:ok, blocker} = User.block(blocker, blocked)
554 blocked = Repo.get(User, blocked.id)
555
556 assert User.blocks?(blocker, blocked)
557
558 refute User.following?(blocker, blocked)
559 refute User.following?(blocked, blocker)
560 end
561
562 test "blocks tear down blocker->blocked follow relationships" do
563 blocker = insert(:user)
564 blocked = insert(:user)
565
566 {:ok, blocker} = User.follow(blocker, blocked)
567
568 assert User.following?(blocker, blocked)
569 refute User.following?(blocked, blocker)
570
571 {:ok, blocker} = User.block(blocker, blocked)
572 blocked = Repo.get(User, blocked.id)
573
574 assert User.blocks?(blocker, blocked)
575
576 refute User.following?(blocker, blocked)
577 refute User.following?(blocked, blocker)
578 end
579
580 test "blocks tear down blocked->blocker follow relationships" do
581 blocker = insert(:user)
582 blocked = insert(:user)
583
584 {:ok, blocked} = User.follow(blocked, blocker)
585
586 refute User.following?(blocker, blocked)
587 assert User.following?(blocked, blocker)
588
589 {:ok, blocker} = User.block(blocker, blocked)
590 blocked = Repo.get(User, blocked.id)
591
592 assert User.blocks?(blocker, blocked)
593
594 refute User.following?(blocker, blocked)
595 refute User.following?(blocked, blocker)
596 end
597 end
598
599 describe "domain blocking" do
600 test "blocks domains" do
601 user = insert(:user)
602 collateral_user = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
603
604 {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
605
606 assert User.blocks?(user, collateral_user)
607 end
608
609 test "unblocks domains" do
610 user = insert(:user)
611 collateral_user = insert(:user, %{ap_id: "https://awful-and-rude-instance.com/user/bully"})
612
613 {:ok, user} = User.block_domain(user, "awful-and-rude-instance.com")
614 {:ok, user} = User.unblock_domain(user, "awful-and-rude-instance.com")
615
616 refute User.blocks?(user, collateral_user)
617 end
618 end
619
620 describe "blocks_import" do
621 test "it imports user blocks from list" do
622 [user1, user2, user3] = insert_list(3, :user)
623
624 identifiers = [
625 user2.ap_id,
626 user3.nickname
627 ]
628
629 result = User.blocks_import(user1, identifiers)
630 assert is_list(result)
631 assert result == [user2, user3]
632 end
633 end
634
635 test "get recipients from activity" do
636 actor = insert(:user)
637 user = insert(:user, local: true)
638 user_two = insert(:user, local: false)
639 addressed = insert(:user, local: true)
640 addressed_remote = insert(:user, local: false)
641
642 {:ok, activity} =
643 CommonAPI.post(actor, %{
644 "status" => "hey @#{addressed.nickname} @#{addressed_remote.nickname}"
645 })
646
647 assert [addressed] == User.get_recipients_from_activity(activity)
648
649 {:ok, user} = User.follow(user, actor)
650 {:ok, _user_two} = User.follow(user_two, actor)
651 recipients = User.get_recipients_from_activity(activity)
652 assert length(recipients) == 2
653 assert user in recipients
654 assert addressed in recipients
655 end
656
657 test ".deactivate can de-activate then re-activate a user" do
658 user = insert(:user)
659 assert false == user.info.deactivated
660 {:ok, user} = User.deactivate(user)
661 assert true == user.info.deactivated
662 {:ok, user} = User.deactivate(user, false)
663 assert false == user.info.deactivated
664 end
665
666 test ".delete deactivates a user, all follow relationships and all create activities" do
667 user = insert(:user)
668 followed = insert(:user)
669 follower = insert(:user)
670
671 {:ok, user} = User.follow(user, followed)
672 {:ok, follower} = User.follow(follower, user)
673
674 {:ok, activity} = CommonAPI.post(user, %{"status" => "2hu"})
675 {:ok, activity_two} = CommonAPI.post(follower, %{"status" => "3hu"})
676
677 {:ok, _, _} = CommonAPI.favorite(activity_two.id, user)
678 {:ok, _, _} = CommonAPI.favorite(activity.id, follower)
679 {:ok, _, _} = CommonAPI.repeat(activity.id, follower)
680
681 {:ok, _} = User.delete(user)
682
683 followed = Repo.get(User, followed.id)
684 follower = Repo.get(User, follower.id)
685 user = Repo.get(User, user.id)
686
687 assert user.info.deactivated
688
689 refute User.following?(user, followed)
690 refute User.following?(followed, follower)
691
692 # TODO: Remove favorites, repeats, delete activities.
693
694 refute Repo.get(Activity, activity.id)
695 end
696
697 test "get_public_key_for_ap_id fetches a user that's not in the db" do
698 assert {:ok, _key} = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin")
699 end
700
701 test "insert or update a user from given data" do
702 user = insert(:user, %{nickname: "nick@name.de"})
703 data = %{ap_id: user.ap_id <> "xxx", name: user.name, nickname: user.nickname}
704
705 assert {:ok, %User{}} = User.insert_or_update_user(data)
706 end
707
708 describe "per-user rich-text filtering" do
709 test "html_filter_policy returns default policies, when rich-text is enabled" do
710 user = insert(:user)
711
712 assert Pleroma.Config.get([:markup, :scrub_policy]) == User.html_filter_policy(user)
713 end
714
715 test "html_filter_policy returns TwitterText scrubber when rich-text is disabled" do
716 user = insert(:user, %{info: %{no_rich_text: true}})
717
718 assert Pleroma.HTML.Scrubber.TwitterText == User.html_filter_policy(user)
719 end
720 end
721
722 describe "caching" do
723 test "invalidate_cache works" do
724 user = insert(:user)
725 _user_info = User.get_cached_user_info(user)
726
727 User.invalidate_cache(user)
728
729 {:ok, nil} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
730 {:ok, nil} = Cachex.get(:user_cache, "nickname:#{user.nickname}")
731 {:ok, nil} = Cachex.get(:user_cache, "user_info:#{user.id}")
732 end
733
734 test "User.delete() plugs any possible zombie objects" do
735 user = insert(:user)
736
737 {:ok, _} = User.delete(user)
738
739 {:ok, cached_user} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
740
741 assert cached_user != user
742
743 {:ok, cached_user} = Cachex.get(:user_cache, "nickname:#{user.ap_id}")
744
745 assert cached_user != user
746 end
747 end
748
749 describe "User.search" do
750 test "finds a user, ranking by similarity" do
751 _user = insert(:user, %{name: "lain"})
752 _user_two = insert(:user, %{name: "ean"})
753 _user_three = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social"})
754 user_four = insert(:user, %{nickname: "lain@pleroma.soykaf.com"})
755
756 assert user_four ==
757 User.search("lain@ple") |> List.first() |> Map.put(:search_distance, nil)
758 end
759 end
760 end