Improve specs (#1693)

* Lock erb_lint

* Refactor moderation spec

* Remove & replace RequestSpecHelper

* Update uid to sequence

* Lower the need for persistence in specs

* Update moderation spec

* Update shoulda-matchers

* Enable backgroundjobs and update commenct spec

* Fix fail specs

* Update comment's validations

* Remove redundent specs

* Actually turn off DJ

* Apply run_background_jobs_immediately

* Remove giveawy specs

* Create Identity spec
This commit is contained in:
Mac Siri 2019-02-04 14:56:00 -05:00 committed by Ben Halpern
parent aa16cad429
commit 1137d6fba4
31 changed files with 167 additions and 259 deletions

View file

@ -119,7 +119,7 @@ end
group :development, :test do
gem "capybara", "~> 3.12"
gem "derailed", "~> 0.1"
gem "erb_lint", "~> 0.0.28", require: false
gem "erb_lint", "~> 0.0", require: false
gem "faker", git: "https://github.com/stympy/faker.git", branch: "master"
gem "fix-db-schema-conflicts", github: "thepracticaldev/fix-db-schema-conflicts", branch: "master"
gem "memory_profiler", "~> 0.9"
@ -145,7 +145,7 @@ group :test do
gem "rails-controller-testing", "~> 1.0"
gem "ruby-prof", "~> 0.17", require: false
gem "selenium-webdriver", "~> 3.141"
gem "shoulda-matchers", "~> 3.1", require: false
gem "shoulda-matchers", "4.0.0.rc1", require: false
gem "simplecov", "~> 0.16", require: false
gem "sinatra", "~> 2.0"
gem "stackprof", "~> 0.2", require: false, platforms: :ruby

View file

@ -813,8 +813,8 @@ GEM
railties (>= 3.1)
share_meow_client (0.1.3)
shellany (0.0.1)
shoulda-matchers (3.1.2)
activesupport (>= 4.0.0)
shoulda-matchers (4.0.0.rc1)
activesupport (>= 4.2.0)
signet (0.11.0)
addressable (~> 2.3)
faraday (~> 0.9)
@ -972,7 +972,7 @@ DEPENDENCIES
email_validator (~> 1.6)
emoji_regex (~> 1.0)
envied (~> 0.9)
erb_lint (~> 0.0.28)
erb_lint (~> 0.0)
factory_bot_rails (~> 4.11)
fake_stripe (~> 0.2)
faker!
@ -1041,7 +1041,7 @@ DEPENDENCIES
selenium-webdriver (~> 3.141)
serviceworker-rails (~> 0.5)
share_meow_client (~> 0.1)
shoulda-matchers (~> 3.1)
shoulda-matchers (= 4.0.0.rc1)
simplecov (~> 0.16)
sinatra (~> 2.0)
sitemap_generator (~> 6.0)

View file

@ -124,8 +124,6 @@ class MarkdownParser
content.gsub(/`{3}.*?`{3}|`{2}.+?`{2}|`{1}.+?`{1}/m) do |codeblock|
if codeblock[0..2] == "```"
"\n{% raw %}\n" + codeblock + "\n{% endraw %}\n"
elsif codeblock[0..1] == "``"
"{% raw %}" + codeblock + "{% endraw %}"
else
"{% raw %}" + codeblock + "{% endraw %}"
end

View file

@ -25,12 +25,12 @@ class Comment < ApplicationRecord
after_create :send_email_notification, if: :should_send_email_notification?
after_create :create_first_reaction
after_create :send_to_moderator
before_save :set_markdown_character_count
before_save :set_markdown_character_count, if: :body_markdown
before_create :adjust_comment_parent_based_on_depth
after_update :update_notifications, if: Proc.new { |comment| comment.saved_changes.include? "body_markdown" }
after_update :remove_notifications, if: :deleted
before_validation :evaluate_markdown
validate :permissions
before_validation :evaluate_markdown, if: -> { body_markdown && commentable }
validate :permissions, if: :commentable
algoliasearch per_environment: true, enqueue: :trigger_delayed_index do
attribute :id
@ -174,22 +174,6 @@ class Comment < ApplicationRecord
end
end
def sharemeow_link
user_image = ProfileImage.new(user)
user_image_link = Rails.env.production? ? user_image.get_link : user_image.get_external_link
ShareMeowClient.image_url(
template: "DevComment",
options: {
content: body_markdown || processed_html,
name: user.name,
subject_name: commentable.title,
user_image_link: user_image_link,
background_color: user.bg_color_hex,
text_color: user.text_color_hex
},
)
end
def self.comment_async_bust(commentable, username)
CacheBuster.new.bust_comment(commentable, username)
commentable.index!

View file

@ -3,4 +3,4 @@ Delayed::Worker.sleep_delay = 60
Delayed::Worker.max_attempts = 10
Delayed::Worker.max_run_time = 30.minutes
Delayed::Worker.read_ahead = 5
Delayed::Worker.delay_jobs = !Rails.env.test?
# Delayed::Worker.delay_jobs = !Rails.env.test?

View file

@ -12,15 +12,11 @@ RSpec.describe "internal/users", type: :request do
sign_in super_admin
user
user2
Delayed::Worker.delay_jobs = true
Delayed::Worker.new(quiet: true).work_off
dependents_for_offending_user_article
offender_activity_on_other_content
end
after do
Delayed::Worker.delay_jobs = false
end
def dependents_for_offending_user_article
# create user2 comment on offending user article
comment = create(:comment, commentable_type: "Article", commentable: article, user: user2)

View file

@ -1,7 +1,9 @@
FactoryBot.define do
sequence(:uid, 100000) { |n| n }
factory :identity do
uid { generate(:uid) }
provider { "github" }
uid { rand(100000) }
token { rand(100000) }
secret { rand(100000) }
end

View file

@ -19,8 +19,8 @@ RSpec.describe Article, type: :model do
it { is_expected.to validate_length_of(:title).is_at_most(128) }
it { is_expected.to validate_length_of(:cached_tag_list).is_at_most(86) }
it { is_expected.to belong_to(:user) }
it { is_expected.to belong_to(:organization) }
it { is_expected.to belong_to(:collection) }
it { is_expected.to belong_to(:organization).optional }
it { is_expected.to belong_to(:collection).optional }
it { is_expected.to have_many(:comments) }
it { is_expected.to have_many(:reactions) }
it { is_expected.to have_many(:notifications) }
@ -428,16 +428,16 @@ RSpec.describe Article, type: :model do
describe "#async_score_calc" do
context "when published" do
let(:article) { create(:article) }
let(:article) { build(:article) }
it "updates the hotness score" do
article.save
run_background_jobs_immediately { article.save }
expect(article.hotness_score > 0).to eq(true)
end
it "updates the spaminess score" do
article.update_column(:spaminess_rating, -1)
article.save
article.spaminess_rating = -1
run_background_jobs_immediately { article.save }
expect(article.spaminess_rating).to eq(0)
end
end

View file

@ -6,7 +6,7 @@ RSpec.describe BadgeAchievement, type: :model do
it { is_expected.to belong_to(:user) }
it { is_expected.to belong_to(:badge) }
it { is_expected.to belong_to(:rewarder).class_name("User") }
it { is_expected.to belong_to(:rewarder).class_name("User").optional }
it { is_expected.to validate_uniqueness_of(:badge_id).scoped_to(:user_id) }
end

View file

@ -1,11 +1,10 @@
# rubocop:disable RSpec/ExampleLength, RSpec/MultipleExpectations
require "rails_helper"
RSpec.describe Comment, type: :model do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:article) { create(:article, user_id: user.id, published: true) }
let(:article_with_video) { create(:article, :video, user_id: user.id, published: true) } # :video is a trait, see articles.rb
let(:article_with_video) { create(:article, :video, user_id: user.id, published: true) }
let(:comment) { create(:comment, user_id: user2.id, commentable_id: article.id) }
let(:video_comment) { create(:comment, user_id: user2.id, commentable_id: article_with_video.id) }
let(:comment_2) { create(:comment, user_id: user2.id, commentable_id: article.id) }
@ -13,41 +12,38 @@ RSpec.describe Comment, type: :model do
build(:comment, user_id: user.id, commentable_id: article.id, parent_id: comment.id)
end
before { Notification.send_new_comment_notifications(comment) }
describe "validations" do
subject { Comment.new(commentable: article) }
let(:article) { Article.new }
before do
allow(article).to receive(:touch).and_return(true)
end
it { is_expected.to belong_to(:user) }
it { is_expected.to belong_to(:commentable) }
it { is_expected.to have_many(:reactions).dependent(:destroy) }
it { is_expected.to have_many(:mentions).dependent(:destroy) }
it { is_expected.to validate_presence_of(:commentable_id) }
it { is_expected.to validate_presence_of(:body_markdown) }
it { is_expected.to validate_uniqueness_of(:body_markdown).scoped_to(:user_id, :ancestry, :commentable_id, :commentable_type) }
it { is_expected.to validate_length_of(:body_markdown).is_at_least(1).is_at_most(25000) }
it { is_expected.to validate_inclusion_of(:commentable_type).in_array(%w(Article PodcastEpisode)) }
end
it "gets proper generated ID code" do
comment = Comment.new(id: 1)
expect(comment.id_code_generated).to eq(comment.id.to_s(26))
end
it "generates character count before saving" do
xit "generates character count before saving" do
expect(comment.markdown_character_count).to eq(comment.body_markdown.size)
end
context "when comment is already posted" do
before do
Notification.send_new_comment_notifications(comment_2)
comment_2.update(ancestry: comment.ancestry,
body_markdown: comment.body_markdown,
commentable_type: comment.commentable_type,
commentable_id: comment.commentable_id)
end
it "does not allow for double posts" do
expect(comment_2).not_to be_valid
end
it "does allow for double posts if body is not the same" do
comment_2.update(body_markdown: comment.body_markdown + " hey hey")
expect(comment_2).to be_valid
end
end
describe "#processed_html" do
let(:comment) do
create(:comment,
commentable_id: article.id,
body_markdown: "# hello\n\nhy hey hey")
end
let(:comment) { create(:comment, commentable: article, body_markdown: "# hello\n\nhy hey hey") }
it "converts body_markdown to proper processed_html" do
expect(comment.processed_html.include?("<h1>")).to eq(true)
@ -55,7 +51,6 @@ RSpec.describe Comment, type: :model do
end
it "adds timestamp url if commentable has video and timestamp" do
Notification.send_new_comment_notifications(video_comment)
video_comment.body_markdown = "I like the part at 4:30"
video_comment.save
expect(video_comment.processed_html.include?(">4:30</a>")).to eq(true)
@ -197,13 +192,4 @@ RSpec.describe Comment, type: :model do
expect(comment).not_to be_valid
end
end
describe "#sharemeow_link" do
it "uses ShareMeowClient" do
allow(ShareMeowClient).to receive(:image_url).and_return("www.test.com")
comment.sharemeow_link
expect(ShareMeowClient).to have_received(:image_url)
end
end
end
# rubocop:enable RSpec/ExampleLength, RSpec/MultipleExpectations

