Don't allow the following characters in names: * https://codepoints.net/U+115F (HANGUL CHOSEONG FILLER) * https://codepoints.net/U+1160 (HANGUL JUNGSEONG FILLER) * https://codepoints.net/U+3164 (HANGUL FILLER) * https://codepoints.net/U+FFA0 (HALFWIDTH HANGUL FILLER) These are space-like characters that were inadvertently allowed because they're not considered whitespace by Unicode and because they're in the Hangul script (which we otherwise allow).
338 lines
11 KiB
Ruby
338 lines
11 KiB
Ruby
require 'test_helper'
|
|
|
|
class UserTest < ActiveSupport::TestCase
|
|
def assert_promoted_to(new_level, user, promoter)
|
|
user.promote_to!(new_level, promoter)
|
|
assert_equal(new_level, user.reload.level)
|
|
end
|
|
|
|
def assert_not_promoted_to(new_level, user, promoter)
|
|
assert_raise(User::PrivilegeError) do
|
|
user.promote_to!(new_level, promoter)
|
|
end
|
|
|
|
assert_not_equal(new_level, user.reload.level)
|
|
end
|
|
|
|
context "A user" do
|
|
setup do
|
|
@user = FactoryBot.create(:user)
|
|
CurrentUser.user = @user
|
|
end
|
|
|
|
teardown do
|
|
CurrentUser.user = nil
|
|
end
|
|
|
|
context "promoting a user" do
|
|
setup do
|
|
@builder = create(:builder_user)
|
|
@mod = create(:moderator_user)
|
|
@admin = create(:admin_user)
|
|
@owner = create(:owner_user)
|
|
end
|
|
|
|
should "allow moderators to promote users up to builder level" do
|
|
assert_promoted_to(User::Levels::GOLD, @user, @mod)
|
|
assert_promoted_to(User::Levels::PLATINUM, @user, @mod)
|
|
assert_promoted_to(User::Levels::BUILDER, @user, @mod)
|
|
|
|
assert_not_promoted_to(User::Levels::MODERATOR, @user, @mod)
|
|
assert_not_promoted_to(User::Levels::ADMIN, @user, @mod)
|
|
assert_not_promoted_to(User::Levels::OWNER, @user, @mod)
|
|
end
|
|
|
|
should "allow admins to promote users up to moderator level" do
|
|
assert_promoted_to(User::Levels::GOLD, @user, @admin)
|
|
assert_promoted_to(User::Levels::PLATINUM, @user, @admin)
|
|
assert_promoted_to(User::Levels::BUILDER, @user, @admin)
|
|
assert_promoted_to(User::Levels::MODERATOR, @user, @admin)
|
|
|
|
assert_not_promoted_to(User::Levels::ADMIN, @user, @admin)
|
|
assert_not_promoted_to(User::Levels::OWNER, @user, @admin)
|
|
end
|
|
|
|
should "allow the owner to promote users up to admin level" do
|
|
assert_promoted_to(User::Levels::GOLD, @user, @owner)
|
|
assert_promoted_to(User::Levels::PLATINUM, @user, @owner)
|
|
assert_promoted_to(User::Levels::BUILDER, @user, @owner)
|
|
assert_promoted_to(User::Levels::MODERATOR, @user, @owner)
|
|
assert_promoted_to(User::Levels::ADMIN, @user, @owner)
|
|
|
|
assert_not_promoted_to(User::Levels::OWNER, @user, @owner)
|
|
end
|
|
|
|
should "not allow non-moderators to promote other users" do
|
|
assert_not_promoted_to(User::Levels::GOLD, @user, @builder)
|
|
assert_not_promoted_to(User::Levels::PLATINUM, @user, @builder)
|
|
assert_not_promoted_to(User::Levels::BUILDER, @user, @builder)
|
|
assert_not_promoted_to(User::Levels::MODERATOR, @user, @builder)
|
|
assert_not_promoted_to(User::Levels::ADMIN, @user, @builder)
|
|
assert_not_promoted_to(User::Levels::OWNER, @user, @builder)
|
|
end
|
|
|
|
should "not allow users to promote or demote other users at their rank or above" do
|
|
assert_not_promoted_to(User::Levels::ADMIN, create(:moderator_user), @mod)
|
|
assert_not_promoted_to(User::Levels::BUILDER, create(:moderator_user), @mod)
|
|
|
|
assert_not_promoted_to(User::Levels::OWNER, create(:admin_user), @admin)
|
|
assert_not_promoted_to(User::Levels::MODERATOR, create(:admin_user), @admin)
|
|
|
|
assert_not_promoted_to(User::Levels::ADMIN, create(:owner_user), @owner)
|
|
end
|
|
|
|
should "not allow users to promote themselves" do
|
|
assert_not_promoted_to(User::Levels::ADMIN, @mod, @mod)
|
|
assert_not_promoted_to(User::Levels::OWNER, @admin, @admin)
|
|
end
|
|
|
|
should "not allow users to demote themselves" do
|
|
assert_not_promoted_to(User::Levels::MEMBER, @mod, @mod)
|
|
assert_not_promoted_to(User::Levels::MEMBER, @admin, @admin)
|
|
assert_not_promoted_to(User::Levels::MEMBER, @owner, @owner)
|
|
end
|
|
|
|
should "create a neutral feedback" do
|
|
@user.promote_to!(User::Levels::GOLD, @mod)
|
|
assert_equal("You have been promoted to a Gold level account from Member.", @user.feedback.last.body)
|
|
end
|
|
|
|
should "send an automated dmail to the user" do
|
|
bot = FactoryBot.create(:user)
|
|
User.stubs(:system).returns(bot)
|
|
|
|
assert_difference("Dmail.count", 1) do
|
|
@user.promote_to!(User::Levels::GOLD, @admin)
|
|
end
|
|
|
|
assert(@user.dmails.exists?(from: bot, to: @user, title: "Your account has been updated"))
|
|
refute(@user.dmails.exists?(from: bot, to: @user, title: "Your user record has been updated"))
|
|
end
|
|
end
|
|
|
|
should "authenticate password" do
|
|
assert_equal(@user, @user.authenticate_password("password"))
|
|
assert_equal(false, @user.authenticate_password("password2"))
|
|
end
|
|
|
|
should "normalize its level" do
|
|
user = FactoryBot.create(:user, :level => User::Levels::OWNER)
|
|
assert(user.is_owner?)
|
|
assert(user.is_admin?)
|
|
assert(user.is_moderator?)
|
|
assert(user.is_gold?)
|
|
|
|
user = FactoryBot.create(:user, :level => User::Levels::ADMIN)
|
|
assert(!user.is_owner?)
|
|
assert(user.is_moderator?)
|
|
assert(user.is_gold?)
|
|
|
|
user = FactoryBot.create(:user, :level => User::Levels::MODERATOR)
|
|
assert(!user.is_admin?)
|
|
assert(user.is_moderator?)
|
|
assert(user.is_gold?)
|
|
|
|
user = FactoryBot.create(:user, :level => User::Levels::GOLD)
|
|
assert(!user.is_admin?)
|
|
assert(!user.is_moderator?)
|
|
assert(user.is_gold?)
|
|
|
|
user = FactoryBot.create(:user)
|
|
assert(!user.is_admin?)
|
|
assert(!user.is_moderator?)
|
|
assert(!user.is_gold?)
|
|
end
|
|
|
|
context "name" do
|
|
should "not contain whitespace" do
|
|
# U+2007: https://en.wikipedia.org/wiki/Figure_space
|
|
user = build(:user, name: "foo\u2007bar")
|
|
user.save
|
|
assert_equal(["Name can't contain whitespace"], user.errors.full_messages)
|
|
end
|
|
|
|
should "not conflict with an existing name" do
|
|
create(:user, name: "bkub")
|
|
user = build(:user, name: "BKUB")
|
|
user.save
|
|
assert_equal(["Name already taken"], user.errors.full_messages)
|
|
end
|
|
|
|
should "be less than 25 characters long" do
|
|
user = build(:user, name: "a"*25)
|
|
user.save
|
|
assert_equal(["Name must be less than 25 characters long"], user.errors.full_messages)
|
|
end
|
|
|
|
should "not contain a colon" do
|
|
user = build(:user, name: "a:b")
|
|
user.save
|
|
assert_equal(["Name can't contain ':'"], user.errors.full_messages)
|
|
end
|
|
|
|
should "not begin with an underscore" do
|
|
user = build(:user, name: "_x")
|
|
user.save
|
|
assert_equal(["Name can't start with '_'"], user.errors.full_messages)
|
|
end
|
|
|
|
should "not end with an underscore" do
|
|
user = build(:user, name: "x_")
|
|
user.save
|
|
assert_equal(["Name can't end with '_'"], user.errors.full_messages)
|
|
end
|
|
|
|
should "not contain consecutive underscores" do
|
|
user = build(:user, name: "x__y")
|
|
user.save
|
|
assert_equal(["Name can't contain multiple underscores in a row"], user.errors.full_messages)
|
|
end
|
|
|
|
should "not allow non-ASCII characters" do
|
|
user = build(:user, name: "Pokémon")
|
|
user.save
|
|
assert_equal(["Name must contain only basic letters or numbers"], user.errors.full_messages)
|
|
end
|
|
|
|
should "not allow names ending in file extensions" do
|
|
user = build(:user, name: "evazion.json")
|
|
user.save
|
|
assert_equal(["Name can't end with a file extension"], user.errors.full_messages)
|
|
end
|
|
|
|
should "not be in the same format as a deleted user" do
|
|
user = build(:user, name: "user_1234")
|
|
user.save
|
|
assert_equal(["Name can't be the same as a deleted user"], user.errors.full_messages)
|
|
end
|
|
|
|
should "not allow blacklisted names" do
|
|
Danbooru.config.stubs(:user_name_blacklist).returns(["voldemort"])
|
|
user = build(:user, name: "voldemort42")
|
|
user.save
|
|
assert_equal(["Name is not allowed"], user.errors.full_messages)
|
|
end
|
|
|
|
should_not allow_value("any").for(:name)
|
|
should_not allow_value("none").for(:name)
|
|
should_not allow_value("new").for(:name)
|
|
should_not allow_value("admin").for(:name)
|
|
should_not allow_value("mod").for(:name)
|
|
should_not allow_value("moderator").for(:name)
|
|
|
|
should_not allow_value("foo_\u115F").for(:name)
|
|
should_not allow_value("foo_\u1160").for(:name)
|
|
should_not allow_value("foo_\u3164").for(:name)
|
|
should_not allow_value("foo_\uFFA0").for(:name)
|
|
end
|
|
|
|
context "searching for users by name" do
|
|
setup do
|
|
@miku = create(:user, name: "hatsune_miku")
|
|
end
|
|
|
|
should "be case-insensitive" do
|
|
assert_equal("hatsune_miku", User.normalize_name("Hatsune_Miku"))
|
|
assert_equal(@miku.id, User.find_by_name("Hatsune_Miku").id)
|
|
assert_equal(@miku.id, User.name_to_id("Hatsune_Miku"))
|
|
end
|
|
|
|
should "handle whitespace" do
|
|
assert_equal("hatsune_miku", User.normalize_name(" hatsune miku "))
|
|
assert_equal(@miku.id, User.find_by_name(" hatsune miku ").id)
|
|
assert_equal(@miku.id, User.name_to_id(" hatsune miku "))
|
|
end
|
|
|
|
should "return nil for nonexistent names" do
|
|
assert_nil(User.find_by_name("does_not_exist"))
|
|
assert_nil(User.name_to_id("does_not_exist"))
|
|
end
|
|
|
|
should "work for names containing asterisks or backslashes" do
|
|
@user1 = build(:user, name: "user*1")
|
|
@user2 = build(:user, name: "user*2")
|
|
@user3 = build(:user, name: "user\*3")
|
|
|
|
@user1.save(validate: false)
|
|
@user2.save(validate: false)
|
|
@user3.save(validate: false)
|
|
|
|
assert_equal(@user1.id, User.find_by_name("user*1").id)
|
|
assert_equal(@user2.id, User.find_by_name("user*2").id)
|
|
assert_equal(@user3.id, User.find_by_name("user\*3").id)
|
|
end
|
|
end
|
|
|
|
context "ip address" do
|
|
setup do
|
|
@user = FactoryBot.create(:user)
|
|
end
|
|
|
|
context "in the json representation" do
|
|
should "not appear" do
|
|
assert(@user.to_json !~ /addr/)
|
|
end
|
|
end
|
|
|
|
context "in the xml representation" do
|
|
should "not appear" do
|
|
assert(@user.to_xml !~ /addr/)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "password" do
|
|
context "in the json representation" do
|
|
setup do
|
|
@user = FactoryBot.create(:user)
|
|
end
|
|
|
|
should "not appear" do
|
|
assert(@user.to_json !~ /password/)
|
|
end
|
|
end
|
|
|
|
context "in the xml representation" do
|
|
setup do
|
|
@user = FactoryBot.create(:user)
|
|
end
|
|
|
|
should "not appear" do
|
|
assert(@user.to_xml !~ /password/)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when searched by name" do
|
|
should "match wildcards" do
|
|
user1 = build(:user, name: "foo")
|
|
user2 = build(:user, name: "foo*bar")
|
|
user3 = build(:user, name: "bar\*baz")
|
|
|
|
user1.save(validate: false)
|
|
user2.save(validate: false)
|
|
user3.save(validate: false)
|
|
|
|
assert_search_equals([user2, user1], name: "foo*")
|
|
assert_search_equals(user2, name: "foo\*bar")
|
|
assert_search_equals(user3, name: "bar\\\*baz")
|
|
end
|
|
end
|
|
|
|
context "custom CSS" do
|
|
should "raise a validation error on invalid custom CSS" do
|
|
user = build(:user, custom_style: "}}}")
|
|
|
|
assert_equal(true, user.invalid?)
|
|
assert_match(/Custom CSS contains a syntax error/, user.errors[:base].first)
|
|
end
|
|
|
|
should "allow blank CSS" do
|
|
user = build(:user, custom_style: " ")
|
|
|
|
assert_equal(true, user.valid?)
|
|
end
|
|
end
|
|
end
|
|
end
|