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