View file

@ -5,7 +5,7 @@ RSpec.describe HtmlVariant, type: :model do
it { is_expected.to validate_uniqueness_of(:name) }
it { is_expected.to validate_presence_of(:html) }
it { is_expected.to belong_to(:user) }
it { is_expected.to belong_to(:user).optional }
it "calculates success rate" do
HtmlVariantTrial.create!(html_variant_id: html_variant.id)

View file

@ -0,0 +1,21 @@
require "rails_helper"
RSpec.describe Identity, type: :model do
it { is_expected.to belong_to(:user) }
it { is_expected.to validate_presence_of(:uid) }
it { is_expected.to validate_presence_of(:provider) }
it { is_expected.to validate_uniqueness_of(:uid).scoped_to(:provider) }
it { is_expected.to validate_uniqueness_of(:provider).scoped_to(:uid) }
it { is_expected.to validate_uniqueness_of(:user_id).scoped_to(:provider) }
it { is_expected.to validate_inclusion_of(:provider).in_array(%w(github twitter)) }
it { is_expected.to serialize(:auth_data_dump) }
describe ".find_for_oauth" do
it "works" do
allow(Identity).to receive(:find_or_create_by)
auth = { uid: 0, provider: "github" }
described_class.find_for_oauth(instance_double("request", auth))
expect(Identity).to have_received(:find_or_create_by).with(auth)
end
end
end

