mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-28 01:56:01 -05:00
b3d769ff4f
update rspec syntax to v3 change syntax to rspec v3 oops. fix typo mailers classes with rspec3 syntax helpers with rspec3 syntax jobs with rspec3 syntax serializers with rspec3 syntax views with rspec3 syntax support to rspec3 syntax category spec with rspec3 syntax
1200 lines
35 KiB
Ruby
1200 lines
35 KiB
Ruby
require 'spec_helper'
|
|
require_dependency 'user'
|
|
|
|
describe User do
|
|
|
|
it { is_expected.to validate_presence_of :username }
|
|
it { is_expected.to validate_presence_of :email }
|
|
|
|
describe '#count_by_signup_date' do
|
|
before(:each) do
|
|
User.destroy_all
|
|
Timecop.freeze
|
|
Fabricate(:user)
|
|
Fabricate(:user, created_at: 1.day.ago)
|
|
Fabricate(:user, created_at: 1.day.ago)
|
|
Fabricate(:user, created_at: 2.days.ago)
|
|
Fabricate(:user, created_at: 4.days.ago)
|
|
end
|
|
after(:each) { Timecop.return }
|
|
let(:signups_by_day) { {1.day.ago.to_date => 2, 2.days.ago.to_date => 1, Time.now.utc.to_date => 1} }
|
|
|
|
it 'collect closed interval signups' do
|
|
expect(User.count_by_signup_date(2.days.ago, Time.now)).to include(signups_by_day)
|
|
expect(User.count_by_signup_date(2.days.ago, Time.now)).not_to include({4.days.ago.to_date => 1})
|
|
end
|
|
end
|
|
|
|
context '.enqueue_welcome_message' do
|
|
let(:user) { Fabricate(:user) }
|
|
|
|
it 'enqueues the system message' do
|
|
Jobs.expects(:enqueue).with(:send_system_message, user_id: user.id, message_type: 'welcome_user')
|
|
user.enqueue_welcome_message('welcome_user')
|
|
end
|
|
|
|
it "doesn't enqueue the system message when the site settings disable it" do
|
|
SiteSetting.expects(:send_welcome_message?).returns(false)
|
|
Jobs.expects(:enqueue).with(:send_system_message, user_id: user.id, message_type: 'welcome_user').never
|
|
user.enqueue_welcome_message('welcome_user')
|
|
end
|
|
|
|
end
|
|
|
|
describe '.approve' do
|
|
let(:user) { Fabricate(:user) }
|
|
let(:admin) { Fabricate(:admin) }
|
|
|
|
it "enqueues a 'signup after approval' email" do
|
|
Jobs.expects(:enqueue).with(
|
|
:user_email, has_entries(type: :signup_after_approval)
|
|
)
|
|
user.approve(admin)
|
|
end
|
|
|
|
context 'after approval' do
|
|
before do
|
|
user.approve(admin)
|
|
end
|
|
|
|
it 'marks the user as approved' do
|
|
expect(user).to be_approved
|
|
end
|
|
|
|
it 'has the admin as the approved by' do
|
|
expect(user.approved_by).to eq(admin)
|
|
end
|
|
|
|
it 'has a value for approved_at' do
|
|
expect(user.approved_at).to be_present
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
describe 'bookmark' do
|
|
before do
|
|
@post = Fabricate(:post)
|
|
end
|
|
|
|
it "creates a bookmark with the true parameter" do
|
|
expect {
|
|
PostAction.act(@post.user, @post, PostActionType.types[:bookmark])
|
|
}.to change(PostAction, :count).by(1)
|
|
end
|
|
|
|
describe 'when removing a bookmark' do
|
|
before do
|
|
PostAction.act(@post.user, @post, PostActionType.types[:bookmark])
|
|
end
|
|
|
|
it 'reduces the bookmark count of the post' do
|
|
active = PostAction.where(deleted_at: nil)
|
|
expect {
|
|
PostAction.remove_act(@post.user, @post, PostActionType.types[:bookmark])
|
|
}.to change(active, :count).by(-1)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'change_username' do
|
|
|
|
let(:user) { Fabricate(:user) }
|
|
|
|
context 'success' do
|
|
let(:new_username) { "#{user.username}1234" }
|
|
|
|
before do
|
|
@result = user.change_username(new_username)
|
|
end
|
|
|
|
it 'returns true' do
|
|
expect(@result).to eq(true)
|
|
end
|
|
|
|
it 'should change the username' do
|
|
user.reload
|
|
expect(user.username).to eq(new_username)
|
|
end
|
|
|
|
it 'should change the username_lower' do
|
|
user.reload
|
|
expect(user.username_lower).to eq(new_username.downcase)
|
|
end
|
|
end
|
|
|
|
context 'failure' do
|
|
let(:wrong_username) { "" }
|
|
let(:username_before_change) { user.username }
|
|
let(:username_lower_before_change) { user.username_lower }
|
|
|
|
before do
|
|
@result = user.change_username(wrong_username)
|
|
end
|
|
|
|
it 'returns false' do
|
|
expect(@result).to eq(false)
|
|
end
|
|
|
|
it 'should not change the username' do
|
|
user.reload
|
|
expect(user.username).to eq(username_before_change)
|
|
end
|
|
|
|
it 'should not change the username_lower' do
|
|
user.reload
|
|
expect(user.username_lower).to eq(username_lower_before_change)
|
|
end
|
|
end
|
|
|
|
describe 'change the case of my username' do
|
|
let!(:myself) { Fabricate(:user, username: 'hansolo') }
|
|
|
|
it 'should return true' do
|
|
expect(myself.change_username('HanSolo')).to eq(true)
|
|
end
|
|
|
|
it 'should change the username' do
|
|
myself.change_username('HanSolo')
|
|
expect(myself.reload.username).to eq('HanSolo')
|
|
end
|
|
end
|
|
|
|
describe 'allow custom minimum username length from site settings' do
|
|
before do
|
|
@custom_min = 2
|
|
SiteSetting.min_username_length = @custom_min
|
|
end
|
|
|
|
it 'should allow a shorter username than default' do
|
|
result = user.change_username('a' * @custom_min)
|
|
expect(result).not_to eq(false)
|
|
end
|
|
|
|
it 'should not allow a shorter username than limit' do
|
|
result = user.change_username('a' * (@custom_min - 1))
|
|
expect(result).to eq(false)
|
|
end
|
|
|
|
it 'should not allow a longer username than limit' do
|
|
result = user.change_username('a' * (User.username_length.end + 1))
|
|
expect(result).to eq(false)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'delete posts' do
|
|
before do
|
|
@post1 = Fabricate(:post)
|
|
@user = @post1.user
|
|
@post2 = Fabricate(:post, topic: @post1.topic, user: @user)
|
|
@post3 = Fabricate(:post, user: @user)
|
|
@posts = [@post1, @post2, @post3]
|
|
@guardian = Guardian.new(Fabricate(:admin))
|
|
end
|
|
|
|
it 'allows moderator to delete all posts' do
|
|
@user.delete_all_posts!(@guardian)
|
|
expect(Post.where(id: @posts.map(&:id))).to be_empty
|
|
@posts.each do |p|
|
|
if p.post_number == 1
|
|
expect(Topic.find_by(id: p.topic_id)).to be_nil
|
|
end
|
|
end
|
|
end
|
|
|
|
it 'does not allow non moderators to delete all posts' do
|
|
invalid_guardian = Guardian.new(Fabricate(:user))
|
|
|
|
expect do
|
|
@user.delete_all_posts!(invalid_guardian)
|
|
end.to raise_error Discourse::InvalidAccess
|
|
|
|
@posts.each do |p|
|
|
p.reload
|
|
expect(p).to be_present
|
|
expect(p.topic).to be_present
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'new' do
|
|
|
|
subject { Fabricate.build(:user) }
|
|
|
|
it { is_expected.to be_valid }
|
|
it { is_expected.not_to be_admin }
|
|
it { is_expected.not_to be_approved }
|
|
|
|
it "is properly initialized" do
|
|
expect(subject.approved_at).to be_blank
|
|
expect(subject.approved_by_id).to be_blank
|
|
expect(subject.email_private_messages).to eq(true)
|
|
expect(subject.email_direct).to eq(true)
|
|
end
|
|
|
|
context 'digest emails' do
|
|
it 'defaults to digests every week' do
|
|
expect(subject.email_digests).to eq(true)
|
|
expect(subject.digest_after_days).to eq(7)
|
|
end
|
|
|
|
it 'uses default_digest_email_frequency' do
|
|
SiteSetting.stubs(:default_digest_email_frequency).returns(1)
|
|
expect(subject.email_digests).to eq(true)
|
|
expect(subject.digest_after_days).to eq(1)
|
|
end
|
|
|
|
it 'disables digests by default if site setting says so' do
|
|
SiteSetting.stubs(:default_digest_email_frequency).returns('')
|
|
expect(subject.email_digests).to eq(false)
|
|
end
|
|
end
|
|
|
|
context 'after_save' do
|
|
before { subject.save }
|
|
|
|
it "has an email token" do
|
|
expect(subject.email_tokens).to be_present
|
|
end
|
|
end
|
|
|
|
it "downcases email addresses" do
|
|
user = Fabricate.build(:user, email: 'Fancy.Caps.4.U@gmail.com')
|
|
user.save
|
|
expect(user.reload.email).to eq('fancy.caps.4.u@gmail.com')
|
|
end
|
|
end
|
|
|
|
describe 'ip address validation' do
|
|
it 'validates ip_address for new users' do
|
|
u = Fabricate.build(:user)
|
|
AllowedIpAddressValidator.any_instance.expects(:validate_each).with(u, :ip_address, u.ip_address)
|
|
u.valid?
|
|
end
|
|
|
|
it 'does not validate ip_address when updating an existing user' do
|
|
u = Fabricate(:user)
|
|
u.ip_address = '87.123.23.11'
|
|
AllowedIpAddressValidator.any_instance.expects(:validate_each).never
|
|
u.valid?
|
|
end
|
|
end
|
|
|
|
describe "trust levels" do
|
|
|
|
# NOTE be sure to use build to avoid db calls
|
|
let(:user) { Fabricate.build(:user, trust_level: TrustLevel[0]) }
|
|
|
|
it "sets to the default trust level setting" do
|
|
SiteSetting.default_trust_level = TrustLevel[4]
|
|
expect(User.new.trust_level).to eq(TrustLevel[4])
|
|
end
|
|
|
|
describe 'has_trust_level?' do
|
|
|
|
it "raises an error with an invalid level" do
|
|
expect { user.has_trust_level?(:wat) }.to raise_error
|
|
end
|
|
|
|
it "is true for your basic level" do
|
|
expect(user.has_trust_level?(TrustLevel[0])).to eq(true)
|
|
end
|
|
|
|
it "is false for a higher level" do
|
|
expect(user.has_trust_level?(TrustLevel[2])).to eq(false)
|
|
end
|
|
|
|
it "is true if you exceed the level" do
|
|
user.trust_level = TrustLevel[4]
|
|
expect(user.has_trust_level?(TrustLevel[1])).to eq(true)
|
|
end
|
|
|
|
it "is true for an admin even with a low trust level" do
|
|
user.trust_level = TrustLevel[0]
|
|
user.admin = true
|
|
expect(user.has_trust_level?(TrustLevel[1])).to eq(true)
|
|
end
|
|
|
|
end
|
|
|
|
describe 'moderator' do
|
|
it "isn't a moderator by default" do
|
|
expect(user.moderator?).to eq(false)
|
|
end
|
|
|
|
it "is a moderator if the user level is moderator" do
|
|
user.moderator = true
|
|
expect(user.has_trust_level?(TrustLevel[4])).to eq(true)
|
|
end
|
|
|
|
it "is staff if the user is an admin" do
|
|
user.admin = true
|
|
expect(user.staff?).to eq(true)
|
|
end
|
|
|
|
end
|
|
|
|
|
|
end
|
|
|
|
describe 'staff and regular users' do
|
|
let(:user) { Fabricate.build(:user) }
|
|
|
|
describe '#staff?' do
|
|
subject { user.staff? }
|
|
|
|
it { is_expected.to eq(false) }
|
|
|
|
context 'for a moderator user' do
|
|
before { user.moderator = true }
|
|
|
|
it { is_expected.to eq(true) }
|
|
end
|
|
|
|
context 'for an admin user' do
|
|
before { user.admin = true }
|
|
|
|
it { is_expected.to eq(true) }
|
|
end
|
|
end
|
|
|
|
describe '#regular?' do
|
|
subject { user.regular? }
|
|
|
|
it { is_expected.to eq(true) }
|
|
|
|
context 'for a moderator user' do
|
|
before { user.moderator = true }
|
|
|
|
it { is_expected.to eq(false) }
|
|
end
|
|
|
|
context 'for an admin user' do
|
|
before { user.admin = true }
|
|
|
|
it { is_expected.to eq(false) }
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'temporary_key' do
|
|
|
|
let(:user) { Fabricate(:user) }
|
|
let!(:temporary_key) { user.temporary_key}
|
|
|
|
it 'has a temporary key' do
|
|
expect(temporary_key).to be_present
|
|
end
|
|
|
|
describe 'User#find_by_temporary_key' do
|
|
|
|
it 'can be used to find the user' do
|
|
expect(User.find_by_temporary_key(temporary_key)).to eq(user)
|
|
end
|
|
|
|
it 'returns nil with an invalid key' do
|
|
expect(User.find_by_temporary_key('asdfasdf')).to be_blank
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
describe 'email_hash' do
|
|
before do
|
|
@user = Fabricate(:user)
|
|
end
|
|
|
|
it 'should have a sane email hash' do
|
|
expect(@user.email_hash).to match(/^[0-9a-f]{32}$/)
|
|
end
|
|
|
|
it 'should use downcase email' do
|
|
@user.email = "example@example.com"
|
|
@user2 = Fabricate(:user)
|
|
@user2.email = "ExAmPlE@eXaMpLe.com"
|
|
|
|
expect(@user.email_hash).to eq(@user2.email_hash)
|
|
end
|
|
|
|
it 'should trim whitespace before hashing' do
|
|
@user.email = "example@example.com"
|
|
@user2 = Fabricate(:user)
|
|
@user2.email = " example@example.com "
|
|
|
|
expect(@user.email_hash).to eq(@user2.email_hash)
|
|
end
|
|
end
|
|
|
|
describe 'associated_accounts' do
|
|
it 'should correctly find social associations' do
|
|
user = Fabricate(:user)
|
|
expect(user.associated_accounts).to eq(I18n.t("user.no_accounts_associated"))
|
|
|
|
TwitterUserInfo.create(user_id: user.id, screen_name: "sam", twitter_user_id: 1)
|
|
FacebookUserInfo.create(user_id: user.id, username: "sam", facebook_user_id: 1)
|
|
GoogleUserInfo.create(user_id: user.id, email: "sam@sam.com", google_user_id: 1)
|
|
GithubUserInfo.create(user_id: user.id, screen_name: "sam", github_user_id: 1)
|
|
|
|
user.reload
|
|
expect(user.associated_accounts).to eq("Twitter(sam), Facebook(sam), Google(sam@sam.com), Github(sam)")
|
|
|
|
end
|
|
end
|
|
|
|
describe 'name heuristics' do
|
|
it 'is able to guess a decent name from an email' do
|
|
expect(User.suggest_name('sam.saffron@gmail.com')).to eq('Sam Saffron')
|
|
end
|
|
end
|
|
|
|
describe 'username format' do
|
|
it "should be #{SiteSetting.min_username_length} chars or longer" do
|
|
@user = Fabricate.build(:user)
|
|
@user.username = 'ss'
|
|
expect(@user.save).to eq(false)
|
|
end
|
|
|
|
it "should never end with a ." do
|
|
@user = Fabricate.build(:user)
|
|
@user.username = 'sam.'
|
|
expect(@user.save).to eq(false)
|
|
end
|
|
|
|
it "should never contain spaces" do
|
|
@user = Fabricate.build(:user)
|
|
@user.username = 'sam s'
|
|
expect(@user.save).to eq(false)
|
|
end
|
|
|
|
['Bad One', 'Giraf%fe', 'Hello!', '@twitter', 'me@example.com', 'no.dots', 'purple.', '.bilbo', '_nope', 'sa$sy'].each do |bad_nickname|
|
|
it "should not allow username '#{bad_nickname}'" do
|
|
@user = Fabricate.build(:user)
|
|
@user.username = bad_nickname
|
|
expect(@user.save).to eq(false)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'username uniqueness' do
|
|
before do
|
|
@user = Fabricate.build(:user)
|
|
@user.save!
|
|
@codinghorror = Fabricate.build(:coding_horror)
|
|
end
|
|
|
|
it "should not allow saving if username is reused" do
|
|
@codinghorror.username = @user.username
|
|
expect(@codinghorror.save).to eq(false)
|
|
end
|
|
|
|
it "should not allow saving if username is reused in different casing" do
|
|
@codinghorror.username = @user.username.upcase
|
|
expect(@codinghorror.save).to eq(false)
|
|
end
|
|
end
|
|
|
|
context '.username_available?' do
|
|
it "returns true for a username that is available" do
|
|
expect(User.username_available?('BruceWayne')).to eq(true)
|
|
end
|
|
|
|
it 'returns false when a username is taken' do
|
|
expect(User.username_available?(Fabricate(:user).username)).to eq(false)
|
|
end
|
|
end
|
|
|
|
describe 'email_validator' do
|
|
it 'should allow good emails' do
|
|
user = Fabricate.build(:user, email: 'good@gmail.com')
|
|
expect(user).to be_valid
|
|
end
|
|
|
|
it 'should reject some emails based on the email_domains_blacklist site setting' do
|
|
SiteSetting.stubs(:email_domains_blacklist).returns('mailinator.com')
|
|
expect(Fabricate.build(:user, email: 'notgood@mailinator.com')).not_to be_valid
|
|
expect(Fabricate.build(:user, email: 'mailinator@gmail.com')).to be_valid
|
|
end
|
|
|
|
it 'should reject some emails based on the email_domains_blacklist site setting' do
|
|
SiteSetting.stubs(:email_domains_blacklist).returns('mailinator.com|trashmail.net')
|
|
expect(Fabricate.build(:user, email: 'notgood@mailinator.com')).not_to be_valid
|
|
expect(Fabricate.build(:user, email: 'notgood@trashmail.net')).not_to be_valid
|
|
expect(Fabricate.build(:user, email: 'mailinator.com@gmail.com')).to be_valid
|
|
end
|
|
|
|
it 'should not reject partial matches' do
|
|
SiteSetting.stubs(:email_domains_blacklist).returns('mail.com')
|
|
expect(Fabricate.build(:user, email: 'mailinator@gmail.com')).to be_valid
|
|
end
|
|
|
|
it 'should reject some emails based on the email_domains_blacklist site setting ignoring case' do
|
|
SiteSetting.stubs(:email_domains_blacklist).returns('trashmail.net')
|
|
expect(Fabricate.build(:user, email: 'notgood@TRASHMAIL.NET')).not_to be_valid
|
|
end
|
|
|
|
it 'should not interpret a period as a wildcard' do
|
|
SiteSetting.stubs(:email_domains_blacklist).returns('trashmail.net')
|
|
expect(Fabricate.build(:user, email: 'good@trashmailinet.com')).to be_valid
|
|
end
|
|
|
|
it 'should not be used to validate existing records' do
|
|
u = Fabricate(:user, email: 'in_before_blacklisted@fakemail.com')
|
|
SiteSetting.stubs(:email_domains_blacklist).returns('fakemail.com')
|
|
expect(u).to be_valid
|
|
end
|
|
|
|
it 'should be used when email is being changed' do
|
|
SiteSetting.stubs(:email_domains_blacklist).returns('mailinator.com')
|
|
u = Fabricate(:user, email: 'good@gmail.com')
|
|
u.email = 'nope@mailinator.com'
|
|
expect(u).not_to be_valid
|
|
end
|
|
|
|
it 'whitelist should reject some emails based on the email_domains_whitelist site setting' do
|
|
SiteSetting.stubs(:email_domains_whitelist).returns('vaynermedia.com')
|
|
expect(Fabricate.build(:user, email: 'notgood@mailinator.com')).not_to be_valid
|
|
expect(Fabricate.build(:user, email: 'sbauch@vaynermedia.com')).to be_valid
|
|
end
|
|
|
|
it 'should reject some emails based on the email_domains_whitelist site setting when whitelisting multiple domains' do
|
|
SiteSetting.stubs(:email_domains_whitelist).returns('vaynermedia.com|gmail.com')
|
|
expect(Fabricate.build(:user, email: 'notgood@mailinator.com')).not_to be_valid
|
|
expect(Fabricate.build(:user, email: 'notgood@trashmail.net')).not_to be_valid
|
|
expect(Fabricate.build(:user, email: 'mailinator.com@gmail.com')).to be_valid
|
|
expect(Fabricate.build(:user, email: 'mailinator.com@vaynermedia.com')).to be_valid
|
|
end
|
|
|
|
it 'should accept some emails based on the email_domains_whitelist site setting ignoring case' do
|
|
SiteSetting.stubs(:email_domains_whitelist).returns('vaynermedia.com')
|
|
expect(Fabricate.build(:user, email: 'good@VAYNERMEDIA.COM')).to be_valid
|
|
end
|
|
|
|
it 'email whitelist should not be used to validate existing records' do
|
|
u = Fabricate(:user, email: 'in_before_whitelisted@fakemail.com')
|
|
SiteSetting.stubs(:email_domains_blacklist).returns('vaynermedia.com')
|
|
expect(u).to be_valid
|
|
end
|
|
|
|
it 'email whitelist should be used when email is being changed' do
|
|
SiteSetting.stubs(:email_domains_whitelist).returns('vaynermedia.com')
|
|
u = Fabricate(:user, email: 'good@vaynermedia.com')
|
|
u.email = 'nope@mailinator.com'
|
|
expect(u).not_to be_valid
|
|
end
|
|
end
|
|
|
|
describe 'passwords' do
|
|
before do
|
|
@user = Fabricate.build(:user, active: false)
|
|
@user.password = "ilovepasta"
|
|
@user.save!
|
|
end
|
|
|
|
it "should have a valid password after the initial save" do
|
|
expect(@user.confirm_password?("ilovepasta")).to eq(true)
|
|
end
|
|
|
|
it "should not have an active account after initial save" do
|
|
expect(@user.active).to eq(false)
|
|
end
|
|
end
|
|
|
|
describe "previous_visit_at" do
|
|
|
|
let(:user) { Fabricate(:user) }
|
|
let!(:first_visit_date) { Time.zone.now }
|
|
let!(:second_visit_date) { 2.hours.from_now }
|
|
let!(:third_visit_date) { 5.hours.from_now }
|
|
|
|
before do
|
|
SiteSetting.stubs(:active_user_rate_limit_secs).returns(0)
|
|
SiteSetting.stubs(:previous_visit_timeout_hours).returns(1)
|
|
end
|
|
|
|
it "should act correctly" do
|
|
expect(user.previous_visit_at).to eq(nil)
|
|
|
|
# first visit
|
|
user.update_last_seen!(first_visit_date)
|
|
expect(user.previous_visit_at).to eq(nil)
|
|
|
|
# updated same time
|
|
user.update_last_seen!(first_visit_date)
|
|
user.reload
|
|
expect(user.previous_visit_at).to eq(nil)
|
|
|
|
# second visit
|
|
user.update_last_seen!(second_visit_date)
|
|
user.reload
|
|
expect(user.previous_visit_at).to be_within_one_second_of(first_visit_date)
|
|
|
|
# third visit
|
|
user.update_last_seen!(third_visit_date)
|
|
user.reload
|
|
expect(user.previous_visit_at).to be_within_one_second_of(second_visit_date)
|
|
end
|
|
|
|
end
|
|
|
|
describe "last_seen_at" do
|
|
let(:user) { Fabricate(:user) }
|
|
|
|
it "should have a blank last seen on creation" do
|
|
expect(user.last_seen_at).to eq(nil)
|
|
end
|
|
|
|
it "should have 0 for days_visited" do
|
|
expect(user.user_stat.days_visited).to eq(0)
|
|
end
|
|
|
|
describe 'with no previous values' do
|
|
let!(:date) { Time.zone.now }
|
|
|
|
before do
|
|
Timecop.freeze(date)
|
|
user.update_last_seen!
|
|
end
|
|
|
|
after do
|
|
Timecop.return
|
|
end
|
|
|
|
it "updates last_seen_at" do
|
|
expect(user.last_seen_at).to be_within_one_second_of(date)
|
|
end
|
|
|
|
it "should have 0 for days_visited" do
|
|
user.reload
|
|
expect(user.user_stat.days_visited).to eq(1)
|
|
end
|
|
|
|
it "should log a user_visit with the date" do
|
|
expect(user.user_visits.first.visited_at).to eq(date.to_date)
|
|
end
|
|
|
|
context "called twice" do
|
|
|
|
before do
|
|
Timecop.freeze(date)
|
|
user.update_last_seen!
|
|
user.update_last_seen!
|
|
user.reload
|
|
end
|
|
|
|
after do
|
|
Timecop.return
|
|
end
|
|
|
|
it "doesn't increase days_visited twice" do
|
|
expect(user.user_stat.days_visited).to eq(1)
|
|
end
|
|
|
|
end
|
|
|
|
describe "after 3 days" do
|
|
let!(:future_date) { 3.days.from_now }
|
|
|
|
before do
|
|
Timecop.freeze(future_date)
|
|
user.update_last_seen!
|
|
end
|
|
|
|
after do
|
|
Timecop.return
|
|
end
|
|
|
|
it "should log a second visited_at record when we log an update later" do
|
|
expect(user.user_visits.count).to eq(2)
|
|
end
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
describe 'email_confirmed?' do
|
|
let(:user) { Fabricate(:user) }
|
|
|
|
context 'when email has not been confirmed yet' do
|
|
it 'should return false' do
|
|
expect(user.email_confirmed?).to eq(false)
|
|
end
|
|
end
|
|
|
|
context 'when email has been confirmed' do
|
|
it 'should return true' do
|
|
token = user.email_tokens.find_by(email: user.email)
|
|
EmailToken.confirm(token.token)
|
|
expect(user.email_confirmed?).to eq(true)
|
|
end
|
|
end
|
|
|
|
context 'when user has no email tokens for some reason' do
|
|
it 'should return false' do
|
|
user.email_tokens.each {|t| t.destroy}
|
|
user.reload
|
|
expect(user.email_confirmed?).to eq(true)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "flag_linked_posts_as_spam" do
|
|
let(:user) { Fabricate(:user) }
|
|
let!(:admin) { Fabricate(:admin) }
|
|
let!(:post) { PostCreator.new(user, title: "this topic contains spam", raw: "this post has a link: http://discourse.org").create }
|
|
let!(:another_post) { PostCreator.new(user, title: "this topic also contains spam", raw: "this post has a link: http://discourse.org/asdfa").create }
|
|
let!(:post_without_link) { PostCreator.new(user, title: "this topic shouldn't be spam", raw: "this post has no links in it.").create }
|
|
|
|
it "has flagged all the user's posts as spam" do
|
|
user.flag_linked_posts_as_spam
|
|
|
|
post.reload
|
|
expect(post.spam_count).to eq(1)
|
|
|
|
another_post.reload
|
|
expect(another_post.spam_count).to eq(1)
|
|
|
|
post_without_link.reload
|
|
expect(post_without_link.spam_count).to eq(0)
|
|
|
|
# It doesn't raise an exception if called again
|
|
user.flag_linked_posts_as_spam
|
|
|
|
end
|
|
|
|
end
|
|
|
|
describe '#readable_name' do
|
|
context 'when name is missing' do
|
|
it 'returns just the username' do
|
|
expect(Fabricate(:user, username: 'foo', name: nil).readable_name).to eq('foo')
|
|
end
|
|
end
|
|
context 'when name and username are identical' do
|
|
it 'returns just the username' do
|
|
expect(Fabricate(:user, username: 'foo', name: 'foo').readable_name).to eq('foo')
|
|
end
|
|
end
|
|
context 'when name and username are not identical' do
|
|
it 'returns the name and username' do
|
|
expect(Fabricate(:user, username: 'foo', name: 'Bar Baz').readable_name).to eq('Bar Baz (foo)')
|
|
end
|
|
end
|
|
end
|
|
|
|
describe '.find_by_username_or_email' do
|
|
it 'finds users' do
|
|
bob = Fabricate(:user, username: 'bob', email: 'bob@example.com')
|
|
found_user = User.find_by_username_or_email('Bob')
|
|
expect(found_user).to eq bob
|
|
|
|
found_user = User.find_by_username_or_email('bob@Example.com')
|
|
expect(found_user).to eq bob
|
|
|
|
found_user = User.find_by_username_or_email('Bob@Example.com')
|
|
expect(found_user).to eq bob
|
|
|
|
found_user = User.find_by_username_or_email('bob1')
|
|
expect(found_user).to be_nil
|
|
|
|
found_user = User.find_by_email('bob@Example.com')
|
|
expect(found_user).to eq bob
|
|
|
|
found_user = User.find_by_email('BOB@Example.com')
|
|
expect(found_user).to eq bob
|
|
|
|
found_user = User.find_by_email('bob')
|
|
expect(found_user).to be_nil
|
|
|
|
found_user = User.find_by_username('bOb')
|
|
expect(found_user).to eq bob
|
|
end
|
|
|
|
end
|
|
|
|
describe "#added_a_day_ago?" do
|
|
context "when user is more than a day old" do
|
|
subject(:user) { Fabricate(:user, created_at: Date.today - 2.days) }
|
|
|
|
it "returns false" do
|
|
expect(user).to_not be_added_a_day_ago
|
|
end
|
|
end
|
|
|
|
context "is less than a day old" do
|
|
subject(:user) { Fabricate(:user) }
|
|
|
|
it "returns true" do
|
|
expect(user).to be_added_a_day_ago
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'api keys' do
|
|
let(:admin) { Fabricate(:admin) }
|
|
let(:other_admin) { Fabricate(:admin) }
|
|
let(:user) { Fabricate(:user) }
|
|
|
|
describe '.generate_api_key' do
|
|
|
|
it "generates an api key when none exists, and regenerates when it does" do
|
|
expect(user.api_key).to be_blank
|
|
|
|
# Generate a key
|
|
api_key = user.generate_api_key(admin)
|
|
expect(api_key.user).to eq(user)
|
|
expect(api_key.key).to be_present
|
|
expect(api_key.created_by).to eq(admin)
|
|
|
|
user.reload
|
|
expect(user.api_key).to eq(api_key)
|
|
|
|
# Regenerate a key. Keeps the same record, updates the key
|
|
new_key = user.generate_api_key(other_admin)
|
|
expect(new_key.id).to eq(api_key.id)
|
|
expect(new_key.key).to_not eq(api_key.key)
|
|
expect(new_key.created_by).to eq(other_admin)
|
|
end
|
|
|
|
end
|
|
|
|
describe '.revoke_api_key' do
|
|
|
|
it "revokes an api key when exists" do
|
|
expect(user.api_key).to be_blank
|
|
|
|
# Revoke nothing does nothing
|
|
user.revoke_api_key
|
|
user.reload
|
|
expect(user.api_key).to be_blank
|
|
|
|
# When a key is present it is removed
|
|
user.generate_api_key(admin)
|
|
user.reload
|
|
user.revoke_api_key
|
|
user.reload
|
|
expect(user.api_key).to be_blank
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
describe "posted too much in topic" do
|
|
let!(:user) { Fabricate(:user, trust_level: TrustLevel[0]) }
|
|
let!(:topic) { Fabricate(:post).topic }
|
|
|
|
before do
|
|
# To make testing easier, say 1 reply is too much
|
|
SiteSetting.stubs(:newuser_max_replies_per_topic).returns(1)
|
|
end
|
|
|
|
context "for a user who didn't create the topic" do
|
|
let!(:post) { Fabricate(:post, topic: topic, user: user) }
|
|
|
|
it "does not return true for staff" do
|
|
user.stubs(:staff?).returns(true)
|
|
expect(user.posted_too_much_in_topic?(topic.id)).to eq(false)
|
|
end
|
|
|
|
it "returns true when the user has posted too much" do
|
|
expect(user.posted_too_much_in_topic?(topic.id)).to eq(true)
|
|
end
|
|
|
|
context "with a reply" do
|
|
before do
|
|
PostCreator.new(Fabricate(:user), raw: 'whatever this is a raw post', topic_id: topic.id, reply_to_post_number: post.post_number).create
|
|
end
|
|
|
|
it "resets the `posted_too_much` threshold" do
|
|
expect(user.posted_too_much_in_topic?(topic.id)).to eq(false)
|
|
end
|
|
end
|
|
end
|
|
|
|
it "returns false for a user who created the topic" do
|
|
topic_user = topic.user
|
|
topic_user.trust_level = TrustLevel[0]
|
|
expect(topic.user.posted_too_much_in_topic?(topic.id)).to eq(false)
|
|
end
|
|
|
|
end
|
|
|
|
describe "#find_email" do
|
|
|
|
let(:user) { Fabricate(:user, email: "bob@example.com") }
|
|
|
|
context "when email is exists in the email logs" do
|
|
before { user.stubs(:last_sent_email_address).returns("bob@lastemail.com") }
|
|
|
|
it "returns email from the logs" do
|
|
expect(user.find_email).to eq("bob@lastemail.com")
|
|
end
|
|
end
|
|
|
|
context "when email does not exist in the email logs" do
|
|
before { user.stubs(:last_sent_email_address).returns(nil) }
|
|
|
|
it "fetches the user's email" do
|
|
expect(user.find_email).to eq(user.email)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#gravatar_template" do
|
|
|
|
it "returns a gravatar based template" do
|
|
expect(User.gravatar_template("em@il.com")).to eq("//www.gravatar.com/avatar/6dc2fde946483a1d8a84b89345a1b638.png?s={size}&r=pg&d=identicon")
|
|
end
|
|
|
|
end
|
|
|
|
describe ".small_avatar_url" do
|
|
|
|
let(:user) { build(:user, username: 'Sam') }
|
|
|
|
it "returns a 45-pixel-wide avatar" do
|
|
expect(user.small_avatar_url).to eq("//test.localhost/letter_avatar/sam/45/#{LetterAvatar::VERSION}.png")
|
|
end
|
|
|
|
end
|
|
|
|
describe ".avatar_template_url" do
|
|
|
|
let(:user) { build(:user, uploaded_avatar_id: 99, username: 'Sam') }
|
|
|
|
it "returns a schemaless avatar template with correct id" do
|
|
expect(user.avatar_template_url).to eq("//test.localhost/user_avatar/test.localhost/sam/{size}/99.png")
|
|
end
|
|
|
|
it "returns a schemaless cdn-based avatar template" do
|
|
Rails.configuration.action_controller.stubs(:asset_host).returns("http://my.cdn.com")
|
|
expect(user.avatar_template_url).to eq("//my.cdn.com/user_avatar/test.localhost/sam/{size}/99.png")
|
|
end
|
|
|
|
end
|
|
|
|
describe "update_posts_read!" do
|
|
context "with a UserVisit record" do
|
|
let!(:user) { Fabricate(:user) }
|
|
let!(:now) { Time.zone.now }
|
|
before { user.update_last_seen!(now) }
|
|
|
|
it "with existing UserVisit record, increments the posts_read value" do
|
|
expect {
|
|
user_visit = user.update_posts_read!(2)
|
|
expect(user_visit.posts_read).to eq(2)
|
|
}.to_not change { UserVisit.count }
|
|
end
|
|
|
|
it "with no existing UserVisit record, creates a new UserVisit record and increments the posts_read count" do
|
|
expect {
|
|
user_visit = user.update_posts_read!(3, 5.days.ago)
|
|
expect(user_visit.posts_read).to eq(3)
|
|
}.to change { UserVisit.count }.by(1)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "primary_group_id" do
|
|
let!(:user) { Fabricate(:user) }
|
|
|
|
it "has no primary_group_id by default" do
|
|
expect(user.primary_group_id).to eq(nil)
|
|
end
|
|
|
|
context "when the user has a group" do
|
|
let!(:group) { Fabricate(:group) }
|
|
|
|
before do
|
|
group.usernames = user.username
|
|
group.save
|
|
user.primary_group_id = group.id
|
|
user.save
|
|
user.reload
|
|
end
|
|
|
|
it "should allow us to use it as a primary group" do
|
|
expect(user.primary_group_id).to eq(group.id)
|
|
|
|
# If we remove the user from the group
|
|
group.usernames = ""
|
|
group.save
|
|
|
|
# It should unset it from the primary_group_id
|
|
user.reload
|
|
expect(user.primary_group_id).to eq(nil)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "should_be_redirected_to_top" do
|
|
let!(:user) { Fabricate(:user) }
|
|
|
|
it "should be redirected to top when there is a reason to" do
|
|
user.expects(:redirected_to_top_reason).returns("42")
|
|
expect(user.should_be_redirected_to_top).to eq(true)
|
|
end
|
|
|
|
it "should not be redirected to top when there is no reason to" do
|
|
user.expects(:redirected_to_top_reason).returns(nil)
|
|
expect(user.should_be_redirected_to_top).to eq(false)
|
|
end
|
|
|
|
end
|
|
|
|
describe ".redirected_to_top_reason" do
|
|
let!(:user) { Fabricate(:user) }
|
|
|
|
it "should have no reason when `SiteSetting.redirect_users_to_top_page` is disabled" do
|
|
SiteSetting.expects(:redirect_users_to_top_page).returns(false)
|
|
expect(user.redirected_to_top_reason).to eq(nil)
|
|
end
|
|
|
|
context "when `SiteSetting.redirect_users_to_top_page` is enabled" do
|
|
before { SiteSetting.expects(:redirect_users_to_top_page).returns(true) }
|
|
|
|
it "should have no reason when top is not in the `SiteSetting.top_menu`" do
|
|
SiteSetting.expects(:top_menu).returns("latest")
|
|
expect(user.redirected_to_top_reason).to eq(nil)
|
|
end
|
|
|
|
context "and when top is in the `SiteSetting.top_menu`" do
|
|
before { SiteSetting.expects(:top_menu).returns("latest|top") }
|
|
|
|
it "should have no reason when there aren't enough topics" do
|
|
SiteSetting.expects(:has_enough_topics_to_redirect_to_top).returns(false)
|
|
expect(user.redirected_to_top_reason).to eq(nil)
|
|
end
|
|
|
|
context "and when there are enough topics" do
|
|
before { SiteSetting.expects(:has_enough_topics_to_redirect_to_top).returns(true) }
|
|
|
|
describe "a new user" do
|
|
before do
|
|
user.stubs(:trust_level).returns(0)
|
|
user.stubs(:last_seen_at).returns(5.minutes.ago)
|
|
end
|
|
|
|
it "should have a reason for the first visit" do
|
|
user.expects(:last_redirected_to_top_at).returns(nil)
|
|
user.expects(:update_last_redirected_to_top!).once
|
|
|
|
expect(user.redirected_to_top_reason).to eq(I18n.t('redirected_to_top_reasons.new_user'))
|
|
end
|
|
|
|
it "should not have a reason for next visits" do
|
|
user.expects(:last_redirected_to_top_at).returns(10.minutes.ago)
|
|
user.expects(:update_last_redirected_to_top!).never
|
|
|
|
expect(user.redirected_to_top_reason).to eq(nil)
|
|
end
|
|
end
|
|
|
|
describe "an older user" do
|
|
before { user.stubs(:trust_level).returns(1) }
|
|
|
|
it "should have a reason when the user hasn't been seen in a month" do
|
|
user.last_seen_at = 2.months.ago
|
|
user.expects(:update_last_redirected_to_top!).once
|
|
|
|
expect(user.redirected_to_top_reason).to eq(I18n.t('redirected_to_top_reasons.not_seen_in_a_month'))
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
describe "automatic avatar creation" do
|
|
it "sets a system avatar for new users" do
|
|
u = User.create!(username: "bob", email: "bob@bob.com")
|
|
u.reload
|
|
expect(u.uploaded_avatar_id).to eq(nil)
|
|
expect(u.avatar_template).to eq("/letter_avatar/bob/{size}/#{LetterAvatar::VERSION}.png")
|
|
end
|
|
end
|
|
|
|
describe "custom fields" do
|
|
it "allows modification of custom fields" do
|
|
user = Fabricate(:user)
|
|
|
|
expect(user.custom_fields["a"]).to eq(nil)
|
|
|
|
user.custom_fields["bob"] = "marley"
|
|
user.custom_fields["jack"] = "black"
|
|
user.save
|
|
|
|
user = User.find(user.id)
|
|
|
|
expect(user.custom_fields["bob"]).to eq("marley")
|
|
expect(user.custom_fields["jack"]).to eq("black")
|
|
|
|
user.custom_fields.delete("bob")
|
|
user.custom_fields["jack"] = "jill"
|
|
|
|
user.save
|
|
user = User.find(user.id)
|
|
|
|
expect(user.custom_fields).to eq({"jack" => "jill"})
|
|
end
|
|
end
|
|
|
|
describe "refresh_avatar" do
|
|
it "enqueues the update_gravatar job when automatically downloading gravatars" do
|
|
SiteSetting.automatically_download_gravatars = true
|
|
|
|
user = Fabricate(:user)
|
|
|
|
Jobs.expects(:enqueue).with(:update_gravatar, anything)
|
|
|
|
user.refresh_avatar
|
|
end
|
|
end
|
|
|
|
describe "#purge_unactivated" do
|
|
let!(:user) { Fabricate(:user) }
|
|
let!(:inactive) { Fabricate(:user, active: false) }
|
|
let!(:inactive_old) { Fabricate(:user, active: false, created_at: 1.month.ago) }
|
|
|
|
it 'should only remove old, unactivated users' do
|
|
User.purge_unactivated
|
|
all_users = User.all
|
|
expect(all_users.include?(user)).to eq(true)
|
|
expect(all_users.include?(inactive)).to eq(true)
|
|
expect(all_users.include?(inactive_old)).to eq(false)
|
|
end
|
|
end
|
|
|
|
describe "hash_passwords" do
|
|
|
|
let(:too_long) { "x" * (User.max_password_length + 1) }
|
|
|
|
def hash(password, salt)
|
|
User.new.send(:hash_password, password, salt)
|
|
end
|
|
|
|
it "returns the same hash for the same password and salt" do
|
|
expect(hash('poutine', 'gravy')).to eq(hash('poutine', 'gravy'))
|
|
end
|
|
|
|
it "returns a different hash for the same salt and different password" do
|
|
expect(hash('poutine', 'gravy')).not_to eq(hash('fries', 'gravy'))
|
|
end
|
|
|
|
it "returns a different hash for the same password and different salt" do
|
|
expect(hash('poutine', 'gravy')).not_to eq(hash('poutine', 'cheese'))
|
|
end
|
|
|
|
it "raises an error when passwords are too long" do
|
|
expect { hash(too_long, 'gravy') }.to raise_error
|
|
end
|
|
|
|
end
|
|
|
|
end
|