From 63cf95d8110ce62062301cd507885db2f08aab17 Mon Sep 17 00:00:00 2001 From: Michael Kohl Date: Wed, 23 Dec 2020 21:55:06 +0700 Subject: [PATCH] Split BadgeRewarder into multiple services (#11902) --- app/labor/badge_rewarder.rb | 151 ----------- app/models/badge.rb | 4 + app/services/badges/award.rb | 15 ++ app/services/badges/award_beloved_comment.rb | 38 +++ app/services/badges/award_contributor.rb | 13 + .../badges/award_contributor_from_github.rb | 27 ++ .../badges/award_eight_week_streak.rb | 7 + app/services/badges/award_fab_five.rb | 14 + app/services/badges/award_four_week_streak.rb | 7 + .../badges/award_sixteen_week_streak.rb | 7 + app/services/badges/award_streak.rb | 61 +++++ app/services/badges/award_tag.rb | 45 ++++ app/services/badges/award_top_seven.rb | 14 + app/services/badges/award_yearly_club.rb | 38 +++ .../badge_achievements/badge_award_worker.rb | 10 +- config/schedule.yml | 14 +- lib/tasks/badges.rake | 6 +- spec/labor/badge_rewarder_spec.rb | 249 ------------------ spec/models/badge_spec.rb | 12 + .../badges/award_beloved_comment_spec.rb | 24 ++ .../award_contributor_from_github_spec.rb | 36 +++ .../services/badges/award_contributor_spec.rb | 13 + .../badges/award_eight_week_streak_spec.rb | 9 + spec/services/badges/award_fab_five_spec.rb | 13 + .../badges/award_four_week_streak_spec.rb | 9 + .../badges/award_sixteen_week_streak_spec.rb | 9 + spec/services/badges/award_spec.rb | 22 ++ spec/services/badges/award_streak_spec.rb | 25 ++ spec/services/badges/award_tag_spec.rb | 64 +++++ spec/services/badges/award_top_seven_spec.rb | 13 + .../services/badges/award_yearly_club_spec.rb | 51 ++++ .../badge_award_worker_spec.rb | 17 +- 32 files changed, 616 insertions(+), 421 deletions(-) delete mode 100644 app/labor/badge_rewarder.rb create mode 100644 app/services/badges/award.rb create mode 100644 app/services/badges/award_beloved_comment.rb create mode 100644 app/services/badges/award_contributor.rb create mode 100644 app/services/badges/award_contributor_from_github.rb create mode 100644 app/services/badges/award_eight_week_streak.rb create mode 100644 app/services/badges/award_fab_five.rb create mode 100644 app/services/badges/award_four_week_streak.rb create mode 100644 app/services/badges/award_sixteen_week_streak.rb create mode 100644 app/services/badges/award_streak.rb create mode 100644 app/services/badges/award_tag.rb create mode 100644 app/services/badges/award_top_seven.rb create mode 100644 app/services/badges/award_yearly_club.rb delete mode 100644 spec/labor/badge_rewarder_spec.rb create mode 100644 spec/services/badges/award_beloved_comment_spec.rb create mode 100644 spec/services/badges/award_contributor_from_github_spec.rb create mode 100644 spec/services/badges/award_contributor_spec.rb create mode 100644 spec/services/badges/award_eight_week_streak_spec.rb create mode 100644 spec/services/badges/award_fab_five_spec.rb create mode 100644 spec/services/badges/award_four_week_streak_spec.rb create mode 100644 spec/services/badges/award_sixteen_week_streak_spec.rb create mode 100644 spec/services/badges/award_spec.rb create mode 100644 spec/services/badges/award_streak_spec.rb create mode 100644 spec/services/badges/award_tag_spec.rb create mode 100644 spec/services/badges/award_top_seven_spec.rb create mode 100644 spec/services/badges/award_yearly_club_spec.rb diff --git a/app/labor/badge_rewarder.rb b/app/labor/badge_rewarder.rb deleted file mode 100644 index 00e39caba..000000000 --- a/app/labor/badge_rewarder.rb +++ /dev/null @@ -1,151 +0,0 @@ -module BadgeRewarder - YEARS = { 1 => "one", 2 => "two", 3 => "three", 4 => "four", 5 => "five", 6 => "six", 7 => "seven" }.freeze - REPOSITORIES = [ - "forem/forem", - "forem/forem-browser-extension", - "thepracticaldev/DEV-Android", - "thepracticaldev/DEV-ios", - ].freeze - - LONGEST_STREAK_WEEKS = 16 - LONGEST_STREAK_MESSAGE = "16 weeks! You've achieved the longest writing " \ - "streak possible. This makes you eligible for special quests in the future. Keep up the amazing contributions to" \ - " our community!".freeze - - MINIMUM_QUALITY = -25 - - def self.award_yearly_club_badges - total_years = Time.current.year - SiteConfig.community_copyright_start_year.to_i - (1..total_years).each do |i| - message = "Happy #{SiteConfig.community_name} birthday! " \ - "Can you believe it's been #{i} #{'year'.pluralize(i)} already?!" - badge = Badge.find_by(slug: "#{YEARS[i]}-year-club") - next unless badge - - User.registered.where("created_at < ? AND created_at > ?", i.year.ago, i.year.ago - 2.days).find_each do |user| - achievement = BadgeAchievement.create( - user_id: user.id, - badge_id: badge.id, - rewarding_context_message_markdown: message, - ) - user.save if achievement.valid? - end - end - end - - def self.award_beloved_comment_badges(comment_count = 24) - badge_id = Badge.find_by(slug: "beloved-comment")&.id - return unless badge_id - - Comment.includes(:user).where("public_reactions_count > ?", comment_count).find_each do |comment| - message = "You're famous! [This is the comment](#{URL.comment(comment)}) for which you are being recognized. 😄" - achievement = BadgeAchievement.create( - user_id: comment.user_id, - badge_id: badge_id, - rewarding_context_message_markdown: message, - ) - comment.user.save if achievement.valid? - end - end - - def self.award_top_seven_badges(usernames, message_markdown = "Congrats!!!") - award_badges(usernames, "top-7", message_markdown) - end - - def self.award_fab_five_badges(usernames, message_markdown = "Congrats!!!") - award_badges(usernames, "fab-5", message_markdown) - end - - def self.award_contributor_badges(usernames, message_markdown = "Thank you so much for your contributions!") - award_badges(usernames, "dev-contributor", message_markdown) - end - - def self.award_tag_badges - Tag.where.not(badge_id: nil).find_each do |tag| - past_winner_user_ids = BadgeAchievement.where(badge_id: tag.badge_id).pluck(:user_id) - winning_article = Article.where("score > 100") - .published - .where.not(user_id: past_winner_user_ids) - .order(score: :desc) - .where("published_at > ?", 7.5.days.ago) # More than seven days, to have some wiggle room. - .cached_tagged_with(tag).first - if winning_article - award_badges( - [winning_article.user.username], - tag.badge.slug, - "Congratulations on posting the most beloved [##{tag.name}](#{URL.tag(tag)}) post " \ - "from the past seven days! " \ - "Your winning post was [#{winning_article.title}](#{URL.article(winning_article)}). " \ - "(You can only win once per badge-eligible tag)", - ) - end - end - end - - def self.award_contributor_badges_from_github(since = 1.day.ago, msg = "Thank you so much for your contributions!") - badge = Badge.find_by(slug: "dev-contributor") - return unless badge - - REPOSITORIES.each do |repo| - commits = Github::OauthClient.new.commits(repo, since: since.utc.iso8601) - - authors_uids = commits.map { |commit| commit.author.id } - Identity.github.where(uid: authors_uids).find_each do |i| - BadgeAchievement.where(user_id: i.user_id, badge_id: badge.id).first_or_create( - rewarding_context_message_markdown: msg, - ) - end - end - end - - def self.award_four_week_streak_badge - award_streak_badge(4) - end - - def self.award_eight_week_streak_badge - award_streak_badge(8) - end - - def self.award_sixteen_week_streak_badge - award_streak_badge(16) - end - - def self.award_streak_badge(num_weeks) - # No credit for super low quality - article_user_ids = Article.published - .where("published_at > ? AND score > ?", 1.week.ago, MINIMUM_QUALITY) - .pluck(:user_id) - message = if num_weeks == LONGEST_STREAK_WEEKS - LONGEST_STREAK_MESSAGE - else - "Congrats on achieving this streak! Consistent writing is hard. " \ - "The next streak badge you can get is the #{num_weeks * 2} Week Badge. 😉" - end - users = User.where(id: article_user_ids).where("articles_count >= ?", num_weeks) - usernames = [] - users.find_each do |user| - count = 0 - num_weeks.times do |i| - num = i + 1 - count += 1 if user.articles.published.where("published_at > ? AND published_at < ?", num.weeks.ago, - (num - 1).weeks.ago).any? - end - usernames << user.username if count >= num_weeks - end - award_badges(usernames, "#{num_weeks}-week-streak", message) - end - - def self.award_badges(usernames, slug, message_markdown) - badge_id = Badge.find_by(slug: slug)&.id - return unless badge_id - - User.registered.where(username: usernames).find_each do |user| - BadgeAchievement.create( - user_id: user.id, - badge_id: badge_id, - rewarding_context_message_markdown: message_markdown, - ) - user.save - end - end -end diff --git a/app/models/badge.rb b/app/models/badge.rb index ff10d5fdc..2b6d79669 100644 --- a/app/models/badge.rb +++ b/app/models/badge.rb @@ -18,6 +18,10 @@ class Badge < ApplicationRecord "/badge/#{slug}" end + def self.id_for_slug(slug) + select(:id).find_by(slug: slug)&.id + end + private def generate_slug diff --git a/app/services/badges/award.rb b/app/services/badges/award.rb new file mode 100644 index 000000000..8a9893820 --- /dev/null +++ b/app/services/badges/award.rb @@ -0,0 +1,15 @@ +module Badges + class Award + def self.call(user_relation, slug, message_markdown) + return unless (badge_id = Badge.id_for_slug(slug)) + + user_relation.find_each do |user| + achievement = user.badge_achievements.create( + badge_id: badge_id, + rewarding_context_message_markdown: message_markdown, + ) + user.touch if achievement.persisted? + end + end + end +end diff --git a/app/services/badges/award_beloved_comment.rb b/app/services/badges/award_beloved_comment.rb new file mode 100644 index 000000000..a0875bbfa --- /dev/null +++ b/app/services/badges/award_beloved_comment.rb @@ -0,0 +1,38 @@ +module Badges + class AwardBelovedComment + BADGE_SLUG = "beloved-comment".freeze + + MESSAGE_TEMPLATE = + "You're famous! " \ + "[This is the comment](%s) for which you're being recognized. 😄".freeze + + def self.call(comment_count = 25) + new(comment_count).call + end + + def initialize(comment_count) + @comment_count = comment_count + end + + def call + return unless (badge_id = Badge.id_for_slug(BADGE_SLUG)) + + Comment.includes(:user).where(public_reactions_count: comment_count..).find_each do |comment| + achievement = BadgeAchievement.create( + user_id: comment.user_id, + badge_id: badge_id, + rewarding_context_message_markdown: generate_message(comment), + ) + comment.user.touch if achievement.valid? + end + end + + private + + attr_reader :comment_count + + def generate_message(comment) + format(MESSAGE_TEMPLATE, comment: URL.comment(comment)) + end + end +end diff --git a/app/services/badges/award_contributor.rb b/app/services/badges/award_contributor.rb new file mode 100644 index 000000000..96c9d1a54 --- /dev/null +++ b/app/services/badges/award_contributor.rb @@ -0,0 +1,13 @@ +module Badges + class AwardContributor + BADGE_SLUG = "dev-contributor".freeze + + def self.call(usernames, message_markdown = "Thank you so much for your contributions!") + ::Badges::Award.call( + User.where(username: usernames), + BADGE_SLUG, + message_markdown, + ) + end + end +end diff --git a/app/services/badges/award_contributor_from_github.rb b/app/services/badges/award_contributor_from_github.rb new file mode 100644 index 000000000..32344fb09 --- /dev/null +++ b/app/services/badges/award_contributor_from_github.rb @@ -0,0 +1,27 @@ +module Badges + class AwardContributorFromGithub + BADGE_SLUG = "dev-contributor".freeze + + REPOSITORIES = [ + "forem/forem", + "forem/forem-browser-extension", + "forem/DEV-Android", + "forem/DEV-ios", + ].freeze + + def self.call(since = 1.day.ago, msg = "Thank you so much for your contributions!") + return unless (badge_id = Badge.id_for_slug(BADGE_SLUG)) + + REPOSITORIES.each do |repo| + commits = Github::OauthClient.new.commits(repo, since: since.utc.iso8601) + + authors_uids = commits.map { |commit| commit.author.id } + Identity.github.where(uid: authors_uids).find_each do |i| + BadgeAchievement + .where(user_id: i.user_id, badge_id: badge_id) + .first_or_create(rewarding_context_message_markdown: msg) + end + end + end + end +end diff --git a/app/services/badges/award_eight_week_streak.rb b/app/services/badges/award_eight_week_streak.rb new file mode 100644 index 000000000..0610678bc --- /dev/null +++ b/app/services/badges/award_eight_week_streak.rb @@ -0,0 +1,7 @@ +module Badges + class AwardEightWeekStreak + def self.call + ::Badges::AwardStreak.call(weeks: 8) + end + end +end diff --git a/app/services/badges/award_fab_five.rb b/app/services/badges/award_fab_five.rb new file mode 100644 index 000000000..88eb7737c --- /dev/null +++ b/app/services/badges/award_fab_five.rb @@ -0,0 +1,14 @@ +module Badges + class AwardFabFive + BADGE_SLUG = "fab-5".freeze + DEFAULT_MESSAGE = "Congrats!!!".freeze + + def self.call(usernames, message_markdown = DEFAULT_MESSAGE) + ::Badges::Award.call( + User.where(username: usernames), + BADGE_SLUG, + message_markdown, + ) + end + end +end diff --git a/app/services/badges/award_four_week_streak.rb b/app/services/badges/award_four_week_streak.rb new file mode 100644 index 000000000..7b174cccb --- /dev/null +++ b/app/services/badges/award_four_week_streak.rb @@ -0,0 +1,7 @@ +module Badges + class AwardFourWeekStreak + def self.call + ::Badges::AwardStreak.call(weeks: 4) + end + end +end diff --git a/app/services/badges/award_sixteen_week_streak.rb b/app/services/badges/award_sixteen_week_streak.rb new file mode 100644 index 000000000..ca0add15f --- /dev/null +++ b/app/services/badges/award_sixteen_week_streak.rb @@ -0,0 +1,7 @@ +module Badges + class AwardSixteenWeekStreak + def self.call + ::Badges::AwardStreak.call(weeks: 16) + end + end +end diff --git a/app/services/badges/award_streak.rb b/app/services/badges/award_streak.rb new file mode 100644 index 000000000..0ea98e4ef --- /dev/null +++ b/app/services/badges/award_streak.rb @@ -0,0 +1,61 @@ +module Badges + class AwardStreak + LONGEST_STREAK_WEEKS = 16 + LONGEST_STREAK_MESSAGE = "16 weeks! You've achieved the longest writing " \ + "streak possible. This makes you eligible for special quests in the future. " \ + "Keep up the amazing contributions to our community!".freeze + + MINIMUM_QUALITY = -25 + + MESSAGE_TEMPLATE = + "Congrats on achieving this streak! Consistent writing is hard. " \ + "The next streak badge you can get is the %d Week Badge. 😉".freeze + + def self.call(weeks:) + new(weeks: weeks).call + end + + def initialize(weeks:) + @weeks = weeks + end + + def call + badge_slug = "#{weeks}-week-streak" + return unless (badge_id = Badge.id_for_slug(badge_slug)) + + users = User.where(id: article_user_ids).where("articles_count >= ?", weeks) + + users.find_each do |user| + count = weeks.times.count { |i| published_x_weeks_ago?(user, i + 1) } + next unless count >= weeks + + user.badge_achievements.create( + badge_id: badge_id, + rewarding_context_message_markdown: generate_message, + ) + end + end + + private + + attr_reader :weeks + + # No credit for super low quality + def article_user_ids + Article.published + .where("published_at > ? AND score > ?", 1.week.ago, MINIMUM_QUALITY) + .pluck(:user_id) + end + + def published_x_weeks_ago?(user, num) + user.articles.published + .where("published_at > ? AND published_at < ?", num.weeks.ago, (num - 1).weeks.ago).any? + end + + def generate_message + return LONGEST_STREAK_MESSAGE if weeks == LONGEST_STREAK_WEEKS + + format(MESSAGE_TEMPLATE, weeks: weeks * 2) + end + end +end diff --git a/app/services/badges/award_tag.rb b/app/services/badges/award_tag.rb new file mode 100644 index 000000000..60e106d26 --- /dev/null +++ b/app/services/badges/award_tag.rb @@ -0,0 +1,45 @@ +module Badges + class AwardTag + MESSAGE_TEMPLATE = + "Congratulations on posting the most beloved [#%s](%s) post " \ + "from the past seven days! " \ + "Your winning post was [%s](%s). " \ + "(You can only win once per badge-eligible tag)".freeze + + def self.call + new.call + end + + def call + Tag.where.not(badge_id: nil).find_each do |tag| + past_winner_user_ids = BadgeAchievement.where(badge_id: tag.badge_id).pluck(:user_id) + winning_article = Article.where("score > 100") + .published + .where.not(user_id: past_winner_user_ids) + .order(score: :desc) + .where("published_at > ?", 7.5.days.ago) # More than seven days, to have some wiggle room. + .cached_tagged_with(tag).first + next unless winning_article + + user = winning_article.user + achievement = user.badge_achievements.create( + badge_id: tag.badge_id, + rewarding_context_message_markdown: generate_message(tag, winning_article), + ) + user.touch if achievement.persisted? + end + end + + private + + def generate_message(tag, winning_article) + format( + MESSAGE_TEMPLATE, + tag_name: tag.name, + tag_url: URL.tag(tag), + article_title: winning_article.title, + article_url: URL.article(winning_article), + ) + end + end +end diff --git a/app/services/badges/award_top_seven.rb b/app/services/badges/award_top_seven.rb new file mode 100644 index 000000000..4eec8baa7 --- /dev/null +++ b/app/services/badges/award_top_seven.rb @@ -0,0 +1,14 @@ +module Badges + class AwardTopSeven + BADGE_SLUG = "top-7".freeze + DEFAULT_MESSAGE = "Congrats!!!".freeze + + def self.call(usernames, message_markdown = DEFAULT_MESSAGE) + ::Badges::Award.call( + User.where(username: usernames), + BADGE_SLUG, + message_markdown, + ) + end + end +end diff --git a/app/services/badges/award_yearly_club.rb b/app/services/badges/award_yearly_club.rb new file mode 100644 index 000000000..caa394b33 --- /dev/null +++ b/app/services/badges/award_yearly_club.rb @@ -0,0 +1,38 @@ +module Badges + class AwardYearlyClub + YEARS = { + 1 => "one", + 2 => "two", + 3 => "three", + 4 => "four", + 5 => "five", + 6 => "six", + 7 => "seven" + }.freeze + + MESSAGE_TEMPLATE = + "Happy #{SiteConfig.community_name} birthday! " \ + "Can you believe it's been %d %s already?!".freeze + + def self.call + new.call + end + + def call + total_years = Time.current.year - SiteConfig.community_copyright_start_year.to_i + (1..total_years).each do |i| + ::Badges::Award.call( + User.registered.where(created_at: i.year.ago - 2.days...i.year.ago), + "#{YEARS[i]}-year-club", + generate_message(i), + ) + end + end + + private + + def generate_message(years) + format(MESSAGE_TEMPLATE, years: years, noun: "year".pluralize(years)) + end + end +end diff --git a/app/workers/badge_achievements/badge_award_worker.rb b/app/workers/badge_achievements/badge_award_worker.rb index a4993abc4..9680411b5 100644 --- a/app/workers/badge_achievements/badge_award_worker.rb +++ b/app/workers/badge_achievements/badge_award_worker.rb @@ -5,10 +5,14 @@ module BadgeAchievements sidekiq_options queue: :high_priority, retry: 10 def perform(usernames, badge_slug, message) - if BadgeRewarder.respond_to?(badge_slug) - BadgeRewarder.public_send(badge_slug) + if (award_class = "Badges::#{badge_slug.classify}".safe_constantize) + award_class.call else - BadgeRewarder.award_badges(usernames, badge_slug, message) + Badges::Award.call( + User.where(username: usernames), + badge_slug, + message, + ) end end end diff --git a/config/schedule.yml b/config/schedule.yml index 462fac49c..205027c75 100644 --- a/config/schedule.yml +++ b/config/schedule.yml @@ -21,49 +21,49 @@ award_yearly_club_badges: class: "BadgeAchievements::BadgeAwardWorker" args: - "" - - award_yearly_club_badges + - award_yearly_club - "" award_beloved_comment_badges: cron: "5 */12 * * *" # 5 min past every 12th hour UTC class: "BadgeAchievements::BadgeAwardWorker" args: - "" - - award_beloved_comment_badges + - award_beloved_comment - "" award_four_week_streak_badge: cron: "10 */12 * * *" # 10 min past every 12th hour UTC class: "BadgeAchievements::BadgeAwardWorker" args: - "" - - award_four_week_streak_badge + - award_four_week_streak - "" award_eight_week_streak_badge: cron: "15 */12 * * *" # 15 min past every 12th hour UTC class: "BadgeAchievements::BadgeAwardWorker" args: - "" - - award_eight_week_streak_badge + - award_eight_week_streak - "" award_sixteen_week_streak_badge: cron: "20 */12 * * *" # 20 min past every 12th hour UTC class: "BadgeAchievements::BadgeAwardWorker" args: - "" - - award_sixteen_week_streak_badge + - award_sixteen_week_streak - "" award_weekly_tag_badges: cron: "0 11 * * 4" # 11 am UTC every Thursday class: "BadgeAchievements::BadgeAwardWorker" args: - "" - - award_tag_badges + - award_tag - "" award_contributor_badges_from_github: cron: "20 * * * *" # every hour, 20 min after the hour class: "BadgeAchievements::BadgeAwardWorker" args: - "" - - award_contributor_badges_from_github + - award_contributor_from_github - "" remove_old_html_variant_data: cron: "10 * * * *" # every hour, 10 min after the hour diff --git a/lib/tasks/badges.rake b/lib/tasks/badges.rake index dfbc65451..5f55a1ac6 100644 --- a/lib/tasks/badges.rake +++ b/lib/tasks/badges.rake @@ -2,7 +2,7 @@ task :award_top_seven_badges, [:arg1] => :environment do |_t, args| usernames = args[:arg1].split puts "Awarding top-7 badges to #{usernames}" - BadgeRewarder.award_top_seven_badges(usernames) + Badges::AwardTopSeven.call(usernames) puts "Done!" end @@ -10,7 +10,7 @@ end task :award_contributor_badges, [:arg1] => :environment do |_t, args| usernames = args[:arg1].split puts "Awarding dev-contributor badges to #{usernames}" - BadgeRewarder.award_contributor_badges(usernames) + Badges::AwardContributor.call(usernames) puts "Done!" end @@ -18,6 +18,6 @@ end task :award_fab_five_badges, [:arg1] => :environment do |_t, args| usernames = args[:arg1].split puts "Awarding fab 5 badges to #{usernames}" - BadgeRewarder.award_fab_five_badges(usernames) + Badges::AwardFabFive.call(usernames) puts "Done!" end diff --git a/spec/labor/badge_rewarder_spec.rb b/spec/labor/badge_rewarder_spec.rb deleted file mode 100644 index 1138ccbb6..000000000 --- a/spec/labor/badge_rewarder_spec.rb +++ /dev/null @@ -1,249 +0,0 @@ -require "rails_helper" - -RSpec.describe BadgeRewarder, type: :labor do - describe "::award_yearly_club_badges" do - before do - stub_const("#{described_class}::YEARS", BadgeRewarder::YEARS.slice(1, 2, 3)) - allow(ApplicationConfig).to receive(:[]) - allow(SiteConfig).to receive(:community_copyright_start_year).and_return(3.years.ago.year) - create(:badge, title: "one-year-club") - create(:badge, title: "two-year-club") - create(:badge, title: "three-year-club") - create(:badge, title: "heysddssdhey") - end - - it "rewards birthday badge to birthday folks who registered a year ago" do - user = create(:user, created_at: 366.days.ago) - newer_user = create(:user, created_at: 6.days.ago) - older_user = create(:user, created_at: 390.days.ago) - described_class.award_yearly_club_badges - expect(user.badge_achievements.size).to eq(1) - expect(newer_user.badge_achievements.size).to eq(0) - expect(older_user.badge_achievements.size).to eq(0) - end - - it "rewards 2-year birthday badge to birthday folks who registered 2 years ago" do - user = create(:user, created_at: 731.days.ago) - newer_user = create(:user, created_at: 6.days.ago) - older_user = create(:user, created_at: 800.days.ago) - described_class.award_yearly_club_badges - expect(user.badge_achievements.size).to eq(1) - expect(newer_user.badge_achievements.size).to eq(0) - expect(older_user.badge_achievements.size).to eq(0) - end - - it "rewards 3-year birthday badge to birthday folks who registered 3 years ago" do - user = create(:user, created_at: 1096.days.ago) - newer_user = create(:user, created_at: 6.days.ago) - older_user = create(:user, created_at: 1200.days.ago) - described_class.award_yearly_club_badges - expect(user.badge_achievements.size).to eq(1) - expect(newer_user.badge_achievements.size).to eq(0) - expect(older_user.badge_achievements.size).to eq(0) - end - end - - describe "::award_beloved_comments" do - it "rewards beloved comment to folks who have a qualifying comment" do - create(:badge, title: "Beloved comment", slug: "beloved-comment") - comment = create(:comment, commentable: create(:article)) - comment.update(public_reactions_count: 30) - described_class.award_beloved_comment_badges - expect(BadgeAchievement.count).to eq(1) - end - - it "does not reward beloved comment to non-qualifying comment" do - create(:badge, title: "Beloved comment", slug: "beloved-comment") - create(:comment, commentable: create(:article)) - described_class.award_beloved_comment_badges - expect(BadgeAchievement.count).to eq(0) - end - end - - it "rewards top seven badge to users" do - badge = create(:badge, title: "Top 7") - user = create(:user) - user_other = create(:user) - described_class.award_top_seven_badges([user.username, user_other.username]) - expect(BadgeAchievement.where(badge_id: badge.id).size).to eq(2) - end - - it "rewards fab five badge to users" do - badge = create(:badge, title: "Fab 5") - user = create(:user) - user_other = create(:user) - described_class.award_fab_five_badges([user.username, user_other.username]) - expect(BadgeAchievement.where(badge_id: badge.id).size).to eq(2) - end - - it "rewards contributor badges" do - badge = create(:badge, title: "Dev Contributor") - user = create(:user) - user_other = create(:user) - described_class.award_contributor_badges([user.username, user_other.username]) - expect(BadgeAchievement.where(badge_id: badge.id).size).to eq(2) - end - - describe "::award_four_week_streak_badge" do - it "calls award_streak_badge with argument 4" do - allow(described_class).to receive(:award_streak_badge) - described_class.award_four_week_streak_badge - expect(described_class).to have_received(:award_streak_badge).with(4) - end - end - - describe "::award_eight_week_streak_badge" do - it "calls award_streak_badge with argument 8" do - allow(described_class).to receive(:award_streak_badge) - described_class.award_eight_week_streak_badge - expect(described_class).to have_received(:award_streak_badge).with(8) - end - end - - describe "::award_sixteen_week_streak_badge" do - it "calls award_streak_badge with argument 16" do - allow(described_class).to receive(:award_streak_badge) - described_class.award_sixteen_week_streak_badge - expect(described_class).to have_received(:award_streak_badge).with(16) - end - end - - describe "::award_streak_badge" do - it "rewards badge to users with four straight weeks of articles" do - create(:badge, title: "4 Week Streak", slug: "4-week-streak") - user = create(:user) - create(:article, user: user, published: true, published_at: 26.days.ago) - create(:article, user: user, published: true, published_at: 19.days.ago) - create(:article, user: user, published: true, published_at: 12.days.ago) - create(:article, user: user, published: true, published_at: 5.days.ago) - described_class.award_streak_badge(4) - expect(user.badges.size).to eq(1) - end - - it "does not reward the badge to not qualified users" do - create(:badge, title: "4 Week Streak", slug: "4-week-streak") - user = create(:user) - create(:article, user: user, published: true, published_at: 26.days.ago) - create(:article, user: user, published: true, published_at: 19.days.ago) - create(:article, user: user, published: true, published_at: 5.days.ago) - described_class.award_streak_badge(4) - expect(user.badges.size).to eq(0) - end - end - - describe "::award_contributor_badges_from_github", vcr: true do - let(:user) { create(:user, :with_identity, identities: ["github"]) } - let(:badge) { create(:badge, title: "DEV Contributor") } - - before do - badge - omniauth_mock_github_payload - - stub_const("BadgeRewarder::REPOSITORIES", ["rust-lang/rust"]) - - user.identities.github.update_all(uid: "3372342") - end - - it "awards contributor badge" do - expect do - Timecop.freeze("2020-05-15T13:49:20Z") do - VCR.use_cassette("github_client_commits_contributor_badge") do - described_class.award_contributor_badges_from_github - end - end - end.to change(user.badge_achievements, :count).by(1) - end - - it "awards contributor badge once" do - expect do - Timecop.freeze("2020-05-15T13:49:20Z") do - VCR.use_cassette("github_client_commits_contributor_badge_twice") do - described_class.award_contributor_badges_from_github - described_class.award_contributor_badges_from_github - end - end - end.to change(user.badge_achievements, :count).by(1) - end - end - - describe "::award_tag_badges" do - let(:user) { create(:user) } - let(:second_user) { create(:user) } - let(:third_user) { create(:user) } - let(:article) { create(:article, user_id: user.id) } - let(:second_article) { create(:article, user_id: second_user.id) } - let(:third_article) { create(:article, user_id: third_user.id) } - let(:badge) { create(:badge) } - let(:tag) { create(:tag, badge_id: badge.id) } - - it "awards badge if qualifying article by score and tagged appropriately" do - article.update_columns(cached_tag_list: tag.name, score: 101) - described_class.award_tag_badges - expect(user.badge_achievements.size).to eq(1) - expect(user.badge_achievements.last.badge_id).to eq(badge.id) - end - - it "renders html for message" do - article.update_columns(cached_tag_list: tag.name, score: 101) - described_class.award_tag_badges - expect(user.badge_achievements.size).to eq(1) - expect(user.badge_achievements.last.rewarding_context_message).to include("