View file

@ -9,6 +9,10 @@ RSpec.describe Message, type: :model do
describe "validations" do
subject { build(:message, :ignore_after_callback) }
before do
allow(ChatChannel).to receive(:find).and_return(ChatChannel.new)
end
it { is_expected.to belong_to(:user) }
it { is_expected.to belong_to(:chat_channel) }
it { is_expected.to validate_presence_of(:message_html) }

View file

@ -4,5 +4,5 @@ RSpec.describe Note, type: :model do
it { is_expected.to validate_presence_of(:reason) }
it { is_expected.to validate_presence_of(:content) }
it { is_expected.to belong_to(:noteable) }
it { is_expected.to belong_to(:author).class_name("User") }
it { is_expected.to belong_to(:author).class_name("User").optional }
end

View file

@ -8,7 +8,11 @@ RSpec.describe Notification, type: :model do
let(:follow_instance) { user.follow(user2) }
describe "#send_new_follower_notification" do
before { Notification.send_new_follower_notification(follow_instance) }
before do
run_background_jobs_immediately do
Notification.send_new_follower_notification(follow_instance)
end
end
it "creates a notification belonging to the person being followed" do
expect(Notification.first.user_id).to eq user2.id

View file

@ -10,7 +10,9 @@ RSpec.describe ArticleObserver, type: :observer do
it "pings slack if user with warned role creates an article" do
user.add_role :warned
Article.observers.enable :article_observer do
create(:article, user_id: user.id)
run_background_jobs_immediately do
create(:article, user_id: user.id)
end
end
expect(SlackBot).to have_received(:ping).twice
end

