2020-04-20 14:03:57 -07:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
require "rails_helper"
|
2014-11-14 11:45:30 -08:00
|
|
|
|
2020-04-20 14:03:57 -07:00
|
|
|
RSpec.describe User, type: :model do
|
|
|
|
let!(:me) { FactoryBot.create :user }
|
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
describe "basic assigns" do
|
2020-04-20 14:03:57 -07:00
|
|
|
before :each do
|
|
|
|
@user = User.new(
|
2022-01-17 10:23:39 -08:00
|
|
|
screen_name: "FunnyMeme2004",
|
|
|
|
password: "y_u_no_secure_password?",
|
2023-03-28 23:03:13 -07:00
|
|
|
email: "nice.meme@nsa.gov",
|
2020-04-20 14:03:57 -07:00
|
|
|
)
|
2021-12-28 06:09:22 -08:00
|
|
|
Profile.new(user: @user)
|
2020-04-20 14:03:57 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
subject { @user }
|
2014-12-07 02:33:26 -08:00
|
|
|
|
2020-04-20 14:03:57 -07:00
|
|
|
it { should respond_to(:email) }
|
2014-12-07 02:33:26 -08:00
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
it "#email returns a string" do
|
|
|
|
expect(@user.email).to match "nice.meme@nsa.gov"
|
2020-04-20 14:03:57 -07:00
|
|
|
end
|
2014-12-07 02:33:26 -08:00
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
it "#motivation_header has a default value" do
|
|
|
|
expect(@user.profile.motivation_header).to match ""
|
2020-04-20 14:03:57 -07:00
|
|
|
end
|
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
it "does not save an invalid screen name" do
|
|
|
|
@user.screen_name = "$Funny-Meme-%&2004"
|
2020-04-20 14:03:57 -07:00
|
|
|
expect { @user.save! }.to raise_error(ActiveRecord::RecordInvalid)
|
|
|
|
end
|
2014-12-07 02:33:26 -08:00
|
|
|
end
|
|
|
|
|
2023-03-26 01:31:49 -07:00
|
|
|
describe "callbacks" do
|
|
|
|
describe "before_destroy" do
|
|
|
|
it "marks reports about this user as deleted" do
|
|
|
|
other_user = FactoryBot.create(:user)
|
|
|
|
other_user.report me, "va tutto benissimo"
|
|
|
|
|
|
|
|
expect { me.destroy }
|
|
|
|
.to change { Reports::User.find_by(target_id: me.id).deleted? }
|
|
|
|
.from(false)
|
|
|
|
.to(true)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "after_destroy" do
|
|
|
|
it "increments the users_destroyed metric" do
|
|
|
|
expect { me.destroy }.to change { Retrospring::Metrics::USERS_DESTROYED.values.values.sum }.by(1)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "after_create" do
|
|
|
|
subject :user do
|
|
|
|
User.create!(
|
|
|
|
screen_name: "konqi",
|
|
|
|
email: "konqi@example.rrerr.net",
|
|
|
|
password: "dragonsRQt5",
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "creates a profile for the user" do
|
|
|
|
expect { user }.to change { Profile.count }.by(1)
|
|
|
|
expect(Profile.find_by(user:).user).to eq(user)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "increments the users_created metric" do
|
|
|
|
expect { user }.to change { Retrospring::Metrics::USERS_CREATED.values.values.sum }.by(1)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-02-05 11:49:45 -08:00
|
|
|
describe "custom sharing url validation" do
|
|
|
|
subject do
|
|
|
|
FactoryBot.build(:user, sharing_custom_url: url).tap(&:validate).errors[:sharing_custom_url]
|
|
|
|
end
|
|
|
|
|
|
|
|
shared_examples_for "valid url" do |example_url|
|
|
|
|
context "when url is #{example_url}" do
|
|
|
|
let(:url) { example_url }
|
|
|
|
|
|
|
|
it "does not have validation errors" do
|
|
|
|
expect(subject).to be_empty
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
shared_examples_for "invalid url" do |example_url|
|
|
|
|
context "when url is #{example_url}" do
|
|
|
|
let(:url) { example_url }
|
|
|
|
|
|
|
|
it "has validation errors" do
|
|
|
|
expect(subject).not_to be_empty
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
include_examples "valid url", "https://myfunnywebsite.com/"
|
|
|
|
include_examples "valid url", "https://desu.social/share?text="
|
|
|
|
include_examples "valid url", "http://insecurebutvalid.business/"
|
|
|
|
include_examples "invalid url", "ftp://fileprotocols.cool/"
|
|
|
|
include_examples "invalid url", "notevenanurl"
|
2023-02-10 11:48:15 -08:00
|
|
|
include_examples "invalid url", %(https://richtig <strong>oarger</strong> shice) # passes the regexp, but trips up URI.parse
|
|
|
|
include_examples "invalid url", %(https://österreich.gv.at) # needs to be ASCII
|
2023-02-05 11:49:45 -08:00
|
|
|
end
|
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
describe "email validation" do
|
2022-01-11 09:37:07 -08:00
|
|
|
subject do
|
2023-02-20 12:06:10 -08:00
|
|
|
FactoryBot.build(:user, email:).tap(&:validate).errors[:email]
|
2022-01-11 09:37:07 -08:00
|
|
|
end
|
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
shared_examples_for "valid email" do |example_email|
|
2022-01-11 09:37:07 -08:00
|
|
|
context "when email is #{example_email}" do
|
|
|
|
let(:email) { example_email }
|
|
|
|
|
|
|
|
it "does not have validation errors" do
|
|
|
|
expect(subject).to be_empty
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
shared_examples_for "invalid email" do |example_email|
|
2022-01-11 09:37:07 -08:00
|
|
|
context "when email is #{example_email}" do
|
|
|
|
let(:email) { example_email }
|
|
|
|
|
|
|
|
it "has validation errors" do
|
|
|
|
expect(subject).not_to be_empty
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
include_examples "valid email", "ifyouusethismailyouarebanned@nilsding.org"
|
|
|
|
include_examples "valid email", "fritz.fantom@gmail.com"
|
|
|
|
include_examples "valid email", "fritz.fantom@columbiamail.co"
|
|
|
|
include_examples "valid email", "fritz.fantom@protonmail.com"
|
2022-03-03 21:13:38 -08:00
|
|
|
include_examples "valid email", "fritz.fantom@example.email"
|
2022-01-17 10:23:39 -08:00
|
|
|
include_examples "valid email", "fritz.fantom@enterprise.k8s.420stripes.k8s.needs.more.k8s.jira.atlassian.k8s.eu-central-1.s3.amazonaws.com"
|
2022-10-13 12:23:12 -07:00
|
|
|
include_examples "valid email", "fritz.fantom@emacs.horse"
|
2022-01-17 10:23:39 -08:00
|
|
|
include_examples "invalid email", "@jack"
|
2022-01-11 09:37:07 -08:00
|
|
|
|
|
|
|
# examples from the real world:
|
|
|
|
|
2022-10-12 22:59:54 -07:00
|
|
|
# .carrd is not a valid TLD
|
|
|
|
include_examples "invalid email", "fritz.fantom@gmail.carrd"
|
|
|
|
# neither is .con
|
2022-01-17 10:23:39 -08:00
|
|
|
include_examples "invalid email", "fritz.fantom@gmail.con"
|
|
|
|
include_examples "invalid email", "fritz.fantom@protonmail.con"
|
2022-10-12 22:59:54 -07:00
|
|
|
# nor .coom
|
2022-01-17 10:23:39 -08:00
|
|
|
include_examples "invalid email", "fritz.fantom@gmail.coom"
|
2022-06-21 10:33:51 -07:00
|
|
|
# nor .cmo
|
|
|
|
include_examples "invalid email", "gustav.geldsack@gmail.cmo"
|
2022-03-03 21:13:38 -08:00
|
|
|
# nor .mail (.email is, however)
|
|
|
|
include_examples "invalid email", "fritz.fantom@proton.mail"
|
2022-01-11 09:37:07 -08:00
|
|
|
# common typos:
|
2023-01-29 21:01:01 -08:00
|
|
|
include_examples "invalid email", "fritz.fantom@aoo.com"
|
2022-01-17 10:23:39 -08:00
|
|
|
include_examples "invalid email", "fritz.fantom@fmail.com"
|
2022-06-21 10:33:51 -07:00
|
|
|
include_examples "invalid email", "fritz.fantom@gamil.com"
|
2022-01-17 10:23:39 -08:00
|
|
|
include_examples "invalid email", "fritz.fantom@gemail.com"
|
2022-08-25 11:28:29 -07:00
|
|
|
include_examples "invalid email", "fritz.fantom@gmaik.com"
|
2022-06-21 10:33:51 -07:00
|
|
|
include_examples "invalid email", "fritz.fantom@gmail.cm"
|
2022-01-17 10:23:39 -08:00
|
|
|
include_examples "invalid email", "fritz.fantom@gmail.co"
|
2022-10-11 11:43:01 -07:00
|
|
|
include_examples "invalid email", "fritz.fantom@gmail.co.uk"
|
2023-01-06 01:26:32 -08:00
|
|
|
include_examples "invalid email", "fritz.fantom@gmail.om"
|
2022-01-17 10:23:39 -08:00
|
|
|
include_examples "invalid email", "fritz.fantom@gmailcom"
|
|
|
|
include_examples "invalid email", "fritz.fantom@gmaile.com"
|
|
|
|
include_examples "invalid email", "fritz.fantom@gmaill.com"
|
2022-01-17 10:22:34 -08:00
|
|
|
include_examples "invalid email", "fritz.fantom@gmali.com"
|
2023-01-29 10:19:35 -08:00
|
|
|
include_examples "invalid email", "fritz.fantom@gmaul.com"
|
2022-08-14 11:49:51 -07:00
|
|
|
include_examples "invalid email", "fritz.fantom@gnail.com"
|
2023-03-09 11:08:15 -08:00
|
|
|
include_examples "invalid email", "fritz.fantom@hornail.com"
|
2022-06-21 10:33:51 -07:00
|
|
|
include_examples "invalid email", "fritz.fantom@hotamil.com"
|
2022-07-24 12:43:28 -07:00
|
|
|
include_examples "invalid email", "fritz.fantom@hotmai.com"
|
2022-01-17 10:23:39 -08:00
|
|
|
include_examples "invalid email", "fritz.fantom@hotmailcom"
|
2022-03-03 21:13:38 -08:00
|
|
|
include_examples "invalid email", "fritz.fantom@hotmaill.com"
|
2022-06-21 10:33:51 -07:00
|
|
|
include_examples "invalid email", "fritz.fantom@iclooud.com"
|
2022-04-10 00:55:05 -07:00
|
|
|
include_examples "invalid email", "fritz.fantom@iclould.com"
|
2022-01-17 10:23:39 -08:00
|
|
|
include_examples "invalid email", "fritz.fantom@icluod.com"
|
2023-04-22 13:05:15 -07:00
|
|
|
include_examples "invalid email", "fritz.fantom@maibox.org"
|
2022-10-02 13:13:34 -07:00
|
|
|
include_examples "invalid email", "fritz.fantom@protonail.com"
|
2022-08-14 11:49:51 -07:00
|
|
|
include_examples "invalid email", "fritz.fantom@xn--gmail-xk1c.com"
|
2022-07-24 12:43:28 -07:00
|
|
|
include_examples "invalid email", "fritz.fantom@yahooo.com"
|
2022-08-14 11:49:51 -07:00
|
|
|
include_examples "invalid email", "fritz.fantom@☺gmail.com"
|
2022-06-21 10:33:51 -07:00
|
|
|
# gail.com would be a valid email address, but enough people typo it
|
|
|
|
#
|
|
|
|
# if you're the owner of that TLD and would like to use your email on
|
|
|
|
# retrospring, feel free to open a PR that removes this ;-)
|
|
|
|
include_examples "invalid email", "fritz.fantom@gail.com"
|
2022-01-11 09:37:07 -08:00
|
|
|
# no TLD
|
2022-01-17 10:23:39 -08:00
|
|
|
include_examples "invalid email", "fritz.fantom@gmail"
|
|
|
|
include_examples "invalid email", "fritz.fantom@protonmail"
|
2022-01-11 09:37:07 -08:00
|
|
|
end
|
|
|
|
|
2022-07-23 03:29:17 -07:00
|
|
|
describe "#to_param" do
|
|
|
|
subject { me.to_param }
|
|
|
|
|
|
|
|
it { is_expected.to eq me.screen_name }
|
|
|
|
end
|
|
|
|
|
2020-04-20 14:03:57 -07:00
|
|
|
# -- User::TimelineMethods --
|
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
shared_examples_for "result is blank" do
|
|
|
|
it "result is blank" do
|
2020-04-20 14:03:57 -07:00
|
|
|
expect(subject).to be_blank
|
|
|
|
end
|
2014-12-07 02:33:26 -08:00
|
|
|
end
|
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
describe "#timeline" do
|
2020-04-20 14:03:57 -07:00
|
|
|
subject { me.timeline }
|
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
context "user answered nothing and is not following anyone" do
|
|
|
|
include_examples "result is blank"
|
2020-04-20 14:03:57 -07:00
|
|
|
end
|
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
context "user answered something and is not following anyone" do
|
2020-04-20 14:03:57 -07:00
|
|
|
let(:answer) { FactoryBot.create(:answer, user: me) }
|
|
|
|
|
|
|
|
let(:expected) { [answer] }
|
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
it "includes the answer" do
|
2020-04-20 14:03:57 -07:00
|
|
|
expect(subject).to eq(expected)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
context "user answered something and follows users with answers" do
|
2020-04-20 14:03:57 -07:00
|
|
|
let(:user1) { FactoryBot.create(:user) }
|
|
|
|
let(:user2) { FactoryBot.create(:user) }
|
|
|
|
let(:answer1) { FactoryBot.create(:answer, user: user1, created_at: 12.hours.ago) }
|
|
|
|
let(:answer2) { FactoryBot.create(:answer, user: me, created_at: 1.day.ago) }
|
|
|
|
let(:answer3) { FactoryBot.create(:answer, user: user2, created_at: 10.minutes.ago) }
|
|
|
|
let(:answer4) { FactoryBot.create(:answer, user: user1, created_at: Time.now.utc) }
|
|
|
|
|
|
|
|
let!(:expected) do
|
|
|
|
[answer4, answer3, answer1, answer2]
|
|
|
|
end
|
|
|
|
|
|
|
|
before(:each) do
|
|
|
|
me.follow(user1)
|
|
|
|
me.follow(user2)
|
|
|
|
end
|
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
it "includes all answers" do
|
2020-04-20 14:03:57 -07:00
|
|
|
expect(subject).to include(answer1)
|
|
|
|
expect(subject).to include(answer2)
|
|
|
|
expect(subject).to include(answer3)
|
|
|
|
expect(subject).to include(answer4)
|
|
|
|
end
|
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
it "result is ordered by created_at in reverse order" do
|
2020-04-20 14:03:57 -07:00
|
|
|
expect(subject).to eq(expected)
|
|
|
|
end
|
|
|
|
end
|
2023-02-20 12:06:10 -08:00
|
|
|
|
2023-02-20 12:47:46 -08:00
|
|
|
context "user follows users with answers to questions from blocked or muted users", timeline_test_data: true do
|
2023-02-20 12:06:10 -08:00
|
|
|
before do
|
|
|
|
me.follow user1
|
|
|
|
me.follow user2
|
|
|
|
end
|
|
|
|
|
|
|
|
it "includes all answers to questions the user follows" do
|
|
|
|
expect(subject).to include(answer_to_anonymous)
|
|
|
|
expect(subject).to include(answer_to_normal_user)
|
|
|
|
expect(subject).to include(answer_to_normal_user_anonymous)
|
|
|
|
expect(subject).to include(answer_to_blocked_user_anonymous)
|
|
|
|
expect(subject).to include(answer_to_muted_user_anonymous)
|
|
|
|
expect(subject).to include(answer_to_blocked_user)
|
|
|
|
expect(subject).to include(answer_to_muted_user)
|
2023-02-20 12:47:46 -08:00
|
|
|
expect(subject).not_to include(answer_from_blocked_user)
|
|
|
|
expect(subject).not_to include(answer_from_muted_user)
|
2023-02-20 12:06:10 -08:00
|
|
|
end
|
|
|
|
|
|
|
|
context "when blocking and muting some users" do
|
|
|
|
before do
|
|
|
|
me.block blocked_user
|
|
|
|
me.mute muted_user
|
|
|
|
end
|
|
|
|
|
|
|
|
it "only includes answers to questions from users the user doesn't block or mute" do
|
|
|
|
expect(subject).to include(answer_to_anonymous)
|
|
|
|
expect(subject).to include(answer_to_normal_user)
|
|
|
|
expect(subject).to include(answer_to_normal_user_anonymous)
|
|
|
|
expect(subject).to include(answer_to_blocked_user_anonymous)
|
|
|
|
expect(subject).to include(answer_to_muted_user_anonymous)
|
|
|
|
expect(subject).not_to include(answer_to_blocked_user)
|
|
|
|
expect(subject).not_to include(answer_to_muted_user)
|
2023-02-20 12:47:46 -08:00
|
|
|
expect(subject).not_to include(answer_from_blocked_user)
|
|
|
|
expect(subject).not_to include(answer_from_muted_user)
|
2023-02-20 12:06:10 -08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2020-04-20 14:03:57 -07:00
|
|
|
end
|
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
describe "#cursored_timeline" do
|
2020-04-20 14:03:57 -07:00
|
|
|
let(:last_id) { nil }
|
|
|
|
|
2023-02-20 12:06:10 -08:00
|
|
|
subject { me.cursored_timeline(last_id:, size: 3) }
|
2020-04-20 14:03:57 -07:00
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
context "user answered nothing and is not following anyone" do
|
|
|
|
include_examples "result is blank"
|
2020-04-20 14:03:57 -07:00
|
|
|
end
|
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
context "user answered something and is not following anyone" do
|
2020-04-20 14:03:57 -07:00
|
|
|
let(:answer) { FactoryBot.create(:answer, user: me) }
|
|
|
|
|
|
|
|
let(:expected) { [answer] }
|
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
it "includes the answer" do
|
2020-04-20 14:03:57 -07:00
|
|
|
expect(subject).to eq(expected)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
context "user answered something and follows users with answers" do
|
2020-04-20 14:03:57 -07:00
|
|
|
let(:user1) { FactoryBot.create(:user) }
|
|
|
|
let(:user2) { FactoryBot.create(:user) }
|
|
|
|
let!(:answer1) { FactoryBot.create(:answer, user: me, created_at: 1.day.ago) }
|
|
|
|
let!(:answer2) { FactoryBot.create(:answer, user: user1, created_at: 12.hours.ago) }
|
|
|
|
let!(:answer3) { FactoryBot.create(:answer, user: user2, created_at: 10.minutes.ago) }
|
|
|
|
let!(:answer4) { FactoryBot.create(:answer, user: user1, created_at: Time.now.utc) }
|
|
|
|
|
|
|
|
before(:each) do
|
|
|
|
me.follow(user1)
|
|
|
|
me.follow(user2)
|
|
|
|
end
|
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
context "last_id is nil" do
|
2020-04-20 14:03:57 -07:00
|
|
|
let(:last_id) { nil }
|
|
|
|
let(:expected) do
|
|
|
|
[answer4, answer3, answer2]
|
|
|
|
end
|
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
it "includes three answers" do
|
2020-04-20 14:03:57 -07:00
|
|
|
expect(subject).not_to include(answer1)
|
|
|
|
expect(subject).to include(answer2)
|
|
|
|
expect(subject).to include(answer3)
|
|
|
|
expect(subject).to include(answer4)
|
|
|
|
end
|
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
it "result is ordered by created_at in reverse order" do
|
2020-04-20 14:03:57 -07:00
|
|
|
expect(subject).to eq(expected)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
context "last_id is answer2.id" do
|
2020-04-20 14:03:57 -07:00
|
|
|
let(:last_id) { answer2.id }
|
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
it "includes answer1" do
|
2020-04-20 14:03:57 -07:00
|
|
|
expect(subject).to include(answer1)
|
|
|
|
expect(subject).not_to include(answer2)
|
|
|
|
expect(subject).not_to include(answer3)
|
|
|
|
expect(subject).not_to include(answer4)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
context "last_id is answer1.id" do
|
2020-04-20 14:03:57 -07:00
|
|
|
let(:last_id) { answer1.id }
|
|
|
|
|
2022-01-17 10:23:39 -08:00
|
|
|
include_examples "result is blank"
|
2020-04-20 14:03:57 -07:00
|
|
|
end
|
|
|
|
end
|
2014-12-07 02:33:26 -08:00
|
|
|
end
|
2014-11-14 11:45:30 -08:00
|
|
|
end
|