From f2b0be2043a7588bbbadd6cf605ba74d5b574409 Mon Sep 17 00:00:00 2001 From: rhymes Date: Fri, 3 Apr 2020 17:00:06 +0200 Subject: [PATCH] Add Slack messengers: part 2 (#6916) * Add Slack::Messengers::ArticlePublished * Add Slack::Messengers::ReactionVomit * Add Slack::Messengers::CommentUserWarned * Add missing URL helpers * Add Slack::Messengers::ArticleFetchedFeed * Remove callback condition * Build, don't create --- app/helpers/application_helper.rb | 16 ++- app/lib/url.rb | 18 +++- app/models/article.rb | 16 +-- app/models/comment.rb | 18 +--- app/models/reaction.rb | 15 +-- app/services/rss_reader.rb | 13 +-- .../slack/messengers/article_fetched_feed.rb | 39 +++++++ .../slack/messengers/article_published.rb | 39 +++++++ .../slack/messengers/comment_user_warned.rb | 48 +++++++++ .../slack/messengers/reaction_vomit.rb | 43 ++++++++ spec/lib/url_spec.rb | 22 ++++ spec/models/article_spec.rb | 31 ++---- spec/models/comment_spec.rb | 29 ++--- spec/models/reaction_spec.rb | 19 +--- spec/services/rss_reader_spec.rb | 100 ++++++++++++------ .../messengers/article_fetched_feed_spec.rb | 45 ++++++++ .../messengers/article_published_spec.rb | 55 ++++++++++ .../messengers/comment_user_warned_spec.rb | 51 +++++++++ .../slack/messengers/feedback_spec.rb | 2 +- .../messengers/potential_spammer_spec.rb | 2 +- .../slack/messengers/rate_limit_spec.rb | 2 +- .../slack/messengers/reaction_vomit_spec.rb | 42 ++++++++ .../slack/messengers/sponsorship_spec.rb | 6 +- spec/support/initializers/vcr.rb | 4 + 24 files changed, 516 insertions(+), 159 deletions(-) create mode 100644 app/services/slack/messengers/article_fetched_feed.rb create mode 100644 app/services/slack/messengers/article_published.rb create mode 100644 app/services/slack/messengers/comment_user_warned.rb create mode 100644 app/services/slack/messengers/reaction_vomit.rb create mode 100644 spec/services/slack/messengers/article_fetched_feed_spec.rb create mode 100644 spec/services/slack/messengers/article_published_spec.rb create mode 100644 spec/services/slack/messengers/comment_user_warned_spec.rb create mode 100644 spec/services/slack/messengers/reaction_vomit_spec.rb diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 14f9158e9..617ef9649 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -182,14 +182,22 @@ module ApplicationHelper URL.url(uri) end - def tag_url(tag, page) - URL.tag(tag, page) - end - def article_url(article) URL.article(article) end + def comment_url(comment) + URL.comment(comment) + end + + def reaction_url(reaction) + URL.reaction(reaction) + end + + def tag_url(tag, page) + URL.tag(tag, page) + end + def user_url(user) URL.user(user) end diff --git a/app/lib/url.rb b/app/lib/url.rb index 188cd2fd0..b8aa00a53 100644 --- a/app/lib/url.rb +++ b/app/lib/url.rb @@ -30,7 +30,23 @@ module URL url(article.path) end - # Creates an article URL + # Creates a comment URL + # + # @param comment [Comment] the comment to create the URL for + def self.comment(comment) + url(comment.path) + end + + # Creates a reaction URL + # + # A reaction URL is the URL of its reactable. + # + # @param reactable [Reaction] the reaction to create the URL for + def self.reaction(reaction) + url(reaction.reactable.path) + end + + # Creates a tag URL # # @param tag [Tag] the tag to create the URL for def self.tag(tag, page = 1) diff --git a/app/models/article.rb b/app/models/article.rb index a00eef547..c03fc4e2b 100644 --- a/app/models/article.rb +++ b/app/models/article.rb @@ -72,7 +72,7 @@ class Article < ApplicationRecord before_save :update_cached_user after_save :bust_cache, :detect_human_language - after_save :notify_slack_channel_about_publication, if: -> { published && published_at > 30.seconds.ago } + after_save :notify_slack_channel_about_publication after_update_commit :update_notifications, if: proc { |article| article.notifications.any? && !article.saved_changes.empty? } after_update_commit :update_reading_list_reactions, if: proc { |article| @@ -718,18 +718,6 @@ class Article < ApplicationRecord end def notify_slack_channel_about_publication - url = "#{ApplicationConfig['APP_PROTOCOL']}#{ApplicationConfig['APP_DOMAIN']}" - - message = <<~MESSAGE.chomp - New Article Published: #{title} - #{url}#{path} - MESSAGE - - SlackBotPingWorker.perform_async( - message: message, - channel: "activity", - username: "article_bot", - icon_emoji: ":writing_hand:", - ) + Slack::Messengers::ArticlePublished.call(article: self) end end diff --git a/app/models/comment.rb b/app/models/comment.rb index 2bff7c912..04af345d2 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -27,7 +27,7 @@ class Comment < ApplicationRecord validates :commentable_type, inclusion: { in: %w[Article PodcastEpisode] } validates :user_id, presence: true - after_create :notify_slack_channel_about_warned_users, if: -> { user.warned } + after_create :notify_slack_channel_about_warned_users after_create :after_create_checks after_create_commit :record_field_test_event after_commit :calculate_score @@ -248,20 +248,6 @@ class Comment < ApplicationRecord end def notify_slack_channel_about_warned_users - url = "#{ApplicationConfig['APP_PROTOCOL']}#{ApplicationConfig['APP_DOMAIN']}" - - message = <<~MESSAGE.chomp - Activity: #{url}#{path} - Comment text: #{body_markdown.truncate(300)} - --- - Manage commenter - @#{user.username}: #{url}/internal/users/#{user.id} - MESSAGE - - SlackBotPingWorker.perform_async( - message: message, - channel: "warned-user-comments", - username: "sloan_watch_bot", - icon_emoji: ":sloan:", - ) + Slack::Messengers::CommentUserWarned.call(comment: self) end end diff --git a/app/models/reaction.rb b/app/models/reaction.rb index 122f27660..513a29e08 100644 --- a/app/models/reaction.rb +++ b/app/models/reaction.rb @@ -216,19 +216,6 @@ class Reaction < ApplicationRecord end def notify_slack_channel_about_vomit_reaction - url = "#{ApplicationConfig['APP_PROTOCOL']}#{ApplicationConfig['APP_DOMAIN']}" - - message = <<~MESSAGE.chomp - #{user.name} (#{url}#{user.path}) - reacted with a #{category} on - #{url}#{reactable.path} - MESSAGE - - SlackBotPingWorker.perform_async( - message: message, - channel: "abuse-reports", - username: "abuse_bot", - icon_emoji: ":cry:", - ) + Slack::Messengers::ReactionVomit.call(reaction: self) end end diff --git a/app/services/rss_reader.rb b/app/services/rss_reader.rb index 59796ddae..8c35aabb5 100644 --- a/app/services/rss_reader.rb +++ b/app/services/rss_reader.rb @@ -79,7 +79,7 @@ class RssReader organization_id: nil, ) - send_slack_notification(article) + Slack::Messengers::ArticleFetchedFeed.call(article: article) end def get_host_without_www(url) @@ -108,17 +108,6 @@ class RssReader relation.where(title: title).or(relation.where(feed_source_url: feed_source_url)).exists? end - def send_slack_notification(article) - return unless Rails.env.production? - - SlackBotPingWorker.perform_async( - message: "New Article Retrieved via RSS: #{article.title}\nhttps://dev.to#{article.path}", - channel: "activity", - username: "article_bot", - icon_emoji: ":robot_face:", - ) - end - def log_error(error_msg, metadata) Rails.logger.error(error_msg, metadata) end diff --git a/app/services/slack/messengers/article_fetched_feed.rb b/app/services/slack/messengers/article_fetched_feed.rb new file mode 100644 index 000000000..a2dc85dab --- /dev/null +++ b/app/services/slack/messengers/article_fetched_feed.rb @@ -0,0 +1,39 @@ +module Slack + module Messengers + class ArticleFetchedFeed + MESSAGE_TEMPLATE = <<~TEXT.chomp.freeze + New Article Retrieved via RSS: %s + %<url>s + TEXT + + def initialize(article:) + @article = article + end + + def self.call(*args) + new(*args).call + end + + def call + return unless article.published_from_feed? + + message = format( + MESSAGE_TEMPLATE, + title: article.title, + url: URL.article(article), + ) + + SlackBotPingWorker.perform_async( + message: message, + channel: "activity", + username: "article_bot", + icon_emoji: ":robot_face:", + ) + end + + private + + attr_reader :article + end + end +end diff --git a/app/services/slack/messengers/article_published.rb b/app/services/slack/messengers/article_published.rb new file mode 100644 index 000000000..dc97631b2 --- /dev/null +++ b/app/services/slack/messengers/article_published.rb @@ -0,0 +1,39 @@ +module Slack + module Messengers + class ArticlePublished + MESSAGE_TEMPLATE = <<~TEXT.chomp.freeze + New Article Published: %<title>s + %<url>s + TEXT + + def initialize(article:) + @article = article + end + + def self.call(*args) + new(*args).call + end + + def call + return unless article.published && article.published_at > 30.seconds.ago + + message = format( + MESSAGE_TEMPLATE, + title: article.title, + url: URL.article(article), + ) + + SlackBotPingWorker.perform_async( + message: message, + channel: "activity", + username: "article_bot", + icon_emoji: ":writing_hand:", + ) + end + + private + + attr_reader :article + end + end +end diff --git a/app/services/slack/messengers/comment_user_warned.rb b/app/services/slack/messengers/comment_user_warned.rb new file mode 100644 index 000000000..66b4faf00 --- /dev/null +++ b/app/services/slack/messengers/comment_user_warned.rb @@ -0,0 +1,48 @@ +module Slack + module Messengers + class CommentUserWarned + MESSAGE_TEMPLATE = <<~TEXT.chomp.freeze + Activity: %<url>s + Comment text: %<text>s + --- + Manage commenter - @%<username>s: %<internal_user_url>s + TEXT + + def initialize(comment:) + @comment = comment + @user = comment.user + end + + def self.call(*args) + new(*args).call + end + + def call + return unless user.warned + + internal_user_url = URL.url( + Rails.application.routes.url_helpers.internal_user_path(user), + ) + + message = format( + MESSAGE_TEMPLATE, + url: URL.comment(comment), + text: comment.body_markdown.truncate(300), + username: user.username, + internal_user_url: internal_user_url, + ) + + SlackBotPingWorker.perform_async( + message: message, + channel: "warned-user-comments", + username: "sloan_watch_bot", + icon_emoji: ":sloan:", + ) + end + + private + + attr_reader :comment, :user + end + end +end diff --git a/app/services/slack/messengers/reaction_vomit.rb b/app/services/slack/messengers/reaction_vomit.rb new file mode 100644 index 000000000..e77e68f4f --- /dev/null +++ b/app/services/slack/messengers/reaction_vomit.rb @@ -0,0 +1,43 @@ +module Slack + module Messengers + class ReactionVomit + MESSAGE_TEMPLATE = <<~TEXT.chomp.freeze + %<name>s (%<user_url>s) + reacted with a vomit on + %<reactable_url>s + TEXT + + def initialize(reaction:) + @reaction = reaction + end + + def self.call(*args) + new(*args).call + end + + def call + return unless reaction.category == "vomit" + + user = reaction.user + + message = format( + MESSAGE_TEMPLATE, + name: user.name, + user_url: URL.user(user), + reactable_url: URL.reaction(reaction), + ) + + SlackBotPingWorker.perform_async( + message: message, + channel: "abuse-reports", + username: "abuse_bot", + icon_emoji: ":cry:", + ) + end + + private + + attr_reader :reaction + end + end +end diff --git a/spec/lib/url_spec.rb b/spec/lib/url_spec.rb index b2007c871..6e5e75487 100644 --- a/spec/lib/url_spec.rb +++ b/spec/lib/url_spec.rb @@ -45,6 +45,28 @@ RSpec.describe URL, type: :lib do end end + describe ".comment" do + let(:comment) { build(:comment) } + + it "returns the correct URL for a comment" do + expect(described_class.comment(comment)).to eq("https://dev.to#{comment.path}") + end + end + + describe ".reaction" do + it "returns the correct URL for an article's reaction" do + article = build(:article, path: "/username1/slug") + reaction = build(:reaction, reactable: article) + expect(described_class.reaction(reaction)).to eq("https://dev.to#{article.path}") + end + + it "returns the correct URL for a comment's reaction" do + comment = build(:comment) + reaction = build(:reaction, reactable: comment) + expect(described_class.reaction(reaction)).to eq("https://dev.to#{comment.path}") + end + end + describe ".user" do let(:user) { build(:user) } diff --git a/spec/models/article_spec.rb b/spec/models/article_spec.rb index 375c6624c..4abf178d5 100644 --- a/spec/models/article_spec.rb +++ b/spec/models/article_spec.rb @@ -793,46 +793,33 @@ RSpec.describe Article, type: :model do end end - describe "slack notifications" do + describe "slack messages" do before do # making sure there are no other enqueued jobs from other tests sidekiq_perform_enqueued_jobs(only: SlackBotPingWorker) end - it "notifies the proper slack channel about a recently published new article" do - Timecop.freeze(Time.current) do - article = create(:article, published: true) - - url = "#{ApplicationConfig['APP_PROTOCOL']}#{ApplicationConfig['APP_DOMAIN']}" - message = "New Article Published: #{article.title}\n#{url}#{article.path}" - args = { - message: message, - channel: "activity", - username: "article_bot", - icon_emoji: ":writing_hand:" - }.stringify_keys - - sidekiq_assert_enqueued_jobs(1, only: SlackBotPingWorker) - job = sidekiq_enqueued_jobs(worker: SlackBotPingWorker).last - expect(job["args"]).to eq([args]) + it "queues a slack message to be sent" do + sidekiq_assert_enqueued_jobs(1, only: SlackBotPingWorker) do + article.update(published: true, published_at: Time.current) end end - it "does not send a notification for a new article published more than 30 seconds ago" do + it "does not queue a message for an article published more than 30 seconds ago" do Timecop.freeze(Time.current) do sidekiq_assert_no_enqueued_jobs(only: SlackBotPingWorker) do - create(:article, published: true, published_at: 31.seconds.ago) + article.update(published: true, published_at: 31.seconds.ago) end end end - it "does not send a notification for a non published article" do + it "does not queue a message for a draft article" do sidekiq_assert_no_enqueued_jobs(only: SlackBotPingWorker) do - create(:article, published: false) + article.update(body_markdown: "foobar", published: false) end end - it "sends a notification for a draft article that gets published" do + it "queues a message for a draft article that gets published" do Timecop.freeze(Time.current) do sidekiq_assert_enqueued_with(job: SlackBotPingWorker) do article.update_columns(published: false) diff --git a/spec/models/comment_spec.rb b/spec/models/comment_spec.rb index 00d59c9ab..f1c7fb4da 100644 --- a/spec/models/comment_spec.rb +++ b/spec/models/comment_spec.rb @@ -312,35 +312,20 @@ RSpec.describe Comment, type: :model do expect { comment.save }.to change(user, :last_comment_at) end - describe "slack notifications" do + describe "slack messages" do + let!(:user) { create(:user) } + before do # making sure there are no other enqueued jobs from other tests sidekiq_perform_enqueued_jobs(only: SlackBotPingWorker) end - it "notifies proper slack channel when a warned user leaves a comment" do - user = create(:user) + it "queues a slack message when a warned user leaves a comment" do user.add_role(:warned) - comment = create(:comment, user: user, commentable: article) - url = "#{ApplicationConfig['APP_PROTOCOL']}#{ApplicationConfig['APP_DOMAIN']}" - message = <<~MESSAGE.chomp - Activity: #{url}#{comment.path} - Comment text: #{comment.body_markdown.truncate(300)} - --- - Manage commenter - @#{user.username}: #{url}/internal/users/#{user.id} - MESSAGE - - args = { - message: message, - channel: "warned-user-comments", - username: "sloan_watch_bot", - icon_emoji: ":sloan:" - }.stringify_keys - - sidekiq_assert_enqueued_jobs(1, only: SlackBotPingWorker) - job = sidekiq_enqueued_jobs(worker: SlackBotPingWorker).last - expect(job["args"]).to eq([args]) + sidekiq_assert_enqueued_jobs(1, only: SlackBotPingWorker) do + create(:comment, user: user, commentable: article) + end end it "does not send notification if a regular user leaves a comment" do diff --git a/spec/models/reaction_spec.rb b/spec/models/reaction_spec.rb index edf7e3669..a25709391 100644 --- a/spec/models/reaction_spec.rb +++ b/spec/models/reaction_spec.rb @@ -181,7 +181,7 @@ RSpec.describe Reaction, type: :model do end context "when callbacks are called after create" do - describe "slack notifications" do + describe "slack messages" do let_it_be_changeable(:user) { create(:user, :trusted) } let_it_be_readonly(:article) { create(:article, user: user) } @@ -190,28 +190,19 @@ RSpec.describe Reaction, type: :model do sidekiq_perform_enqueued_jobs(only: SlackBotPingWorker) end - it "notifies proper slack channel about vomit reaction" do - url = "#{ApplicationConfig['APP_PROTOCOL']}#{ApplicationConfig['APP_DOMAIN']}" - message = "#{user.name} (#{url}#{user.path})\nreacted with a vomit on\n#{url}#{article.path}" - args = { - message: message, - channel: "abuse-reports", - username: "abuse_bot", - icon_emoji: ":cry:" - }.stringify_keys - - sidekiq_assert_enqueued_with(job: SlackBotPingWorker, args: [args]) do + it "queues a slack message to be sent for a vomit reaction" do + sidekiq_assert_enqueued_jobs(1, only: SlackBotPingWorker) do create(:reaction, reactable: article, user: user, category: "vomit") end end - it "does not send notification for like reaction" do + it "does not queue a message for a like reaction" do sidekiq_assert_no_enqueued_jobs(only: SlackBotPingWorker) do create(:reaction, reactable: article, user: user, category: "like") end end - it "does not send notification for thumbsdown reaction" do + it "does not queue a message for a thumbsdown reaction" do sidekiq_assert_no_enqueued_jobs(only: SlackBotPingWorker) do create(:reaction, reactable: article, user: user, category: "thumbsdown") end diff --git a/spec/services/rss_reader_spec.rb b/spec/services/rss_reader_spec.rb index e359298d4..86d403bdc 100644 --- a/spec/services/rss_reader_spec.rb +++ b/spec/services/rss_reader_spec.rb @@ -1,18 +1,14 @@ require "rails_helper" require "rss" -vcr_option = { - cassette_name: "rss_feeds", - allow_playback_repeats: "true" -} - default_logger = Rails.logger -RSpec.describe RssReader, type: :service, vcr: vcr_option do +RSpec.describe RssReader, type: :service, vcr: VCR_OPTIONS[:rss_feeds] do let(:link) { "https://medium.com/feed/@vaidehijoshi" } let(:nonmedium_link) { "https://circleci.com/blog/feed.xml" } let(:nonpermanent_link) { "https://medium.com/feed/@macsiri/" } let(:rss_data) { RSS::Parser.parse(HTTParty.get(link).body, false) } + let!(:rss_reader) { described_class.new } # Override the default Rails logger as these tests require the Timber logger. before do @@ -30,66 +26,91 @@ RSpec.describe RssReader, type: :service, vcr: vcr_option do end it "fetch only articles from an feed_url" do - described_class.get_all_articles + rss_reader.get_all_articles + # the result within the approval file depends on the feed # not fetching comments is baked into this verify(format: :txt) { Article.count } end it "does not re-create article if it already exist" do - described_class.new.get_all_articles - expect { described_class.new.get_all_articles }.not_to change(Article, :count) + rss_reader.get_all_articles + + expect { rss_reader.get_all_articles }.not_to change(Article, :count) end it "parses correctly" do - described_class.new.get_all_articles + rss_reader.get_all_articles + verify format: :txt do User.find_by(feed_url: nonpermanent_link).articles.first.body_markdown end end - it "sets time current" do - described_class.new.get_all_articles - expect(User.find_by(feed_url: nonpermanent_link).feed_fetched_at).to be > 2.minutes.ago + it "sets feed_fetched_at to the current time" do + Timecop.freeze(Time.current) do + rss_reader.get_all_articles + + user = User.find_by(feed_url: nonpermanent_link) + feed_fetched_at = user.feed_fetched_at + expect(feed_fetched_at.to_i).to eq(Time.current.to_i) + end end it "does refetch same user over and over by default" do user = User.find_by(feed_url: nonpermanent_link) + Timecop.freeze(Time.current) do - user.update_column(:feed_fetched_at, Time.current) + user.update_columns(feed_fetched_at: Time.current) + fetched_at_time = user.reload.feed_fetched_at + # travel a few seconds in the future to simulate a new time 3.times do |i| - Timecop.travel((i + 5).seconds.from_now) { described_class.new.get_all_articles } + Timecop.travel((i + 5).seconds.from_now) do + rss_reader.get_all_articles + end end + expect(user.reload.feed_fetched_at > fetched_at_time).to be(true) end end it "logs an article creation error" do - reader = described_class.new - allow(reader).to receive(:make_from_rss_item).and_raise(StandardError) + allow(rss_reader).to receive(:make_from_rss_item).and_raise(StandardError) allow(Rails.logger).to receive(:error) - reader.get_all_articles + + rss_reader.get_all_articles + expect(Rails.logger).to have_received(:error).at_least(:once) end it "logs a fetching error" do - reader = described_class.new - allow(reader).to receive(:fetch_rss).and_raise(StandardError) + allow(rss_reader).to receive(:fetch_rss).and_raise(StandardError) allow(Rails.logger).to receive(:error) - reader.get_all_articles + + rss_reader.get_all_articles + expect(Rails.logger).to have_received(:error).at_least(:once) end + + it "queues as many slack messages as there are articles" do + expect do + rss_reader.get_all_articles + end.to change(SlackBotPingWorker.jobs, :count).by(12) + end end context "when feed_referential_link is false" do it "does not self-reference links for user" do # Article.find_by is used by find_and_replace_possible_links! - # checking it's invocation s a shortcut to testing the functionality. + # checking its invocation is a shortcut to testing the functionality. allow(Article).to receive(:find_by).and_call_original + create(:user, feed_url: nonpermanent_link, feed_referential_link: false) - described_class.get_all_articles + + rss_reader.get_all_articles + expect(Article).not_to have_received(:find_by) end end @@ -102,41 +123,52 @@ RSpec.describe RssReader, type: :service, vcr: vcr_option do end it "gets articles for user" do + rss_reader.fetch_user(User.find_by(feed_url: link)) + # the result within the approval file depends on the feed - described_class.new.fetch_user(User.first) verify(format: :txt) { Article.count } end it "does not set featured_number" do - described_class.new.fetch_user(User.first) - expect(Article.all.map(&:featured_number).uniq).to eq([nil]) + user = User.find_by(feed_url: link) + rss_reader.fetch_user(user) + + expect(user.articles.select(&:featured_number)).to be_empty end it "logs an article creation error on the standard logger" do - reader = described_class.new - allow(reader).to receive(:make_from_rss_item).and_raise(StandardError) + allow(rss_reader).to receive(:make_from_rss_item).and_raise(StandardError) allow(Rails.logger).to receive(:error) - reader.fetch_user(User.first) + + rss_reader.fetch_user(User.find_by(feed_url: link)) + expect(Rails.logger).to have_received(:error).at_least(:once) end it "logs a fetching error on the standard logger" do - reader = described_class.new - allow(reader).to receive(:fetch_rss).and_raise(StandardError) + allow(rss_reader).to receive(:fetch_rss).and_raise(StandardError) allow(Rails.logger).to receive(:error) - reader.fetch_user(User.first) + + rss_reader.fetch_user(User.find_by(feed_url: link)) + expect(Rails.logger).to have_received(:error).at_least(:once) end + + it "queues as many slack messages as there are user articles" do + expect do + rss_reader.fetch_user(User.find_by(feed_url: link)) + end.to change(SlackBotPingWorker.jobs, :count).by(1) + end end describe "#valid_feed_url?" do it "returns true on valid feed url" do - expect(described_class.new.valid_feed_url?(link)).to be true + expect(rss_reader.valid_feed_url?(link)).to be(true) end it "returns false on invalid feed url" do bad_link = "www.google.com" - expect(described_class.new.valid_feed_url?(bad_link)).to be false + expect(rss_reader.valid_feed_url?(bad_link)).to be(false) end end end diff --git a/spec/services/slack/messengers/article_fetched_feed_spec.rb b/spec/services/slack/messengers/article_fetched_feed_spec.rb new file mode 100644 index 000000000..758b5365d --- /dev/null +++ b/spec/services/slack/messengers/article_fetched_feed_spec.rb @@ -0,0 +1,45 @@ +require "rails_helper" + +RSpec.describe Slack::Messengers::ArticleFetchedFeed, type: :service do + let!(:article) do + build(:article).tap do |article| + article.title = "Awesome article" + article.published_from_feed = true + end + end + + let(:default_params) { { article: article } } + + it "does not message slack for a new article not coming from the feed" do + sidekiq_assert_no_enqueued_jobs(only: SlackBotPingWorker) do + article.published_from_feed = false + + described_class.call(article: article) + end + end + + it "contains the correct info", :aggregate_failures do + sidekiq_assert_enqueued_jobs(1, only: SlackBotPingWorker) do + described_class.call(default_params) + end + + job = sidekiq_enqueued_jobs(worker: SlackBotPingWorker).last + message = job["args"].first["message"] + + expect(message).to include(article.title) + expect(message).to include(URL.article(article)) + end + + it "messages the proper channel with the proper username and emoji", :aggregate_failures do + sidekiq_assert_enqueued_jobs(1, only: SlackBotPingWorker) do + described_class.call(default_params) + end + + job = sidekiq_enqueued_jobs(worker: SlackBotPingWorker).last + job_args = job["args"].first + + expect(job_args["channel"]).to eq("activity") + expect(job_args["username"]).to eq("article_bot") + expect(job_args["icon_emoji"]).to eq(":robot_face:") + end +end diff --git a/spec/services/slack/messengers/article_published_spec.rb b/spec/services/slack/messengers/article_published_spec.rb new file mode 100644 index 000000000..6c74f5c74 --- /dev/null +++ b/spec/services/slack/messengers/article_published_spec.rb @@ -0,0 +1,55 @@ +require "rails_helper" + +RSpec.describe Slack::Messengers::ArticlePublished, type: :service do + let(:article) do + build(:article).tap do |article| + article.title = "Awesome article" + article.published = true + article.published_at = Time.current + end + end + + let(:default_params) { { article: article } } + + it "does not message slack for a draft article" do + sidekiq_assert_no_enqueued_jobs(only: SlackBotPingWorker) do + article = build(:article, published: false) + described_class.call(article: article) + end + end + + it "does not message slack for an article that was published long ago" do + sidekiq_assert_no_enqueued_jobs(only: SlackBotPingWorker) do + article = build(:article).tap do |art| + art.published = true + art.published_at = 1.minute.ago + end + described_class.call(article: article) + end + end + + it "contains the correct info", :aggregate_failures do + sidekiq_assert_enqueued_jobs(1, only: SlackBotPingWorker) do + described_class.call(default_params) + end + + job = sidekiq_enqueued_jobs(worker: SlackBotPingWorker).last + message = job["args"].first["message"] + + expect(message).to include(article.title) + expect(message).to include(URL.article(article)) + end + + it "messages the proper channel with the proper username and emoji", :aggregate_failures do + sidekiq_assert_enqueued_jobs(1, only: SlackBotPingWorker) do + described_class.call(default_params) + end + + job = sidekiq_enqueued_jobs(worker: SlackBotPingWorker).last + job_args = job["args"].first + + expect(job_args["channel"]).to eq("activity") + expect(job_args["username"]).to eq("article_bot") + expect(job_args["icon_emoji"]).to eq(":writing_hand:") + end +end diff --git a/spec/services/slack/messengers/comment_user_warned_spec.rb b/spec/services/slack/messengers/comment_user_warned_spec.rb new file mode 100644 index 000000000..94e120fff --- /dev/null +++ b/spec/services/slack/messengers/comment_user_warned_spec.rb @@ -0,0 +1,51 @@ +require "rails_helper" + +RSpec.describe Slack::Messengers::CommentUserWarned, type: :service do + let(:comment) { create(:comment) } + let(:user) { comment.user } + + let(:default_params) { { comment: comment } } + + it "does not message slack for a comment with a regular user" do + sidekiq_assert_no_enqueued_jobs(only: SlackBotPingWorker) do + described_class.call(comment: build(:comment, user: build(:user))) + end + end + + context "when the uesr has been warned" do + before do + user.add_role(:warned) + end + + it "contains the correct info", :aggregate_failures do + sidekiq_assert_enqueued_jobs(1, only: SlackBotPingWorker) do + described_class.call(default_params) + end + + job = sidekiq_enqueued_jobs(worker: SlackBotPingWorker).last + message = job["args"].first["message"] + + internal_user_url = URL.url( + Rails.application.routes.url_helpers.internal_user_path(user), + ) + + expect(message).to include(URL.comment(comment)) + expect(message).to include(comment.body_markdown.truncate(300)) + expect(message).to include(user.username) + expect(message).to include(internal_user_url) + end + + it "messages the proper channel with the proper username and emoji", :aggregate_failures do + sidekiq_assert_enqueued_jobs(1, only: SlackBotPingWorker) do + described_class.call(default_params) + end + + job = sidekiq_enqueued_jobs(worker: SlackBotPingWorker).last + job_args = job["args"].first + + expect(job_args["channel"]).to eq("warned-user-comments") + expect(job_args["username"]).to eq("sloan_watch_bot") + expect(job_args["icon_emoji"]).to eq(":sloan:") + end + end +end diff --git a/spec/services/slack/messengers/feedback_spec.rb b/spec/services/slack/messengers/feedback_spec.rb index 2f28fd981..377f1b3e5 100644 --- a/spec/services/slack/messengers/feedback_spec.rb +++ b/spec/services/slack/messengers/feedback_spec.rb @@ -1,7 +1,7 @@ require "rails_helper" RSpec.describe Slack::Messengers::Feedback, type: :service do - let_it_be_readonly(:user) { create(:user) } + let(:user) { build(:user) } let(:default_params) do { type: "abuse-reports", diff --git a/spec/services/slack/messengers/potential_spammer_spec.rb b/spec/services/slack/messengers/potential_spammer_spec.rb index a90556542..c70f4f677 100644 --- a/spec/services/slack/messengers/potential_spammer_spec.rb +++ b/spec/services/slack/messengers/potential_spammer_spec.rb @@ -1,7 +1,7 @@ require "rails_helper" RSpec.describe Slack::Messengers::PotentialSpammer, type: :service do - let_it_be_readonly(:user) { create(:user) } + let(:user) { build(:user) } let(:default_params) { { user: user } } diff --git a/spec/services/slack/messengers/rate_limit_spec.rb b/spec/services/slack/messengers/rate_limit_spec.rb index 426177a52..7496220c6 100644 --- a/spec/services/slack/messengers/rate_limit_spec.rb +++ b/spec/services/slack/messengers/rate_limit_spec.rb @@ -1,7 +1,7 @@ require "rails_helper" RSpec.describe Slack::Messengers::RateLimit, type: :service do - let_it_be_readonly(:user) { create(:user) } + let(:user) { build(:user) } let(:default_params) do { diff --git a/spec/services/slack/messengers/reaction_vomit_spec.rb b/spec/services/slack/messengers/reaction_vomit_spec.rb new file mode 100644 index 000000000..63f8a932c --- /dev/null +++ b/spec/services/slack/messengers/reaction_vomit_spec.rb @@ -0,0 +1,42 @@ +require "rails_helper" + +RSpec.describe Slack::Messengers::ReactionVomit, type: :service do + let(:reaction) { build(:reaction, category: :vomit, user: build(:user)) } + let(:user) { reaction.user } + + let(:default_params) { { reaction: reaction } } + + it "does not message slack for a like reaction" do + sidekiq_assert_no_enqueued_jobs(only: SlackBotPingWorker) do + reaction = build(:reaction, category: :like) + described_class.call(reaction: reaction) + end + end + + it "contains the correct info", :aggregate_failures do + sidekiq_assert_enqueued_jobs(1, only: SlackBotPingWorker) do + described_class.call(default_params) + end + + job = sidekiq_enqueued_jobs(worker: SlackBotPingWorker).last + message = job["args"].first["message"] + + expect(message).to include(user.name) + expect(message).to include(URL.user(user)) + expect(message).to include(reaction.category) + expect(message).to include(URL.reaction(reaction)) + end + + it "messages the proper channel with the proper username and emoji", :aggregate_failures do + sidekiq_assert_enqueued_jobs(1, only: SlackBotPingWorker) do + described_class.call(default_params) + end + + job = sidekiq_enqueued_jobs(worker: SlackBotPingWorker).last + job_args = job["args"].first + + expect(job_args["channel"]).to eq("abuse-reports") + expect(job_args["username"]).to eq("abuse_bot") + expect(job_args["icon_emoji"]).to eq(":cry:") + end +end diff --git a/spec/services/slack/messengers/sponsorship_spec.rb b/spec/services/slack/messengers/sponsorship_spec.rb index 4836d341e..46068ccda 100644 --- a/spec/services/slack/messengers/sponsorship_spec.rb +++ b/spec/services/slack/messengers/sponsorship_spec.rb @@ -1,9 +1,9 @@ require "rails_helper" RSpec.describe Slack::Messengers::Sponsorship, type: :service do - let_it_be_readonly(:user) { create(:user) } - let_it_be_readonly(:organization) { create(:organization) } - let(:tag) { create(:tag) } + let(:user) { build(:user) } + let(:organization) { build(:organization) } + let(:tag) { build(:tag) } let(:default_params) do { diff --git a/spec/support/initializers/vcr.rb b/spec/support/initializers/vcr.rb index c59b013eb..4e0f63459 100644 --- a/spec/support/initializers/vcr.rb +++ b/spec/support/initializers/vcr.rb @@ -29,5 +29,9 @@ VCR_OPTIONS = { twitter_fetch_status: { cassette_name: "twitter_fetch_status", allow_playback_repeats: true + }, + rss_feeds: { + cassette_name: "rss_feeds", + allow_playback_repeats: true } }.freeze