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