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