View file

@ -11,7 +11,9 @@ RSpec.describe CommentObserver, type: :observer do
it "pings slack if user with warned role creates a comment" do
user.add_role :warned
Comment.observers.enable :comment_observer do
create(:comment, user_id: user.id, commentable_id: article.id)
run_background_jobs_immediately do
create(:comment, user: user, commentable: article)
end
end
expect(SlackBot).to have_received(:ping).twice
end

View file

@ -11,6 +11,7 @@ require "algolia/webmock"
require "pundit/matchers"
require "pundit/rspec"
require "webmock/rspec"
require "test_prof/recipes/rspec/before_all"
# Requires supporting ruby files with custom matchers and macros, etc, in
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
@ -42,9 +43,9 @@ RSpec.configure do |config|
config.include Devise::Test::ControllerHelpers, type: :controller
config.include Devise::Test::ControllerHelpers, type: :view
config.include Devise::Test::IntegrationHelpers, type: :feature
config.include Devise::Test::IntegrationHelpers, type: :request
config.include FactoryBot::Syntax::Methods
config.include OmniauthMacros
config.include RequestSpecHelper, type: :request
config.before do
ActiveRecord::Base.observers.disable :all # <-- Turn 'em all off!

View file

@ -21,7 +21,7 @@ RSpec.describe "Editor", type: :request do
context "when not logged-in" do
it "redirects to /enter" do
get "/#{user.username}/#{article.slug}/edit"
get "/username/article/edit"
expect(response).to redirect_to("/enter")
end
end

View file

