From 1c461f8891038ad9bca7215f7992ad5c86e3aad8 Mon Sep 17 00:00:00 2001 From: Molly Struve Date: Wed, 20 May 2020 11:54:30 -0500 Subject: [PATCH] [deploy] Remove Unused Pro Memberships (#7894) --- .erb-lint.yml | 1 - .../initializers/initializeSettings.js | 20 +- .../admin/pro_memberships_controller.rb | 21 -- app/controllers/pro_memberships_controller.rb | 46 ---- app/controllers/users_controller.rb | 6 - app/dashboards/dashboard_manifest.rb | 1 - app/dashboards/pro_membership_dashboard.rb | 67 ----- app/decorators/user_decorator.rb | 1 - app/labor/article_suggester.rb | 4 +- app/mailers/pro_membership_mailer.rb | 11 - app/models/article.rb | 3 +- app/models/organization.rb | 4 - app/models/pro_membership.rb | 61 ----- app/models/user.rb | 5 +- app/policies/pro_membership_policy.rb | 9 - app/services/pro_memberships/biller.rb | 118 -------- app/services/pro_memberships/creator.rb | 40 --- .../pro_memberships/expiration_notifier.rb | 47 ---- app/services/suggester/articles/boosted.rb | 2 +- app/services/suggester/articles/classic.rb | 2 +- .../suggester/articles/high_quality.rb | 2 +- app/services/users/delete_activity.rb | 1 - .../_article_content_area.html.erb | 1 - .../_article_followable_area.html.erb | 1 - app/views/articles/_bottom_content.html.erb | 2 +- app/views/articles/_single_story.html.erb | 1 - app/views/articles/_sticky_nav.html.erb | 1 - app/views/articles/show.html.erb | 2 +- .../credits/_ledger_pro_membership.html.erb | 3 - .../expiring_membership.html.erb | 8 - .../expiring_membership.text.erb | 8 - app/views/pro_memberships/show.html.erb | 51 ---- app/views/shared/_pro_checkmark.html.erb | 1 - app/views/users/_pro_membership.html.erb | 66 ----- app/views/users/show.html.erb | 2 +- config/routes.rb | 2 - lib/tasks/pro_memberships.rake | 19 -- spec/decorators/user_decorator_spec.rb | 26 +- spec/factories/users.rb | 6 - .../previews/pro_membership_mailer_preview.rb | 7 - spec/mailers/pro_membership_mailer_spec.rb | 69 ----- spec/models/article_spec.rb | 2 - spec/models/organization_spec.rb | 6 - spec/models/pro_membership_spec.rb | 138 ---------- spec/models/user_spec.rb | 24 +- spec/requests/credits_spec.rb | 13 - spec/requests/dashboard_spec.rb | 9 - spec/requests/pro_memberships_spec.rb | 173 ------------ spec/services/pro_memberships/biller_spec.rb | 257 ------------------ .../expiration_notifier_spec.rb | 66 ----- .../user_creates_a_pro_membership_spec.rb | 20 -- .../user_visits_pro_page_spec.rb | 69 ----- ...isits_settings_pro_membership_page_spec.rb | 90 ------ 53 files changed, 25 insertions(+), 1590 deletions(-) delete mode 100644 app/controllers/admin/pro_memberships_controller.rb delete mode 100644 app/controllers/pro_memberships_controller.rb delete mode 100644 app/dashboards/pro_membership_dashboard.rb delete mode 100644 app/mailers/pro_membership_mailer.rb delete mode 100644 app/models/pro_membership.rb delete mode 100644 app/policies/pro_membership_policy.rb delete mode 100644 app/services/pro_memberships/biller.rb delete mode 100644 app/services/pro_memberships/creator.rb delete mode 100644 app/services/pro_memberships/expiration_notifier.rb delete mode 100644 app/views/credits/_ledger_pro_membership.html.erb delete mode 100644 app/views/mailers/pro_membership_mailer/expiring_membership.html.erb delete mode 100644 app/views/mailers/pro_membership_mailer/expiring_membership.text.erb delete mode 100644 app/views/pro_memberships/show.html.erb delete mode 100644 app/views/shared/_pro_checkmark.html.erb delete mode 100644 app/views/users/_pro_membership.html.erb delete mode 100644 lib/tasks/pro_memberships.rake delete mode 100644 spec/mailers/previews/pro_membership_mailer_preview.rb delete mode 100644 spec/mailers/pro_membership_mailer_spec.rb delete mode 100644 spec/models/pro_membership_spec.rb delete mode 100644 spec/requests/pro_memberships_spec.rb delete mode 100644 spec/services/pro_memberships/biller_spec.rb delete mode 100644 spec/services/pro_memberships/expiration_notifier_spec.rb delete mode 100644 spec/system/pro_memberships/user_creates_a_pro_membership_spec.rb delete mode 100644 spec/system/pro_memberships/user_visits_pro_page_spec.rb delete mode 100644 spec/system/pro_memberships/user_visits_settings_pro_membership_page_spec.rb diff --git a/.erb-lint.yml b/.erb-lint.yml index 59e7c35e4..5e68e12e6 100644 --- a/.erb-lint.yml +++ b/.erb-lint.yml @@ -40,4 +40,3 @@ linters: - '**/app/views/pages/onboarding.html.erb' - '**/app/views/partnerships/show.html.erb' - '**/app/views/partnerships/index.html.erb' - - '**/app/views/pro_memberships/show.html.erb' diff --git a/app/assets/javascripts/initializers/initializeSettings.js b/app/assets/javascripts/initializers/initializeSettings.js index 3fe0c4149..37d216c41 100644 --- a/app/assets/javascripts/initializers/initializeSettings.js +++ b/app/assets/javascripts/initializers/initializeSettings.js @@ -1,12 +1,10 @@ -'use strict'; - /* global timestampToLocalDateTime */ function initializeSettings() { // highlights organization secret on click const settingsOrgSecret = document.getElementById('settings-org-secret'); if (settingsOrgSecret) { - settingsOrgSecret.addEventListener('click', event => { + settingsOrgSecret.addEventListener('click', (event) => { event.target.select(); }); } @@ -29,20 +27,4 @@ function initializeSettings() { timeOptions, ); } - - // asks for confirmation on activating pro membership - const createProForm = document.getElementById('new_pro_membership'); - if (createProForm) { - createProForm.addEventListener('submit', event => { - event.preventDefault(); - - // eslint-disable-next-line no-alert - if (window.confirm('Are you sure?')) { - event.target.submit(); - return true; - } - - return false; - }); - } } diff --git a/app/controllers/admin/pro_memberships_controller.rb b/app/controllers/admin/pro_memberships_controller.rb deleted file mode 100644 index 4d4dadc0d..000000000 --- a/app/controllers/admin/pro_memberships_controller.rb +++ /dev/null @@ -1,21 +0,0 @@ -module Admin - class ProMembershipsController < Admin::ApplicationController - # To customize the behavior of this controller, - # you can overwrite any of the RESTful actions. For example: - # - # def index - # super - # @resources = ProMembership. - # page(params[:page]). - # per(10) - # end - - # Define a custom finder by overriding the `find_resource` method: - # def find_resource(param) - # ProMembership.find_by!(slug: param) - # end - - # See https://administrate-prototype.herokuapp.com/customizing_controller_actions - # for more information - end -end diff --git a/app/controllers/pro_memberships_controller.rb b/app/controllers/pro_memberships_controller.rb deleted file mode 100644 index 802fd884c..000000000 --- a/app/controllers/pro_memberships_controller.rb +++ /dev/null @@ -1,46 +0,0 @@ -class ProMembershipsController < ApplicationController - before_action :authenticate_user!, except: %i[show] - before_action :load_pro_membership, only: %i[update] - after_action :verify_authorized, except: %i[show] - - def show - @user = current_user - @pro_membership = current_user&.pro_membership - end - - def create - authorize ProMembership - - if ProMemberships::Creator.call(current_user) - flash[:settings_notice] = "You are now a Pro!" - else - flash[:error] = "You don't have enough credits!" - end - - redirect_to user_settings_path("pro-membership") - end - - def update - raise Pundit::NotAuthorizedError, "You don't have a Pro Membership" unless @pro_membership - - authorize @pro_membership - - if @pro_membership.update(update_params) - flash[:settings_notice] = "Your membership has been updated!" - else - flash[:error] = "An error has occurred while updating your membership!" - end - - redirect_to user_settings_path("pro-membership") - end - - private - - def load_pro_membership - @pro_membership = current_user.pro_membership - end - - def update_params - params.require(:pro_membership).permit(:auto_recharge) - end -end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 2fed8d243..782d6174f 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -253,8 +253,6 @@ class UsersController < ApplicationController handle_integrations_tab when "billing" handle_billing_tab - when "pro-membership" - handle_pro_membership_tab when "account" handle_account_tab when "response-templates" @@ -323,10 +321,6 @@ class UsersController < ApplicationController @customer = Payments::Customer.get(stripe_code) if stripe_code.present? end - def handle_pro_membership_tab - @pro_membership = current_user.pro_membership - end - def handle_account_tab community_name = ApplicationConfig["COMMUNITY_NAME"] @email_body = <<~HEREDOC diff --git a/app/dashboards/dashboard_manifest.rb b/app/dashboards/dashboard_manifest.rb index 83eac66d6..1b419b0f8 100644 --- a/app/dashboards/dashboard_manifest.rb +++ b/app/dashboards/dashboard_manifest.rb @@ -29,7 +29,6 @@ class DashboardManifest html_variant_trials html_variant_successes sponsorships - pro_memberships classified_listing_categories ].freeze # DASHBOARDS = [ diff --git a/app/dashboards/pro_membership_dashboard.rb b/app/dashboards/pro_membership_dashboard.rb deleted file mode 100644 index 43d16ee87..000000000 --- a/app/dashboards/pro_membership_dashboard.rb +++ /dev/null @@ -1,67 +0,0 @@ -require "administrate/base_dashboard" - -class ProMembershipDashboard < Administrate::BaseDashboard - # ATTRIBUTE_TYPES - # a hash that describes the type of each of the model's fields. - # - # Each different type represents an Administrate::Field object, - # which determines how the attribute is displayed - # on pages throughout the dashboard. - ATTRIBUTE_TYPES = { - user: Field::BelongsTo, - id: Field::Number, - status: Field::String, - expires_at: Field::DateTime, - expiration_notification_at: Field::DateTime, - expiration_notifications_count: Field::Number, - auto_recharge: Field::Boolean, - created_at: Field::DateTime, - updated_at: Field::DateTime - }.freeze - - # COLLECTION_ATTRIBUTES - # an array of attributes that will be displayed on the model's index page. - # - # By default, it's limited to four items to reduce clutter on index pages. - # Feel free to add, remove, or rearrange items. - COLLECTION_ATTRIBUTES = %i[ - user - id - status - expires_at - auto_recharge - ].freeze - - # SHOW_PAGE_ATTRIBUTES - # an array of attributes that will be displayed on the model's show page. - SHOW_PAGE_ATTRIBUTES = %i[ - user - id - status - expires_at - expiration_notification_at - expiration_notifications_count - auto_recharge - created_at - updated_at - ].freeze - - # FORM_ATTRIBUTES - # an array of attributes that will be displayed - # on the model's form (`new` and `edit`) pages. - FORM_ATTRIBUTES = %i[ - user - status - expires_at - expiration_notification_at - expiration_notifications_count - auto_recharge - ].freeze - - # Overwrite this method to customize how pro memberships are displayed - # across all pages of the admin dashboard. - # - # def display_resource(pro_membership) - # "ProMembership ##{pro_membership.id}" - # end -end diff --git a/app/decorators/user_decorator.rb b/app/decorators/user_decorator.rb index 1e1998ce5..635eda1f8 100644 --- a/app/decorators/user_decorator.rb +++ b/app/decorators/user_decorator.rb @@ -53,7 +53,6 @@ class UserDecorator < ApplicationDecorator body_class = [ config_theme.tr("_", "-"), "#{config_font.tr('_', '-')}-article-body", - "pro-status-#{pro?}", "trusted-status-#{trusted}", "#{config_navbar.tr('_', '-')}-navbar-config", ] diff --git a/app/labor/article_suggester.rb b/app/labor/article_suggester.rb index 535b84a8a..8bdc2872e 100644 --- a/app/labor/article_suggester.rb +++ b/app/labor/article_suggester.rb @@ -31,7 +31,7 @@ class ArticleSuggester Article.published.where(featured: true). where.not(id: ids_to_ignore). order("hotness_score DESC"). - includes(user: [:pro_membership]). + includes(:user). offset(rand(0..offsets[1])). first(max) end @@ -40,7 +40,7 @@ class ArticleSuggester Article.published.tagged_with(article.tag_list, any: true). where.not(id: article.id). order("hotness_score DESC"). - includes(user: [:pro_membership]). + includes(:user). offset(rand(0..offsets[0])). first(max) end diff --git a/app/mailers/pro_membership_mailer.rb b/app/mailers/pro_membership_mailer.rb deleted file mode 100644 index 08e4b4008..000000000 --- a/app/mailers/pro_membership_mailer.rb +++ /dev/null @@ -1,11 +0,0 @@ -class ProMembershipMailer < ApplicationMailer - default from: -> { email_from("Pro Memberships") } - - def expiring_membership(pro_membership, expiration_date) - @pro_membership = pro_membership - @user = pro_membership.user - @days = (Time.current.to_date..expiration_date.to_date).count - 1 - @date = expiration_date.to_date - mail(to: @user.email, subject: "Your Pro Membership will expire in #{@days} days!") - end -end diff --git a/app/models/article.rb b/app/models/article.rb index 99673ab1b..5b6f108eb 100644 --- a/app/models/article.rb +++ b/app/models/article.rb @@ -551,8 +551,7 @@ class Article < ApplicationRecord username: object.username, slug: object == organization ? object.slug : object.username, profile_image_90: object.profile_image_90, - profile_image_url: object.profile_image_url, - pro: object == user ? user.pro? : false # organizations can't be pro users + profile_image_url: object.profile_image_url } end diff --git a/app/models/organization.rb b/app/models/organization.rb index 51f8fd164..0994d4fe7 100644 --- a/app/models/organization.rb +++ b/app/models/organization.rb @@ -99,10 +99,6 @@ class Organization < ApplicationRecord credits.unspent.size >= num_credits_needed end - def pro? - false - end - def banned false end diff --git a/app/models/pro_membership.rb b/app/models/pro_membership.rb deleted file mode 100644 index c35f2e300..000000000 --- a/app/models/pro_membership.rb +++ /dev/null @@ -1,61 +0,0 @@ -class ProMembership < ApplicationRecord - STATUSES = %w[active expired].freeze - MONTHLY_COST = 5 - MONTHLY_COST_USD = 25 - - belongs_to :user - - validates :user, :status, :expiration_notifications_count, presence: true - validates :user, uniqueness: true - validates :expires_at, presence: true, on: :save - validates :status, inclusion: { in: STATUSES } - - scope :active, -> { where(status: :active) } - scope :expired, -> { where(status: :expired) } - - before_create :set_expiration_date - after_save :resave_user_articles - after_save :bust_cache - - def expired? - expires_at <= Time.current - end - - def active? - expires_at > Time.current - end - - def expire! - update!(expires_at: Time.current, status: :expired) - end - - def renew! - update!( - expires_at: 1.month.from_now, - status: :active, - expiration_notification_at: nil, - expiration_notifications_count: 0, - ) - end - - private - - def set_expiration_date - self.expires_at = 1.month.from_now - end - - # if the membership is new or the user flips from expired to active and viceversa, - # we need to resave all of the user's articles to make sure that the pro details - # that are cached with them are refreshed - def resave_user_articles - if saved_change_to_created_at? || - saved_change_to_expires_at? || - saved_change_to_status? - Users::ResaveArticlesWorker.perform_async(user.id) - end - end - - def bust_cache - Rails.cache.delete("user-#{user.id}/has_pro_membership") - end -end diff --git a/app/models/user.rb b/app/models/user.rb index 858cf819c..cf22987a0 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -92,7 +92,6 @@ class User < ApplicationRecord has_many :webhook_endpoints, class_name: "Webhook::Endpoint", foreign_key: :user_id, inverse_of: :user, dependent: :delete_all has_one :counters, class_name: "UserCounter", dependent: :destroy - has_one :pro_membership, dependent: :destroy mount_uploader :profile_image, ProfileImageUploader @@ -292,8 +291,8 @@ class User < ApplicationRecord end def pro? - Rails.cache.fetch("user-#{id}/has_pro_membership", expires_in: 200.hours) do - pro_membership&.active? || has_role?(:pro) + Rails.cache.fetch("user-#{id}/has_pro_role", expires_in: 200.hours) do + has_role?(:pro) end end diff --git a/app/policies/pro_membership_policy.rb b/app/policies/pro_membership_policy.rb deleted file mode 100644 index e1816f4d1..000000000 --- a/app/policies/pro_membership_policy.rb +++ /dev/null @@ -1,9 +0,0 @@ -class ProMembershipPolicy < ApplicationPolicy - def create? - user.pro_membership.nil? && !user.has_role?(:pro) - end - - def update? - user.pro_membership.present? - end -end diff --git a/app/services/pro_memberships/biller.rb b/app/services/pro_memberships/biller.rb deleted file mode 100644 index 2399cb30e..000000000 --- a/app/services/pro_memberships/biller.rb +++ /dev/null @@ -1,118 +0,0 @@ -module ProMemberships - # Bills pro memberships that expire today - class Biller - def self.call(*args) - new(*args).call - end - - def call - relation = ProMembership.includes(user: [:credits]). - where("DATE(expires_at) = ?", Time.zone.today) - relation.find_each do |membership| - user = membership.user - cost = ProMembership::MONTHLY_COST - - if user.enough_credits?(cost) - renew_membership(membership, cost) - elsif membership.auto_recharge - if user.stripe_id_code - charge_user_and_renew_membership(membership, cost) - else - notify_admins(user, "auto recharge error: missing Stripe customer ID!") - end - else - expire_membership(membership) - notify_admins(user, "pro membership expired!") - end - end - end - - private - - def renew_membership(membership, cost) - ActiveRecord::Base.transaction do - user = membership.user - - membership.renew! - - success = Credits::Buyer.call( - purchaser: user, - purchase: membership, - cost: cost, - ) - - unless success - notify_admins("#{user.name}'s pro membership could not be renewed with enough credits!") - raise ActiveRecord::Rollback - end - - chat_channel = ChatChannel.find_by(slug: "pro-members") - if chat_channel && !chat_channel.chat_channel_memberships.exists?(user_id: user.id) - chat_channel.add_users(user) - end - - success - end - rescue StandardError => e - Rails.logger.error(e) - notify_admins(membership.user, "error: #{e.message}") - end - - def expire_membership(membership) - ActiveRecord::Base.transaction do - user = membership.user - - membership.expire! - - chat_channel = ChatChannel.find_by(slug: "pro-members") - chat_channel&.remove_user(user) - - user.save - end - rescue StandardError => e - Rails.logger.error(e) - notify_admins(membership.user, "error: #{e.message}") - end - - def recharge(membership, cost) - user = membership.user - - # charge customer for credits - customer = Payments::Customer.get(user.stripe_id_code) - Payments::Customer.charge( - customer: customer, - amount: ProMembership::MONTHLY_COST_USD, - description: "Purchase of #{cost} credits.", - ) - - # add credits - credits = Array.new(cost) { Credit.new(user: user, cost: 1) } - user.credits << credits - user.save! - end - - def charge_user_and_renew_membership(membership, cost) - user = membership.user - recharge(membership, cost) - renew_membership(membership, cost) - rescue Payments::PaymentsError => e - Rails.logger.error(e) - notify_admins(user, "payment error: #{e.message}") - rescue ActiveRecord::RecordInvalid => e - Rails.logger.error(e) - notify_admins(user, "credits creation error: #{e.message}") - rescue StandardError => e - Rails.logger.error(e) - notify_admins(user, "error: #{e.message}") - end - - def notify_admins(user, message) - Slack::Messengers::Worker.perform_async( - message: "ProMemberships::Biller: #{user.username}: #{message}", - channel: "pro-memberships", - username: "pro-memberships", - icon_emoji: ":fire:", - ) - end - end -end diff --git a/app/services/pro_memberships/creator.rb b/app/services/pro_memberships/creator.rb deleted file mode 100644 index 2195342fc..000000000 --- a/app/services/pro_memberships/creator.rb +++ /dev/null @@ -1,40 +0,0 @@ -module ProMemberships - class Creator - def initialize(user) - @user = user - end - - def self.call(*args) - new(*args).call - end - - def call - if purchase_pro_membership - channel = ChatChannel.find_by(slug: "pro-members") - channel&.add_users(user) - - true - else - false - end - end - - private - - attr_reader :user - - def purchase_pro_membership - cost = ProMembership::MONTHLY_COST - return false unless user.enough_credits?(cost) - - ActiveRecord::Base.transaction do - pro_membership = ProMembership.create!(user: user) - Credits::Buyer.call( - purchaser: user, - purchase: pro_membership, - cost: cost, - ) - end - end - end -end diff --git a/app/services/pro_memberships/expiration_notifier.rb b/app/services/pro_memberships/expiration_notifier.rb deleted file mode 100644 index 8b4853ed6..000000000 --- a/app/services/pro_memberships/expiration_notifier.rb +++ /dev/null @@ -1,47 +0,0 @@ -module ProMemberships - # Notifies Pro members with insufficient credits of the impeding expiration - class ExpirationNotifier - def initialize(expiration_date) - @expiration_date = expiration_date - end - - def self.call(*args) - new(*args).call - end - - def call - count = 0 - - # NOTE: naive implementation because this is supposed to be called every 24hrs - relation = ProMembership. - includes(user: [:credits]). - where("DATE(expires_at) = ?", expiration_date). - where(auto_recharge: false) - relation.find_each do |membership| - next if membership.user.enough_credits?(ProMembership::MONTHLY_COST) - - # NOTE: maybe we should "deliver_later" and update the flags there - ProMembershipMailer.expiring_membership(membership, expiration_date).deliver_now - - membership.expiration_notification_at = Time.current - membership.increment(:expiration_notifications_count) - membership.save! - - Slack::Messengers::Worker.perform_async( - message: "#{membership.user.name}'s pro membership expires on #{expiration_date}", - channel: "pro-memberships", - username: "pro-memberships", - icon_emoji: ":fire:", - ) - - count += 1 - end - - count - end - - private - - attr_reader :expiration_date - end -end diff --git a/app/services/suggester/articles/boosted.rb b/app/services/suggester/articles/boosted.rb index 91d1c7f00..21d7e2c6f 100644 --- a/app/services/suggester/articles/boosted.rb +++ b/app/services/suggester/articles/boosted.rb @@ -10,7 +10,7 @@ module Suggester end def suggest - base_articles = Article.includes(user: [:pro_membership]). + base_articles = Article.includes(:user). includes(:organization). where.not(id: not_ids, organization_id: nil). cached_tagged_with(tag) diff --git a/app/services/suggester/articles/classic.rb b/app/services/suggester/articles/classic.rb index 71ee2930d..ef7f8d499 100644 --- a/app/services/suggester/articles/classic.rb +++ b/app/services/suggester/articles/classic.rb @@ -24,7 +24,7 @@ module Suggester tag_name = tag_names.sample Rails.cache.fetch("classic-article-for-tag-#{tag_name}}", expires_in: 90.minutes) do Article.published.cached_tagged_with(tag_name). - includes(user: [:pro_membership]). + includes(:user). limited_column_select. where(featured: true). where("positive_reactions_count > ?", MIN_REACTION_COUNT). diff --git a/app/services/suggester/articles/high_quality.rb b/app/services/suggester/articles/high_quality.rb index c985be3ee..e3237a1a0 100644 --- a/app/services/suggester/articles/high_quality.rb +++ b/app/services/suggester/articles/high_quality.rb @@ -9,7 +9,7 @@ module Suggester def suggest(num) Article.published.where(featured: true). - includes(user: [:pro_membership]). + includes(:user). limited_column_select. where("positive_reactions_count > ?", MIN_HQ_REACTION_COUNT). where.not(id: @not_ids). diff --git a/app/services/users/delete_activity.rb b/app/services/users/delete_activity.rb index d153bd82c..33332ca09 100644 --- a/app/services/users/delete_activity.rb +++ b/app/services/users/delete_activity.rb @@ -21,7 +21,6 @@ module Users user.page_views.delete_all user.poll_skips.delete_all user.poll_votes.delete_all - user.pro_membership.delete if user.pro_membership.present? user.rating_votes.delete_all user.response_templates.delete_all user.classified_listings.destroy_all diff --git a/app/views/additional_content_boxes/_article_content_area.html.erb b/app/views/additional_content_boxes/_article_content_area.html.erb index 66640bcf8..33fa0f834 100644 --- a/app/views/additional_content_boxes/_article_content_area.html.erb +++ b/app/views/additional_content_boxes/_article_content_area.html.erb @@ -5,7 +5,6 @@ <%= article.user.username %> profile image <%= article.user.name %> - <%= render "shared/pro_checkmark" if article.user.pro? %>

diff --git a/app/views/additional_content_boxes/_article_followable_area.html.erb b/app/views/additional_content_boxes/_article_followable_area.html.erb index 2b3ff0e97..4710472a2 100644 --- a/app/views/additional_content_boxes/_article_followable_area.html.erb +++ b/app/views/additional_content_boxes/_article_followable_area.html.erb @@ -30,7 +30,6 @@ " data-details="<%= followable&.slug if classification == "boosted" %>__PROFILE"><%= followable.name %> - <%= render "shared/pro_checkmark" if followable.pro? %> <% end %> diff --git a/config/routes.rb b/config/routes.rb index b14b72d76..0010ad160 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -223,12 +223,10 @@ Rails.application.routes.draw do resources :partnerships, only: %i[index create show], param: :option resources :display_ad_events, only: [:create] resources :badges, only: [:index] - resource :pro_membership, path: :pro, only: %i[show create update] resources :user_blocks, param: :blocked_id, only: %i[show create destroy] resources :podcasts, only: %i[new create] resources :article_approvals, only: %i[create] resources :video_chats, only: %i[show] - resolve("ProMembership") { [:pro_membership] } # see https://guides.rubyonrails.org/routing.html#using-resolve namespace :followings, defaults: { format: :json } do get :users get :tags diff --git a/lib/tasks/pro_memberships.rake b/lib/tasks/pro_memberships.rake deleted file mode 100644 index e3c800f1e..000000000 --- a/lib/tasks/pro_memberships.rake +++ /dev/null @@ -1,19 +0,0 @@ -# All of these tasks need to be scheduled daily -namespace :pro_memberships do - desc "Notify pro users with insufficient credits that their membership is about to expire in a week" - task notify_expirations_one_week_before: :environment do - num_notified = ProMemberships::ExpirationNotifier.call(1.week.from_now) - Rails.logger.info("Notified #{num_notified} Pro users...") - end - - desc "Notify pro users with insufficient credits that their membership is about to expire in a day" - task notify_expirations_one_day_before: :environment do - num_notified = ProMemberships::ExpirationNotifier.call(1.day.from_now) - Rails.logger.info("Notified #{num_notified} Pro users...") - end - - desc "Bill pro users and optionally charge their cards" - task bill_users: :environment do - ProMemberships::Biller.call - end -end diff --git a/spec/decorators/user_decorator_spec.rb b/spec/decorators/user_decorator_spec.rb index be3ff434f..9ffbe19dc 100644 --- a/spec/decorators/user_decorator_spec.rb +++ b/spec/decorators/user_decorator_spec.rb @@ -108,7 +108,7 @@ RSpec.describe UserDecorator, type: :decorator do describe "#config_body_class" do it "creates proper body class with defaults" do expected_result = %W[ - default default-article-body pro-status-#{user.pro?} + default default-article-body trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config ].join(" ") expect(user.decorate.config_body_class).to eq(expected_result) @@ -117,7 +117,7 @@ RSpec.describe UserDecorator, type: :decorator do it "creates proper body class with sans serif config" do user.config_font = "sans_serif" expected_result = %W[ - default sans-serif-article-body pro-status-#{user.pro?} + default sans-serif-article-body trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config ].join(" ") expect(user.decorate.config_body_class).to eq(expected_result) @@ -126,7 +126,7 @@ RSpec.describe UserDecorator, type: :decorator do it "creates proper body class with night theme" do user.config_theme = "night_theme" expected_result = %W[ - night-theme default-article-body pro-status-#{user.pro?} + night-theme default-article-body trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config ].join(" ") expect(user.decorate.config_body_class).to eq(expected_result) @@ -135,7 +135,7 @@ RSpec.describe UserDecorator, type: :decorator do it "creates proper body class with pink theme" do user.config_theme = "pink_theme" expected_result = %W[ - pink-theme default-article-body pro-status-#{user.pro?} + pink-theme default-article-body trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config ].join(" ") expect(user.decorate.config_body_class).to eq(expected_result) @@ -144,7 +144,7 @@ RSpec.describe UserDecorator, type: :decorator do it "creates proper body class with minimal light theme" do user.config_theme = "minimal_light_theme" expected_result = %W[ - minimal-light-theme default-article-body pro-status-#{user.pro?} + minimal-light-theme default-article-body trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config ].join(" ") expect(user.decorate.config_body_class).to eq(expected_result) @@ -153,7 +153,7 @@ RSpec.describe UserDecorator, type: :decorator do it "works with static navbar" do user.config_navbar = "static" expected_result = %W[ - default default-article-body pro-status-#{user.pro?} + default default-article-body trusted-status-#{user.trusted} static-navbar-config ].join(" ") expect(user.decorate.config_body_class).to eq(expected_result) @@ -162,21 +162,11 @@ RSpec.describe UserDecorator, type: :decorator do context "when user with roles" do let(:user) { create(:user) } - it "creates proper body class with pro user" do - user.add_role(:pro) - - expected_result = %W[ - default default-article-body pro-status-true - trusted-status-#{user.trusted} default-navbar-config - ].join(" ") - expect(user.decorate.config_body_class).to eq(expected_result) - end - it "creates proper body class with trusted user" do user.add_role(:trusted) - expected_result = %W[ - default default-article-body pro-status-#{user.pro?} + expected_result = %w[ + default default-article-body trusted-status-true default-navbar-config ].join(" ") expect(user.decorate.config_body_class).to eq(expected_result) diff --git a/spec/factories/users.rb b/spec/factories/users.rb index c299adcf7..d95667437 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -124,12 +124,6 @@ FactoryBot.define do end end - trait :with_pro_membership do - after(:create) do |user| - create(:pro_membership, user: user) - end - end - trait :tag_moderator do after(:create) do |user| tag = create(:tag) diff --git a/spec/mailers/previews/pro_membership_mailer_preview.rb b/spec/mailers/previews/pro_membership_mailer_preview.rb deleted file mode 100644 index b0ddb8519..000000000 --- a/spec/mailers/previews/pro_membership_mailer_preview.rb +++ /dev/null @@ -1,7 +0,0 @@ -# Preview all emails at http://localhost:3000/rails/mailers/pro_membership_mailer -class ProMembershipMailerPreview < ActionMailer::Preview - def expiring_membership - pro_membership = ProMembership.find_or_create_by(user: User.last) - ProMembershipMailer.expiring_membership(pro_membership, 1.week.from_now) - end -end diff --git a/spec/mailers/pro_membership_mailer_spec.rb b/spec/mailers/pro_membership_mailer_spec.rb deleted file mode 100644 index 0056ab991..000000000 --- a/spec/mailers/pro_membership_mailer_spec.rb +++ /dev/null @@ -1,69 +0,0 @@ -require "rails_helper" - -RSpec.describe ProMembershipMailer, type: :mailer do - let(:pro_membership) { build_stubbed(:pro_membership) } - let(:user) { pro_membership.user } - - describe "#expiring_membership" do - it "works correctly" do - Timecop.freeze(Time.current) do - email = described_class.expiring_membership(pro_membership, 1.week.from_now) - - expect(email.subject).to eq("Your Pro Membership will expire in 7 days!") - expect(email.to).to eq([user.email]) - expect(email.from).to eq([SiteConfig.email_addresses[:default]]) - expect(email["from"].value).to eq("#{ApplicationConfig['COMMUNITY_NAME']} Pro Memberships <#{SiteConfig.email_addresses[:default]}>") - end - end - - context "when generating the plain text email" do - it "includes the user's name" do - email = described_class.expiring_membership(pro_membership, 1.week.from_now) - expect(email.text_part.body).to include(user.name) - end - - it "includes the expiration date" do - Timecop.freeze(Time.current) do - expiration_date = 1.week.from_now - email = described_class.expiring_membership(pro_membership, expiration_date) - expect(email.text_part.body).to include(expiration_date.to_date.to_s(:long)) - end - end - - it "includes credits path" do - email = described_class.expiring_membership(pro_membership, 1.week.from_now) - expect(email.text_part.body).to include(credits_path) - end - - it "includes pro membership path" do - email = described_class.expiring_membership(pro_membership, 1.week.from_now) - expect(email.text_part.body).to include(user_settings_path("pro-membership")) - end - end - - context "when generating the html email" do - it "includes the user's name" do - email = described_class.expiring_membership(pro_membership, 1.week.from_now) - expect(email.html_part.body).to include(user.name) - end - - it "includes the expiration date" do - Timecop.freeze(Time.current) do - expiration_date = 1.week.from_now - email = described_class.expiring_membership(pro_membership, expiration_date) - expect(email.html_part.body).to include(expiration_date.to_date.to_s(:long)) - end - end - - it "includes credits path" do - email = described_class.expiring_membership(pro_membership, 1.week.from_now) - expect(email.html_part.body).to include(CGI.escape(credits_path)) - end - - it "includes pro membership path" do - email = described_class.expiring_membership(pro_membership, 1.week.from_now) - expect(email.html_part.body).to include(CGI.escape(user_settings_path("pro-membership"))) - end - end - end -end diff --git a/spec/models/article_spec.rb b/spec/models/article_spec.rb index 2737cf32a..996e7b55e 100644 --- a/spec/models/article_spec.rb +++ b/spec/models/article_spec.rb @@ -663,7 +663,6 @@ RSpec.describe Article, type: :model do expect(article.cached_user.slug).to eq(article.user.username) expect(article.cached_user.profile_image_90).to eq(article.user.profile_image_90) expect(article.cached_user.profile_image_url).to eq(article.user.profile_image_url) - expect(article.cached_user.pro).to eq(article.user.pro?) end it "assigns cached_organization on save" do @@ -673,7 +672,6 @@ RSpec.describe Article, type: :model do expect(article.cached_organization.slug).to eq(article.organization.slug) expect(article.cached_organization.profile_image_90).to eq(article.organization.profile_image_90) expect(article.cached_organization.profile_image_url).to eq(article.organization.profile_image_url) - expect(article.cached_organization.pro).to be(false) end end diff --git a/spec/models/organization_spec.rb b/spec/models/organization_spec.rb index 84901e3a1..9e0b175c5 100644 --- a/spec/models/organization_spec.rb +++ b/spec/models/organization_spec.rb @@ -270,12 +270,6 @@ RSpec.describe Organization, type: :model do end end - describe "#pro?" do - it "always returns false" do - expect(build(:organization).pro?).to be(false) - end - end - describe "#decoarated" do it "returns not fully banished" do expect(organization.decorate.fully_banished?).to eq(false) diff --git a/spec/models/pro_membership_spec.rb b/spec/models/pro_membership_spec.rb deleted file mode 100644 index dce3c306e..000000000 --- a/spec/models/pro_membership_spec.rb +++ /dev/null @@ -1,138 +0,0 @@ -require "rails_helper" - -RSpec.describe ProMembership, type: :model do - it { is_expected.to belong_to(:user) } - it { is_expected.to validate_presence_of(:user) } - it { is_expected.to validate_presence_of(:status) } - it { is_expected.to validate_presence_of(:expires_at).on(:save) } - it { is_expected.to validate_presence_of(:expiration_notifications_count) } - it { is_expected.to validate_inclusion_of(:status).in_array(ProMembership::STATUSES) } - it { is_expected.to have_db_index(:status) } - it { is_expected.to have_db_index(:expires_at) } - - describe "constants" do - it "has the correct values for constants" do - expect(ProMembership::STATUSES).to eq(%w[active expired]) - expect(ProMembership::MONTHLY_COST).to eq(5) - expect(ProMembership::MONTHLY_COST_USD).to eq(25) - end - end - - describe "defaults" do - it "has the correct defaults" do - pm = described_class.new - expect(pm.status).to eq("active") - expect(pm.expires_at).to be(nil) - expect(pm.expiration_notification_at).to be(nil) - expect(pm.expiration_notifications_count).to eq(0) - expect(pm.auto_recharge).to be(false) - end - end - - describe "creation" do - it "sets expires_at to a month from now" do - Timecop.freeze(Time.current) do - pm = described_class.create!(user: create(:user)) - expect(pm.expires_at.to_i).to eq(1.month.from_now.to_i) - end - end - end - - describe "#expired?" do - it "returns false if expires_at is in the future" do - Timecop.freeze(Time.current) do - pm = build(:pro_membership, expires_at: 5.seconds.from_now) - expect(pm.expired?).to be(false) - end - end - - it "returns true if expires_at is in the past" do - Timecop.freeze(Time.current) do - pm = build(:pro_membership, expires_at: 5.seconds.ago) - expect(pm.expired?).to be(true) - end - end - end - - describe "#active?" do - it "returns true if expires_at is in the future" do - Timecop.freeze(Time.current) do - pm = build(:pro_membership, expires_at: 5.seconds.from_now) - expect(pm.active?).to be(true) - end - end - - it "returns false if expires_at is in the past" do - Timecop.freeze(Time.current) do - pm = build(:pro_membership, expires_at: 5.seconds.ago) - expect(pm.active?).to be(false) - end - end - end - - describe "#expire!" do - let(:pro_membership) { create(:pro_membership) } - - it "sets expires_at to the current time" do - Timecop.freeze(Time.current) do - pro_membership.expire! - expect(pro_membership.reload.expires_at.to_i).to eq(Time.current.to_i) - end - end - - it "sets status to expired" do - Timecop.freeze(Time.current) do - pro_membership.expire! - expect(pro_membership.reload.status).to eq("expired") - end - end - - it "makes the membership expired" do - Timecop.freeze(Time.current) do - pro_membership.expire! - expect(pro_membership.reload.expired?).to be(true) - end - end - end - - describe "#renew!" do - let(:pro_membership) { create(:pro_membership) } - - it "sets expires_at to a month from now" do - Timecop.freeze(Time.current) do - pro_membership.renew! - expect(pro_membership.reload.expires_at.to_i).to eq(1.month.from_now.to_i) - end - end - - it "sets status to active" do - Timecop.freeze(Time.current) do - pro_membership.renew! - expect(pro_membership.reload.status).to eq("active") - end - end - - it "sets expiration_notification_at to nil" do - Timecop.freeze(Time.current) do - pro_membership.update_column(:expiration_notification_at, Time.current) - pro_membership.renew! - expect(pro_membership.reload.expiration_notification_at).to be(nil) - end - end - - it "sets expiration_notifications_count to 0" do - Timecop.freeze(Time.current) do - pro_membership.update_column(:expiration_notifications_count, 1) - pro_membership.renew! - expect(pro_membership.reload.expiration_notifications_count).to eq(0) - end - end - - it "makes the membership active" do - Timecop.freeze(Time.current) do - pro_membership.renew! - expect(pro_membership.reload.active?).to be(true) - end - end - end -end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index a764e14de..f10f385fc 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -141,7 +141,6 @@ RSpec.describe User, type: :model do end it { is_expected.to have_one(:counters).class_name("UserCounter").dependent(:destroy) } - it { is_expected.to have_one(:pro_membership).dependent(:destroy) } it { is_expected.not_to allow_value("#xyz").for(:bg_color_hex) } it { is_expected.not_to allow_value("#xyz").for(:text_color_hex) } it { is_expected.not_to allow_value("AcMe_1%").for(:username) } @@ -975,7 +974,7 @@ RSpec.describe User, type: :model do describe "theming properties" do it "creates proper body class with defaults" do - expect(user.decorate.config_body_class).to eq("default default-article-body pro-status-#{user.pro?} trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config") + expect(user.decorate.config_body_class).to eq("default default-article-body trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config") end it "determines dark theme if night theme" do @@ -995,22 +994,22 @@ RSpec.describe User, type: :model do it "creates proper body class with sans serif config" do user.config_font = "sans_serif" - expect(user.decorate.config_body_class).to eq("default sans-serif-article-body pro-status-#{user.pro?} trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config") + expect(user.decorate.config_body_class).to eq("default sans-serif-article-body trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config") end it "creates proper body class with open dyslexic config" do user.config_font = "open_dyslexic" - expect(user.decorate.config_body_class).to eq("default open-dyslexic-article-body pro-status-#{user.pro?} trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config") + expect(user.decorate.config_body_class).to eq("default open-dyslexic-article-body trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config") end it "creates proper body class with night theme" do user.config_theme = "night_theme" - expect(user.decorate.config_body_class).to eq("night-theme default-article-body pro-status-#{user.pro?} trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config") + expect(user.decorate.config_body_class).to eq("night-theme default-article-body trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config") end it "creates proper body class with pink theme" do user.config_theme = "pink_theme" - expect(user.decorate.config_body_class).to eq("pink-theme default-article-body pro-status-#{user.pro?} trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config") + expect(user.decorate.config_body_class).to eq("pink-theme default-article-body trusted-status-#{user.trusted} #{user.config_navbar}-navbar-config") end end @@ -1068,19 +1067,6 @@ RSpec.describe User, type: :model do user.add_role(:pro) expect(user.pro?).to be(true) end - - it "returns true if the user has an active pro membership" do - user.pro_membership = build(:pro_membership, status: "active") - expect(user.pro?).to be(true) - end - - it "returns false if the user has an expired pro membership" do - Timecop.freeze(Time.current) do - membership = create(:pro_membership, user: user) - membership.expire! - expect(user.pro?).to be(false) - end - end end describe "#enough_credits?" do diff --git a/spec/requests/credits_spec.rb b/spec/requests/credits_spec.rb index 199f49256..f809ba4bd 100644 --- a/spec/requests/credits_spec.rb +++ b/spec/requests/credits_spec.rb @@ -96,19 +96,6 @@ RSpec.describe "Credits", type: :request do expect(response.body).to include("Purchase history") expect(response.body).to include("Miscellaneous items") end - - it "shows a pro membership purchase" do - pro_membership = create(:pro_membership, user: user) - purchase_params = { user: user, purchase_type: pro_membership.class.name, purchase_id: pro_membership.id } - create(:credit, params.merge(purchase_params)) - - sign_in user - get credits_path - - expect(response.body).to include("Purchase history") - expect(response.body).to include("Pro Membership") - expect(response.body).to include(pro_membership_path) - end end end diff --git a/spec/requests/dashboard_spec.rb b/spec/requests/dashboard_spec.rb index fecf861c6..694f7bf3f 100644 --- a/spec/requests/dashboard_spec.rb +++ b/spec/requests/dashboard_spec.rb @@ -216,15 +216,6 @@ RSpec.describe "Dashboards", type: :request do end end - context "when user has a pro membership" do - it "shows page properly" do - create(:pro_membership, user: user) - sign_in user - get "/dashboard/pro" - expect(response.body).to include("pro") - end - end - context "when user has pro permission and is an org admin" do it "shows page properly" do org = create :organization diff --git a/spec/requests/pro_memberships_spec.rb b/spec/requests/pro_memberships_spec.rb deleted file mode 100644 index 34b62ec38..000000000 --- a/spec/requests/pro_memberships_spec.rb +++ /dev/null @@ -1,173 +0,0 @@ -require "rails_helper" - -RSpec.describe "Pro Memberships", type: :request do - describe "GET /pro" do - it "returns Pro landing page" do - get pro_membership_path - expect(response.body).to include("Like a Pro") - end - end - - describe "POST /pro" do - let(:user) { create(:user) } - - context "when the user is not logged in" do - it "redirects to the sign up page" do - post pro_membership_path - expect(response).to redirect_to(sign_up_path) - end - end - - context "when the user is logged in and already has a pro memberships" do - before do - sign_in user - end - - it "does not authorize creation if it has an active membership" do - create(:pro_membership, user: user) - expect do - post pro_membership_path - end.to raise_error(Pundit::NotAuthorizedError) - end - - it "does not authorize creation if it has an expired membership" do - Timecop.freeze(Time.current) do - membership = create(:pro_membership, user: user) - membership.expire! - - expect do - post pro_membership_path - end.to raise_error(Pundit::NotAuthorizedError) - end - end - - it "does not authorize creation if the user as a pro role" do - user.add_role(:pro) - expect do - post pro_membership_path - end.to raise_error(Pundit::NotAuthorizedError) - end - end - - context "when the user is logged in without a pro membership and enough credits" do - before do - sign_in user - create_list(:credit, ProMembership::MONTHLY_COST, user: user) - end - - it "creates an active pro membership" do - expect do - post pro_membership_path - end.to change(ProMembership, :count).by(1) - expect(user.reload.pro_membership.active?).to be(true) - end - - it "buys the pro membership with the correct amount of credits" do - expect do - post pro_membership_path - end.to change(user.credits.spent, :count).by(ProMembership::MONTHLY_COST) - end - - it "enqueues a job to bust the user's cache" do - ActiveJob::Base.queue_adapter.enqueued_jobs.clear # make sure it hasn't been previously queued - sidekiq_assert_enqueued_with(job: Users::BustCacheWorker, args: [user.id]) do - post pro_membership_path - end - end - - it "enqueues a job to bust the user's articles caches" do - sidekiq_assert_enqueued_with( - job: Users::ResaveArticlesWorker, - args: [user.id], - queue: "medium_priority", - ) do - post pro_membership_path - end - end - - it "adds the user to the pro members channel" do - create(:chat_channel, channel_type: "invite_only", slug: "pro-members") - post pro_membership_path - expect(user.reload.chat_channels.exists?(slug: "pro-members")).to be(true) - end - - it "redirects to the pro membership settings page with a notice" do - post pro_membership_path - expect(response).to redirect_to(user_settings_path("pro-membership")) - expect(flash[:settings_notice]).to eq("You are now a Pro!") - end - end - - context "when the user is logged in without a pro membership and not enough credits" do - before do - sign_in user - end - - it "does not create an active pro membership" do - expect do - post pro_membership_path - end.to change(ProMembership, :count).by(0) - end - - it "does not subtract credits" do - expect do - post pro_membership_path - end.to change(user.credits.spent, :count).by(0) - end - - it "redirects to the pro membership settings page with an error message" do - post pro_membership_path - expect(response).to redirect_to(user_settings_path("pro-membership")) - expect(flash[:error]).to eq("You don't have enough credits!") - end - end - end - - describe "PUT /pro" do - let(:user) { create(:user) } - - context "when the user is not logged in" do - it "redirects to the sign up page" do - put pro_membership_path - expect(response).to redirect_to(sign_up_path) - end - end - - context "when the user is logged in without a pro membership" do - before do - sign_in user - end - - it "does not authorize the operation" do - expect do - put pro_membership_path - end.to raise_error(Pundit::NotAuthorizedError) - end - end - - context "when the user is logged in with a pro membership" do - before do - sign_in user - end - - it "works correctly" do - create(:pro_membership, user: user) - put pro_membership_path, params: { pro_membership: { auto_recharge: true } } - expect(flash[:settings_notice]).to eq("Your membership has been updated!") - expect(response).to redirect_to(user_settings_path("pro-membership")) - end - - it "activates auto recharge" do - pro_membership = create(:pro_membership, user: user) - put pro_membership_path, params: { pro_membership: { auto_recharge: true } } - expect(pro_membership.reload.auto_recharge).to be(true) - end - - it "deactivates auto recharge" do - pro_membership = create(:pro_membership, user: user, auto_recharge: true) - put pro_membership_path, params: { pro_membership: { auto_recharge: false } } - expect(pro_membership.reload.auto_recharge).to be(false) - end - end - end -end diff --git a/spec/services/pro_memberships/biller_spec.rb b/spec/services/pro_memberships/biller_spec.rb deleted file mode 100644 index 1fd6c1701..000000000 --- a/spec/services/pro_memberships/biller_spec.rb +++ /dev/null @@ -1,257 +0,0 @@ -require "rails_helper" - -RSpec.describe ProMemberships::Biller, type: :service do - def format_date(datetime) - # PostgreSQL DATE(..) function uses UTC. - datetime.utc.to_date.iso8601 - end - - context "when there are expiring memberships with enough credits" do - let(:pro_membership) { create(:pro_membership) } - let(:user) { pro_membership.user } - - before do - create_list(:credit, ProMembership::MONTHLY_COST, user: user) - end - - it "renews the membership" do - Timecop.travel(format_date(pro_membership.expires_at)) do - described_class.call - pro_membership.reload - expect(pro_membership.expires_at.to_i).to eq(1.month.from_now.to_i) - expect(pro_membership.status).to eq("active") - end - end - - it "subtracts the correct amount of credits" do - Timecop.travel(format_date(pro_membership.expires_at)) do - expect do - described_class.call - end.to change(user.credits.spent, :size).by(ProMembership::MONTHLY_COST) - end - end - - it "adds the user back to the pro members chat channel" do - create(:chat_channel, slug: "pro-members", channel_type: "invite_only") - - Timecop.travel(format_date(pro_membership.expires_at)) do - described_class.call - expect(user.reload.chat_channels.exists?(slug: "pro-members")).to be(true) - end - end - - it "does not fail if the user is already in the pro members chat channel" do - cc = create(:chat_channel, slug: "pro-members", channel_type: "invite_only") - cc.add_users(user) - - allow(Rails.logger).to receive(:error) - Timecop.travel(format_date(pro_membership.expires_at)) do - described_class.call - expect(Rails.logger).not_to have_received(:error) - expect(user.reload.chat_channels.exists?(slug: "pro-members")).to be(true) - end - end - - it "enqueues a job to bust the users caches" do - ActiveJob::Base.queue_adapter.enqueued_jobs.clear # make sure it hasn't been previously queued - Timecop.travel(format_date(pro_membership.expires_at)) do - sidekiq_assert_enqueued_with(job: Users::BustCacheWorker, args: [user.id]) do - described_class.call - end - end - end - - it "enqueues a job to bust the users articles caches" do - Timecop.travel(format_date(pro_membership.expires_at)) do - sidekiq_assert_enqueued_with( - job: Users::ResaveArticlesWorker, - args: [user.id], - queue: "medium_priority", - ) do - described_class.call - end - end - end - - context "when an error occurs" do - it "does not renew the membership" do - Timecop.travel(format_date(pro_membership.expires_at)) do - pro_membership.expire! - allow(Credits::Buyer).to receive(:call).and_raise(StandardError) - described_class.call - expect(pro_membership.expires_at.to_i).to be(Time.current.to_i) - expect(pro_membership.status).to eq("expired") - end - end - - it "does not subtract credits" do - Timecop.travel(format_date(pro_membership.expires_at)) do - allow(Credits::Buyer).to receive(:call).and_raise(StandardError) - expect do - described_class.call - end.to change(user.credits.spent, :size).by(0) - end - end - - it "notifies the admins about the error" do - Timecop.travel(format_date(pro_membership.expires_at)) do - allow(Credits::Buyer).to receive(:call).and_raise(StandardError) - sidekiq_assert_enqueued_with(job: Slack::Messengers::Worker) do - described_class.call - end - end - end - end - end - - context "when there are expiring memberships with insufficient credits" do - let(:pro_membership) { create(:pro_membership) } - let(:user) { pro_membership.user } - - it "expires the membership" do - Timecop.travel(format_date(pro_membership.expires_at)) do - described_class.call - pro_membership.reload - expect(pro_membership.expired?).to be(true) - expect(pro_membership.status).to eq("expired") - end - end - - it "removes the user from the pro members chat channel" do - cc = create(:chat_channel, slug: "pro-members", channel_type: "invite_only") - cc.add_users(user) - - Timecop.travel(format_date(pro_membership.expires_at)) do - described_class.call - expect(user.reload.chat_channels.exists?(slug: "pro-members")).to be(false) - end - end - - it "notifies the admins about the expiration" do - Timecop.travel(format_date(pro_membership.expires_at)) do - sidekiq_assert_enqueued_with(job: Slack::Messengers::Worker) do - described_class.call - end - end - end - - it "enqueues a job to bust the users caches" do - ActiveJob::Base.queue_adapter.enqueued_jobs.clear # make sure it hasn't been previously queued - Timecop.travel(format_date(pro_membership.expires_at)) do - sidekiq_assert_enqueued_with(job: Users::BustCacheWorker, args: [user.id]) do - described_class.call - end - end - end - - it "enqueues a job to bust the users articles caches" do - Timecop.travel(format_date(pro_membership.expires_at)) do - sidekiq_assert_enqueued_with( - job: Users::ResaveArticlesWorker, - args: [user.id], - queue: "medium_priority", - ) do - described_class.call - end - end - end - end - - context "when there are expiring memberships with insufficient credits and auto recharge" do - let(:pro_membership) { create(:pro_membership, auto_recharge: true) } - let(:user) { pro_membership.user } - - context "when the user has an associated customer" do - before do - StripeMock.start - customer = Payments::Customer.create - user.update_columns(stripe_id_code: customer.id) - end - - after do - StripeMock.stop - end - - it "charges the customer" do - customer = Payments::Customer.get(user.stripe_id_code) - allow(Payments::Customer).to receive(:charge) - Timecop.travel(format_date(pro_membership.expires_at)) do - described_class.call - end - - expect(Payments::Customer).to have_received(:charge).with( - customer: customer, - amount: ProMembership::MONTHLY_COST_USD, - description: "Purchase of 5 credits.", - ) - end - - it "adds the correct amount of credits" do - Timecop.travel(format_date(pro_membership.expires_at)) do - # we cannot use "expect.to change" because of how activerecord-import works - old_num_credits = user.credits.size - described_class.call - expect(user.reload.credits.size).to be(old_num_credits + ProMembership::MONTHLY_COST) - end - end - - it "renews the membership" do - Timecop.travel(format_date(pro_membership.expires_at)) do - described_class.call - pro_membership.reload - expect(pro_membership.expires_at.to_i).to eq(1.month.from_now.to_i) - expect(pro_membership.status).to eq("active") - end - end - - it "spends the correct amount of credits" do - Timecop.travel(format_date(pro_membership.expires_at)) do - # we cannot use "expect.to change" because of how activerecord-import works - old_num_credits = user.reload.credits.spent.size - described_class.call - expect(user.reload.credits.spent.size).to eq(old_num_credits + ProMembership::MONTHLY_COST) - end - end - - it "enqueues a job to bust the users caches" do - ActiveJob::Base.queue_adapter.enqueued_jobs.clear # make sure it hasn't been previously queued - Timecop.travel(format_date(pro_membership.expires_at)) do - sidekiq_assert_enqueued_with(job: Users::BustCacheWorker, args: [user.id]) do - described_class.call - end - end - end - - it "enqueues a job to bust the users articles caches" do - Timecop.travel(format_date(pro_membership.expires_at)) do - sidekiq_assert_enqueued_with( - job: Users::ResaveArticlesWorker, - args: [user.id], - queue: "medium_priority", - ) do - described_class.call - end - end - end - end - - context "when the user has no associated customer" do - it "notifies the admins about the problem" do - allow(user).to receive(:stripe_id_code).and_return(nil) - Timecop.travel(format_date(pro_membership.expires_at)) do - sidekiq_assert_enqueued_with(job: Slack::Messengers::Worker) do - described_class.call - end - end - end - - it "does not change the number of credits" do - Timecop.travel(format_date(pro_membership.expires_at)) do - expect do - described_class.call - end.to change(user.credits, :size).by(0) - end - end - end - end -end diff --git a/spec/services/pro_memberships/expiration_notifier_spec.rb b/spec/services/pro_memberships/expiration_notifier_spec.rb deleted file mode 100644 index fa1a36b5a..000000000 --- a/spec/services/pro_memberships/expiration_notifier_spec.rb +++ /dev/null @@ -1,66 +0,0 @@ -require "rails_helper" - -RSpec.describe ProMemberships::ExpirationNotifier, type: :service do - context "when there are expiring memberships with enough credits" do - let(:pro_membership) { create(:pro_membership) } - let(:user) { pro_membership.user } - - it "does not deliver an email to the user" do - create_list(:credit, ProMembership::MONTHLY_COST, user: user) - Timecop.travel(pro_membership.expires_at - 1.week) do - assert_emails 0 do - described_class.call(1.week.from_now) - end - end - end - end - - context "when there are expiring memberships with insufficient credits" do - let(:pro_membership) { create(:pro_membership) } - - it "delivers an email to the user" do - Timecop.travel(pro_membership.expires_at - 1.week) do - assert_emails 1 do - described_class.call(1.week.from_now) - end - end - end - - it "sets the expiration notification datetime" do - Timecop.freeze(pro_membership.expires_at - 1.week) do - described_class.call(1.week.from_now) - expect(pro_membership.reload.expiration_notification_at.to_i).to eq(Time.current.to_i) - end - end - - it "increments the notifications count" do - Timecop.travel(pro_membership.expires_at - 1.week) do - expect(pro_membership.expiration_notifications_count).to eq(0) - described_class.call(1.week.from_now) - expect(pro_membership.reload.expiration_notifications_count).to eq(1) - end - end - - it "enqueus a slack bot ping job" do - Timecop.travel(pro_membership.expires_at - 1.week) do - sidekiq_assert_enqueued_with(job: Slack::Messengers::Worker) do - described_class.call(1.week.from_now) - end - end - end - end - - context "when there are expiring memberships with insufficient credits and auto recharge" do - let(:pro_membership) { create(:pro_membership) } - let(:user) { pro_membership.user } - - it "does not deliver an email to the user" do - pro_membership.update_columns(auto_recharge: true) - Timecop.travel(pro_membership.expires_at - 1.week) do - assert_emails 0 do - described_class.call(1.week.from_now) - end - end - end - end -end diff --git a/spec/system/pro_memberships/user_creates_a_pro_membership_spec.rb b/spec/system/pro_memberships/user_creates_a_pro_membership_spec.rb deleted file mode 100644 index 23e2025cb..000000000 --- a/spec/system/pro_memberships/user_creates_a_pro_membership_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require "rails_helper" - -RSpec.describe "Creates a Pro Membership", type: :system do - let(:user) { create(:user) } - - context "when signed in as a regular user with enough credits" do - before do - create_list(:credit, 5, user: user) - sign_in user - end - - it "makes the user become a pro" do - visit "/settings/pro-membership" - label = "Become a Pro member" - click_on label - - expect(page).to have_content("You are now a Pro!") - end - end -end diff --git a/spec/system/pro_memberships/user_visits_pro_page_spec.rb b/spec/system/pro_memberships/user_visits_pro_page_spec.rb deleted file mode 100644 index 89a5dc8f5..000000000 --- a/spec/system/pro_memberships/user_visits_pro_page_spec.rb +++ /dev/null @@ -1,69 +0,0 @@ -require "rails_helper" - -RSpec.describe "Visits Pro Memberships page", type: :system do - let(:user) { create(:user) } - - context "when not signed in" do - it "does not say you are a member" do - visit "/pro" - expect(page).not_to have_content("View your Pro Membership") - end - - it "does not ask to become a pro member" do - visit "/pro" - - expect(page).not_to have_content("Become a Pro member") - end - end - - context "when signed in as a regular user" do - before do - sign_in user - end - - it "does not say you are a member" do - visit "/pro" - expect(page).not_to have_content("View your Pro Membership") - end - - it "asks to become a pro member" do - visit "/pro" - - expect(page).to have_content("Become a Pro member") - end - end - - context "when signed in as as user with a pro role" do - before do - user.add_role(:pro) - sign_in user - end - - it "says you are a member" do - visit "/pro" - expect(page).to have_content("View your Pro Membership") - end - - it "does not ask to become a pro member" do - visit "/pro" - expect(page).not_to have_content("Become a Pro member") - end - end - - context "when signed in as as user with a pro membership" do - before do - create(:pro_membership, user: user, expires_at: 1.month.from_now) - sign_in user - end - - it "says you are a member" do - visit "/pro" - expect(page).to have_content("View your Pro Membership") - end - - it "does not ask to become a pro member" do - visit "/pro" - expect(page).not_to have_content("Become a Pro member") - end - end -end diff --git a/spec/system/pro_memberships/user_visits_settings_pro_membership_page_spec.rb b/spec/system/pro_memberships/user_visits_settings_pro_membership_page_spec.rb deleted file mode 100644 index 309bbd330..000000000 --- a/spec/system/pro_memberships/user_visits_settings_pro_membership_page_spec.rb +++ /dev/null @@ -1,90 +0,0 @@ -require "rails_helper" - -RSpec.describe "Visits Pro Membership settings page", type: :system do - let(:user) { create(:user) } - let(:cost) { ProMembership::MONTHLY_COST } - - context "when signed in as a regular user" do - before do - sign_in user - end - - it "contains the correct info if you have no credits" do - visit "/settings/pro-membership" - - expect(page).to have_content("Pro Membership is #{cost} credits per month") - expect(page).to have_content("You currently have no credits, you need #{cost} to become a Pro member") - expect(page).to have_link("Purchase credits", href: "/credits/purchase") - expect(page).to have_link("Pro Membership page", href: "/pro") - end - - it "contains the correct info if you have some credits but not enough" do - create(:credit, user: user) - - visit "/settings/pro-membership" - expect(page).to have_content("Pro Membership is #{cost} credits per month") - expect(page).to have_content( - "You currently have #{cost - 1} credits, you need #{cost} to become a Pro member", - ) - expect(page).to have_link("Purchase credits", href: "/credits/purchase") - expect(page).to have_link("Pro Membership page", href: "/pro") - end - - it "contains the correct info if you have enough credits" do - create_list(:credit, cost, user: user) - - visit "/settings/pro-membership" - expect(page).to have_content("Pro Membership is #{cost} credits per month") - expect(page).to have_content("You currently have #{user.credits.unspent.size} available credits") - expect(page).not_to have_link("Purchase credits", href: "/credits/purchase") - expect(page).to have_selector("input[type=submit][value='Become a Pro member']") - expect(page).to have_link("Pro Membership page", href: "/pro") - end - end - - context "when signed in as as user with a pro role" do - before do - user.add_role(:pro) - sign_in user - end - - it "shows the status of the membership", js: true, percy: true do - visit "/settings/pro-membership" - - Percy.snapshot(page, name: "Settings: /settings/pro-membership renders for pro role") - - expect(page).to have_content("Status") - expect(page).to have_content("Expiration date") - expect(page).to have_content("Never") - expect(page).to have_link("Pro Membership page", href: "/pro") - end - end - - context "when signed in as as user with a pro membership" do - before do - create(:pro_membership, user: user, expires_at: 1.month.from_now) - sign_in user - end - - it "shows the status of the membership", js: true, percy: true do - visit "/settings/pro-membership" - - Percy.snapshot(page, name: "Settings: /settings/pro-membership renders for pro membership") - - expect(page).to have_content("Status") - expect(page).to have_content("Expiration date") - expect(page).to have_content(user.pro_membership.expires_at.to_date.to_s(:long)) - expect(page).to have_content("Top up from credit card?") - expect(page).to have_selector("input[type=submit]") - expect(page).to have_link("Pro Membership page", href: "/pro") - end - - it "updates the auto recharge" do - visit "/settings/pro-membership" - - check "pro_membership[auto_recharge]" - click_on "SUBMIT" - expect(page).to have_content("Your membership has been updated!") - end - end -end