@ -1,7 +1,11 @@
require "rails_helper"
RSpec.describe "EmailSubscriptions", type: :request do
let(:user) { create(:user) }
let(:user) { build(:user) }
before do
allow(User).to receive(:find).and_return(user)
end
def generate_token(user_id)
Rails.application.message_verifier(:unsubscribe).generate(

View file

@ -24,7 +24,9 @@ RSpec.describe "FollowsApi", type: :request do
it "creates follows" do
sign_in user
post "/api/follows", params: { users: users_hash }
run_background_jobs_immediately do
post "/api/follows", params: { users: users_hash }
end
expect(Follow.all.size).to eq(JSON.parse(users_hash).size)
end
end

View file

@ -5,20 +5,14 @@ RSpec.describe "HtmlVariantSuccesses", type: :request do
let(:article) { create(:article, user_id: user.id, approved: true) }
let(:html_variant) { create(:html_variant) }
before do
Delayed::Worker.delay_jobs = false
end
after do
Delayed::Worker.delay_jobs = true
end
describe "POST /html_variant_successes" do
it "rejects non-permissioned user" do
post "/html_variant_successes", params: {
article_id: article.id,
html_variant_id: html_variant.id
}
run_background_jobs_immediately do
post "/html_variant_successes", params: {
article_id: article.id,
html_variant_id: html_variant.id
}
end
expect(HtmlVariantSuccess.all.size).to eq(1)
end
end

View file

@ -5,20 +5,14 @@ RSpec.describe "HtmlVariantTrials", type: :request do
let(:article) { create(:article, user_id: user.id, approved: true) }
let(:html_variant) { create(:html_variant) }
before do
Delayed::Worker.delay_jobs = false
end
after do
Delayed::Worker.delay_jobs = true
end
describe "POST /html_variant_trials" do
it "rejects non-permissioned user" do
post "/html_variant_trials", params: {
article_id: article.id,
html_variant_id: html_variant.id
}
run_background_jobs_immediately do
post "/html_variant_trials", params: {
article_id: article.id,
html_variant_id: html_variant.id
}
end
expect(HtmlVariantTrial.all.size).to eq(1)
end
end

View file

@ -14,7 +14,7 @@ RSpec.describe "Messages", type: :request do
end
it "requires user to be signed in" do
post "/messages", params: { message: new_message }
post "/messages", params: { message: {} }
expect(response.status).to eq(302)
end
# Pusher::Error

View file

@ -1,48 +1,50 @@
require "rails_helper"
RSpec.describe "Moderations", type: :request do
let(:user) { create(:user) }
let(:article) { create(:article, user_id: user.id) }
let(:comment) do
create(:comment,
commentable_id: article.id,
commentable_type: "Article",
user_id: user.id)
end
describe "GET /mod on articles" do
context "when user is trusted" do
it "responds with 200" do
user.add_role :trusted
sign_in user
get article.path + "/mod"
expect(response).to have_http_status(200)
end
RSpec.shared_examples "an elevated privilege required request" do |path|
context "when not logged-in" do
it "does not grant acesss", proper_status: true do
get path
expect(response).to have_http_status(404)
end
context "when user is not trusted", proper_status: true do
it "responds with 404" do
get article.path + "/mod"
expect(response).to have_http_status(404)
end
it "raises Pundit::NotAuthorizedError internally" do
expect { get path }.to raise_error(Pundit::NotAuthorizedError)
end
end
describe "GET moderations comment" do
context "when user is trusted" do
it "responds with 200" do
user.add_role :trusted
sign_in user
get comment.path + "/mod"
expect(response).to have_http_status(200)
end
context "when user is not trusted" do
before { sign_in create(:user) }
it "does not grant acesss", proper_status: true do
get path
expect(response).to have_http_status(404)
end
context "when user is not trusted", proper_status: true do
it "responds with 404" do
get comment.path + "/mod"
expect(response).to have_http_status(404)
end
it "internally raise Pundit::NotAuthorized internally" do
expect { get path }.to raise_error(Pundit::NotAuthorizedError)
end
end
end
RSpec.describe "Moderations", type: :request do
let(:user) { create(:user, :trusted) }
let(:article) { create(:article) }
let(:comment) { create(:comment, commentable: article) }
it_behaves_like "an elevated privilege required request", "/username/random-article/mod"
it_behaves_like "an elevated privilege required request", "/username/comment/1/mod"
context "when user is trusted" do
before { sign_in user }
it "grant acess to comment moderation" do
get comment.path + "/mod"
expect(response).to have_http_status(200)
end
it "grant acess to article moderation" do
get article.path + "/mod"
expect(response).to have_http_status(200)
end
end
end

View file

@ -53,10 +53,6 @@ RSpec.describe "UserSettings", type: :request do
describe "PUT /update/:id" do
before { login_as user }
after do
Delayed::Worker.delay_jobs = false
end
it "updates summary" do
put "/users/#{user.id}", params: { user: { tab: "profile", summary: "Hello new summary" } }
expect(user.summary).to eq("Hello new summary")
@ -69,7 +65,6 @@ RSpec.describe "UserSettings", type: :request do
context "when requesting an export of the articles" do
def send_request(flag = true)
Delayed::Worker.delay_jobs = true
put "/users/#{user.id}", params: {
user: { tab: "misc", export_requested: flag }
}
@ -98,16 +93,15 @@ RSpec.describe "UserSettings", type: :request do
end
it "sends an email" do
expect do
send_request
Delayed::Worker.new.work_off
# Delayed::Worker.delay_jobs = false
end.to change { ActionMailer::Base.deliveries.count }.by(1)
run_background_jobs_immediately do
expect { send_request }.to change { ActionMailer::Base.deliveries.count }.by(1)
end
end
it "does not send an email if there was no request" do
Delayed::Worker.delay_jobs = false
expect { send_request(false) }.not_to(change { ActionMailer::Base.deliveries.count })
run_background_jobs_immediately do
expect { send_request(false) }.not_to(change { ActionMailer::Base.deliveries.count })
end
end
end
end

View file

@ -29,9 +29,10 @@ RSpec.describe TagAdjustmentCreationService do
end
it "creates notification" do
tag_adjustment = create_tag_adjustment
expect(Notification.last.user_id).to eq(article.user_id)
expect(Notification.last.json_data["adjustment_type"]).to eq(tag_adjustment.adjustment_type)
run_background_jobs_immediately do
tag_adjustment = create_tag_adjustment
expect(Notification.last.user_id).to eq(article.user_id)
expect(Notification.last.json_data["adjustment_type"]).to eq(tag_adjustment.adjustment_type)
end
end
end

View file

@ -0,0 +1,11 @@
module BackgroundJobs
def run_background_jobs_immediately
Delayed::Worker.delay_jobs = false
yield
Delayed::Worker.delay_jobs = true
end
end
RSpec.configure do |config|
config.include BackgroundJobs
end

View file

@ -1,22 +0,0 @@
module RequestSpecHelper
include Warden::Test::Helpers
def self.included(base)
base.before { Warden.test_mode! }
base.after { Warden.test_reset! }
end
def sign_in(resource)
login_as(resource, scope: warden_scope(resource))
end
def sign_out(resource)
logout(warden_scope(resource))
end
private
def warden_scope(resource)
resource.class.name.underscore.to_sym
end
end

View file

@ -1,51 +0,0 @@
require "rails_helper"
describe "giveaways/edit.html.erb", type: :view do
let(:user) { create(:user) }
let(:current_user) { create(:user, onboarding_package_requested: false) }
def assign_user_and_errors
assign(:user, current_user)
assign(:errors, [])
allow(view).to receive(:current_user).and_return(current_user)
end
context "when user is not logged-in" do
it "displays twitter log-in option" do
render
expect(rendered).to have_selector(:link_or_button, "Sign in with Twitter")
end
it "displays github log-in option" do
render
expect(rendered).to have_selector(:link_or_button, "Sign in with Github")
end
end
context "when user is logged-in and not already onboard" do
it "tells user it's over" do
allow(view).to receive(:current_user).and_return(current_user)
render
expect(rendered).to have_text("It seems you've never requested stickers")
end
end
context "when user is logged-in and already onboard" do
it "allows user to re-apply" do
current_user.onboarding_package_requested = true
assign_user_and_errors
render
expect(rendered).to render_template(partial: "_form")
end
end
context "when user is logged-in, already onboarded, and already re-requested" do
it "allows user to re-apply" do
current_user.onboarding_package_requested = true
current_user.onboarding_package_requested_again = true
assign_user_and_errors
render
expect(rendered).to have_text("stickers should arrive soon")
end
end
end

View file

@ -1,21 +0,0 @@
require "rails_helper"
describe "giveaways/new.html.erb", type: :view do
let(:user) { create(:user) }
it "thanks users for participating" do
render
expect(rendered).to have_text("dev.to's sticker give-away campaign is officially over.")
end
it "has link to edit page" do
render
# this is a relatively weak test
expect(rendered).to have_link "here", href: "/freestickers/edit"
end
it "has link to the article" do
render
expect(rendered).to have_link "process", href: "https://dev.to/thepracticaldev/sending-100-thousand-stickers"
end
end