mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 05:47:45 +08:00
DEV: Move non scheduled problem checks to classes (#26122)
In AdminDashboardData we have a bunch of problem checks implemented as methods on that class. This PR absolves it of the responsibility by promoting each of those checks to a first class ProblemCheck. This way each of them can have their own priority and arbitrary functionality can be isolated in its own class. Think "extract class" refactoring over and over. Since they were all moved we can also get rid of the @@problem_syms class variable which was basically the old version of the registry now replaced by ProblemCheck.realtime. In addition AdminDashboardData::Problem value object has been entirely replaced with the new ProblemCheck::Problem (with compatible API). Lastly, I added some RSpec matchers to simplify testing of problem checks and provide helpful error messages when assertions fail.
This commit is contained in:
parent
9afb0b29f8
commit
ea5c3a3bdc
|
@ -3,33 +3,7 @@
|
|||
class AdminDashboardData
|
||||
include StatsCacheable
|
||||
|
||||
cattr_reader :problem_syms, :problem_blocks, :problem_messages
|
||||
|
||||
class Problem
|
||||
VALID_PRIORITIES = %w[low high].freeze
|
||||
|
||||
attr_reader :message, :priority, :identifier
|
||||
|
||||
def initialize(message, priority: "low", identifier: nil)
|
||||
@message = message
|
||||
@priority = VALID_PRIORITIES.include?(priority) ? priority : "low"
|
||||
@identifier = identifier
|
||||
end
|
||||
|
||||
def to_s
|
||||
@message
|
||||
end
|
||||
|
||||
def to_h
|
||||
{ message: message, priority: priority, identifier: identifier }
|
||||
end
|
||||
|
||||
def self.from_h(h)
|
||||
h = h.with_indifferent_access
|
||||
return if h[:message].blank?
|
||||
new(h[:message], priority: h[:priority], identifier: h[:identifier])
|
||||
end
|
||||
end
|
||||
cattr_reader :problem_blocks, :problem_messages
|
||||
|
||||
# kept for backward compatibility
|
||||
GLOBAL_REPORTS ||= []
|
||||
|
@ -51,18 +25,16 @@ class AdminDashboardData
|
|||
|
||||
def problems
|
||||
problems = []
|
||||
self.class.problem_syms.each do |sym|
|
||||
message = public_send(sym)
|
||||
problems << Problem.new(message) if message.present?
|
||||
end
|
||||
self.class.problem_blocks.each do |blk|
|
||||
message = instance_exec(&blk)
|
||||
problems << Problem.new(message) if message.present?
|
||||
problems << ProblemCheck::Problem.new(message) if message.present?
|
||||
end
|
||||
self.class.problem_messages.each do |i18n_key|
|
||||
message = self.class.problem_message_check(i18n_key)
|
||||
problems << Problem.new(message) if message.present?
|
||||
problems << ProblemCheck::Problem.new(message) if message.present?
|
||||
end
|
||||
problems.concat(ProblemCheck.realtime.flat_map { |c| c.call(@opts).map(&:to_h) })
|
||||
|
||||
problems += self.class.load_found_scheduled_check_problems
|
||||
problems.compact!
|
||||
|
||||
|
@ -76,7 +48,6 @@ class AdminDashboardData
|
|||
end
|
||||
|
||||
def self.add_problem_check(*syms, &blk)
|
||||
@@problem_syms.push(*syms) if syms
|
||||
@@problem_blocks << blk if blk
|
||||
end
|
||||
|
||||
|
@ -109,7 +80,7 @@ class AdminDashboardData
|
|||
|
||||
found_problems.filter_map do |problem|
|
||||
begin
|
||||
Problem.from_h(JSON.parse(problem))
|
||||
ProblemCheck::Problem.from_h(JSON.parse(problem))
|
||||
rescue JSON::ParserError => err
|
||||
Discourse.warn_exception(
|
||||
err,
|
||||
|
@ -130,7 +101,6 @@ class AdminDashboardData
|
|||
# tests. It will also fire multiple times in development mode because
|
||||
# classes are not cached.
|
||||
def self.reset_problem_checks
|
||||
@@problem_syms = []
|
||||
@@problem_blocks = []
|
||||
|
||||
@@problem_messages = %w[
|
||||
|
@ -139,26 +109,6 @@ class AdminDashboardData
|
|||
dashboard.poll_pop3_auth_error
|
||||
]
|
||||
|
||||
add_problem_check :rails_env_check,
|
||||
:host_names_check,
|
||||
:force_https_check,
|
||||
:ram_check,
|
||||
:google_oauth2_config_check,
|
||||
:facebook_config_check,
|
||||
:twitter_config_check,
|
||||
:github_config_check,
|
||||
:s3_config_check,
|
||||
:s3_cdn_check,
|
||||
:image_magick_check,
|
||||
:failing_emails_check,
|
||||
:subfolder_ends_in_slash_check,
|
||||
:email_polling_errored_recently,
|
||||
:out_of_date_themes,
|
||||
:unreachable_themes,
|
||||
:watched_words_check,
|
||||
:google_analytics_version_check,
|
||||
:translation_overrides_check
|
||||
|
||||
add_problem_check { sidekiq_check || queue_size_check }
|
||||
end
|
||||
reset_problem_checks
|
||||
|
@ -226,16 +176,6 @@ class AdminDashboardData
|
|||
"#{PROBLEM_MESSAGE_PREFIX}#{i18n_key}"
|
||||
end
|
||||
|
||||
def rails_env_check
|
||||
I18n.t("dashboard.rails_env_warning", env: Rails.env) unless Rails.env.production?
|
||||
end
|
||||
|
||||
def host_names_check
|
||||
if %w[localhost production.localhost].include?(Discourse.current_hostname)
|
||||
I18n.t("dashboard.host_names_warning")
|
||||
end
|
||||
end
|
||||
|
||||
def sidekiq_check
|
||||
last_job_performed_at = Jobs.last_job_performed_at
|
||||
if Jobs.queued > 0 && (last_job_performed_at.nil? || last_job_performed_at < 2.minutes.ago)
|
||||
|
@ -247,164 +187,4 @@ class AdminDashboardData
|
|||
queue_size = Jobs.queued
|
||||
I18n.t("dashboard.queue_size_warning", queue_size: queue_size) if queue_size >= 100_000
|
||||
end
|
||||
|
||||
def ram_check
|
||||
I18n.t("dashboard.memory_warning") if MemInfo.new.mem_total && MemInfo.new.mem_total < 950_000
|
||||
end
|
||||
|
||||
def google_oauth2_config_check
|
||||
if SiteSetting.enable_google_oauth2_logins &&
|
||||
(
|
||||
SiteSetting.google_oauth2_client_id.blank? ||
|
||||
SiteSetting.google_oauth2_client_secret.blank?
|
||||
)
|
||||
I18n.t("dashboard.google_oauth2_config_warning", base_path: Discourse.base_path)
|
||||
end
|
||||
end
|
||||
|
||||
def facebook_config_check
|
||||
if SiteSetting.enable_facebook_logins &&
|
||||
(SiteSetting.facebook_app_id.blank? || SiteSetting.facebook_app_secret.blank?)
|
||||
I18n.t("dashboard.facebook_config_warning", base_path: Discourse.base_path)
|
||||
end
|
||||
end
|
||||
|
||||
def twitter_config_check
|
||||
if SiteSetting.enable_twitter_logins &&
|
||||
(SiteSetting.twitter_consumer_key.blank? || SiteSetting.twitter_consumer_secret.blank?)
|
||||
I18n.t("dashboard.twitter_config_warning", base_path: Discourse.base_path)
|
||||
end
|
||||
end
|
||||
|
||||
def github_config_check
|
||||
if SiteSetting.enable_github_logins &&
|
||||
(SiteSetting.github_client_id.blank? || SiteSetting.github_client_secret.blank?)
|
||||
I18n.t("dashboard.github_config_warning", base_path: Discourse.base_path)
|
||||
end
|
||||
end
|
||||
|
||||
def s3_config_check
|
||||
# if set via global setting it is validated during the `use_s3?` call
|
||||
if !GlobalSetting.use_s3?
|
||||
bad_keys =
|
||||
(SiteSetting.s3_access_key_id.blank? || SiteSetting.s3_secret_access_key.blank?) &&
|
||||
!SiteSetting.s3_use_iam_profile
|
||||
|
||||
if SiteSetting.enable_s3_uploads && (bad_keys || SiteSetting.s3_upload_bucket.blank?)
|
||||
return I18n.t("dashboard.s3_config_warning", base_path: Discourse.base_path)
|
||||
end
|
||||
|
||||
if SiteSetting.backup_location == BackupLocationSiteSetting::S3 &&
|
||||
(bad_keys || SiteSetting.s3_backup_bucket.blank?)
|
||||
return I18n.t("dashboard.s3_backup_config_warning", base_path: Discourse.base_path)
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def s3_cdn_check
|
||||
if (GlobalSetting.use_s3? || SiteSetting.enable_s3_uploads) &&
|
||||
SiteSetting.Upload.s3_cdn_url.blank?
|
||||
I18n.t("dashboard.s3_cdn_warning")
|
||||
end
|
||||
end
|
||||
|
||||
def translation_overrides_check
|
||||
if TranslationOverride.exists?(status: %i[outdated invalid_interpolation_keys])
|
||||
I18n.t("dashboard.outdated_translations_warning", base_path: Discourse.base_path)
|
||||
end
|
||||
end
|
||||
|
||||
def image_magick_check
|
||||
if SiteSetting.create_thumbnails && !system("command -v convert >/dev/null;")
|
||||
I18n.t("dashboard.image_magick_warning")
|
||||
end
|
||||
end
|
||||
|
||||
def failing_emails_check
|
||||
num_failed_jobs = Jobs.num_email_retry_jobs
|
||||
if num_failed_jobs > 0
|
||||
I18n.t(
|
||||
"dashboard.failing_emails_warning",
|
||||
num_failed_jobs: num_failed_jobs,
|
||||
base_path: Discourse.base_path,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def subfolder_ends_in_slash_check
|
||||
I18n.t("dashboard.subfolder_ends_in_slash") if Discourse.base_path =~ %r{/\z}
|
||||
end
|
||||
|
||||
def google_analytics_version_check
|
||||
I18n.t("dashboard.v3_analytics_deprecated") if SiteSetting.ga_version == "v3_analytics"
|
||||
end
|
||||
|
||||
def email_polling_errored_recently
|
||||
errors = Jobs::PollMailbox.errors_in_past_24_hours
|
||||
if errors > 0
|
||||
I18n.t(
|
||||
"dashboard.email_polling_errored_recently",
|
||||
count: errors,
|
||||
base_path: Discourse.base_path,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def missing_mailgun_api_key
|
||||
return unless SiteSetting.reply_by_email_enabled
|
||||
return unless ActionMailer::Base.smtp_settings[:address]["smtp.mailgun.org"]
|
||||
return unless SiteSetting.mailgun_api_key.blank?
|
||||
I18n.t("dashboard.missing_mailgun_api_key")
|
||||
end
|
||||
|
||||
def force_https_check
|
||||
return unless @opts[:check_force_https]
|
||||
unless SiteSetting.force_https
|
||||
I18n.t("dashboard.force_https_warning", base_path: Discourse.base_path)
|
||||
end
|
||||
end
|
||||
|
||||
def watched_words_check
|
||||
WatchedWord.actions.keys.each do |action|
|
||||
begin
|
||||
WordWatcher.compiled_regexps_for_action(action, raise_errors: true)
|
||||
rescue RegexpError => e
|
||||
translated_action = I18n.t("admin_js.admin.watched_words.actions.#{action}")
|
||||
I18n.t(
|
||||
"dashboard.watched_word_regexp_error",
|
||||
base_path: Discourse.base_path,
|
||||
action: translated_action,
|
||||
)
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def out_of_date_themes
|
||||
old_themes = RemoteTheme.out_of_date_themes
|
||||
return unless old_themes.present?
|
||||
|
||||
themes_html_format(old_themes, "dashboard.out_of_date_themes")
|
||||
end
|
||||
|
||||
def unreachable_themes
|
||||
themes = RemoteTheme.unreachable_themes
|
||||
return unless themes.present?
|
||||
|
||||
themes_html_format(themes, "dashboard.unreachable_themes")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def themes_html_format(themes, i18n_key)
|
||||
html =
|
||||
themes
|
||||
.map do |name, id|
|
||||
"<li><a href=\"/admin/customize/themes/#{id}\">#{CGI.escapeHTML(name)}</a></li>"
|
||||
end
|
||||
.join("\n")
|
||||
|
||||
"#{I18n.t(i18n_key)}<ul>#{html}</ul>"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -35,6 +35,10 @@ class ProblemCheck
|
|||
checks.select(&:scheduled?)
|
||||
end
|
||||
|
||||
def self.realtime
|
||||
checks.reject(&:scheduled?)
|
||||
end
|
||||
|
||||
def self.identifier
|
||||
name.demodulize.underscore.to_sym
|
||||
end
|
||||
|
@ -45,9 +49,20 @@ class ProblemCheck
|
|||
end
|
||||
delegate :scheduled?, to: :class
|
||||
|
||||
def self.call
|
||||
new.call
|
||||
def self.realtime?
|
||||
!scheduled?
|
||||
end
|
||||
delegate :realtime?, to: :class
|
||||
|
||||
def self.call(data = {})
|
||||
new(data).call
|
||||
end
|
||||
|
||||
def initialize(data = {})
|
||||
@data = OpenStruct.new(data)
|
||||
end
|
||||
|
||||
attr_reader :data
|
||||
|
||||
def call
|
||||
raise NotImplementedError
|
||||
|
@ -55,10 +70,15 @@ class ProblemCheck
|
|||
|
||||
private
|
||||
|
||||
def problem
|
||||
def problem(override_key = nil)
|
||||
[
|
||||
Problem.new(
|
||||
I18n.t(translation_key, base_path: Discourse.base_path),
|
||||
message ||
|
||||
I18n.t(
|
||||
override_key || translation_key,
|
||||
base_path: Discourse.base_path,
|
||||
**translation_data.symbolize_keys,
|
||||
),
|
||||
priority: self.config.priority,
|
||||
identifier:,
|
||||
),
|
||||
|
@ -69,8 +89,16 @@ class ProblemCheck
|
|||
[]
|
||||
end
|
||||
|
||||
def message
|
||||
nil
|
||||
end
|
||||
|
||||
def translation_key
|
||||
# TODO: Infer a default based on class name, then move translations in locale file.
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def translation_data
|
||||
{}
|
||||
end
|
||||
end
|
||||
|
|
25
app/services/problem_check/email_polling_errored_recently.rb
Normal file
25
app/services/problem_check/email_polling_errored_recently.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProblemCheck::EmailPollingErroredRecently < ProblemCheck
|
||||
self.priority = "low"
|
||||
|
||||
def call
|
||||
return no_problem if polling_error_count.to_i == 0
|
||||
|
||||
problem
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def polling_error_count
|
||||
@polling_error_count ||= Jobs::PollMailbox.errors_in_past_24_hours
|
||||
end
|
||||
|
||||
def translation_key
|
||||
"dashboard.email_polling_errored_recently"
|
||||
end
|
||||
|
||||
def translation_data
|
||||
{ count: polling_error_count }
|
||||
end
|
||||
end
|
22
app/services/problem_check/facebook_config.rb
Normal file
22
app/services/problem_check/facebook_config.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProblemCheck::FacebookConfig < ProblemCheck
|
||||
self.priority = "low"
|
||||
|
||||
def call
|
||||
return no_problem if !SiteSetting.enable_facebook_logins
|
||||
return no_problem if facebook_credentials_present?
|
||||
|
||||
problem
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def translation_key
|
||||
"dashboard.facebook_config_warning"
|
||||
end
|
||||
|
||||
def facebook_credentials_present?
|
||||
SiteSetting.facebook_app_id.present? && SiteSetting.facebook_app_secret.present?
|
||||
end
|
||||
end
|
25
app/services/problem_check/failing_emails.rb
Normal file
25
app/services/problem_check/failing_emails.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProblemCheck::FailingEmails < ProblemCheck
|
||||
self.priority = "low"
|
||||
|
||||
def call
|
||||
return no_problem if failed_job_count.to_i == 0
|
||||
|
||||
problem
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def failed_job_count
|
||||
@failed_job_count ||= Jobs.num_email_retry_jobs
|
||||
end
|
||||
|
||||
def translation_key
|
||||
"dashboard.failing_emails_warning"
|
||||
end
|
||||
|
||||
def translation_data
|
||||
{ num_failed_jobs: failed_job_count }
|
||||
end
|
||||
end
|
18
app/services/problem_check/force_https.rb
Normal file
18
app/services/problem_check/force_https.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProblemCheck::ForceHttps < ProblemCheck
|
||||
self.priority = "low"
|
||||
|
||||
def call
|
||||
return no_problem if SiteSetting.force_https
|
||||
return no_problem if !data.check_force_https
|
||||
|
||||
problem
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def translation_key
|
||||
"dashboard.force_https_warning"
|
||||
end
|
||||
end
|
22
app/services/problem_check/github_config.rb
Normal file
22
app/services/problem_check/github_config.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProblemCheck::GithubConfig < ProblemCheck
|
||||
self.priority = "low"
|
||||
|
||||
def call
|
||||
return no_problem if !SiteSetting.enable_github_logins
|
||||
return no_problem if github_credentials_present?
|
||||
|
||||
problem
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def translation_key
|
||||
"dashboard.github_config_warning"
|
||||
end
|
||||
|
||||
def github_credentials_present?
|
||||
SiteSetting.github_client_id.present? && SiteSetting.github_client_secret.present?
|
||||
end
|
||||
end
|
17
app/services/problem_check/google_analytics_version.rb
Normal file
17
app/services/problem_check/google_analytics_version.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProblemCheck::GoogleAnalyticsVersion < ProblemCheck
|
||||
self.priority = "low"
|
||||
|
||||
def call
|
||||
return no_problem if SiteSetting.ga_version != "v3_analytics"
|
||||
|
||||
problem
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def translation_key
|
||||
"dashboard.v3_analytics_deprecated"
|
||||
end
|
||||
end
|
22
app/services/problem_check/google_oauth2_config.rb
Normal file
22
app/services/problem_check/google_oauth2_config.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProblemCheck::GoogleOauth2Config < ProblemCheck
|
||||
self.priority = "low"
|
||||
|
||||
def call
|
||||
return no_problem if !SiteSetting.enable_google_oauth2_logins
|
||||
return no_problem if google_oauth2_credentials_present?
|
||||
|
||||
problem
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def translation_key
|
||||
"dashboard.google_oauth2_config_warning"
|
||||
end
|
||||
|
||||
def google_oauth2_credentials_present?
|
||||
SiteSetting.google_oauth2_client_id.present? && SiteSetting.google_oauth2_client_secret.present?
|
||||
end
|
||||
end
|
17
app/services/problem_check/host_names.rb
Normal file
17
app/services/problem_check/host_names.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProblemCheck::HostNames < ProblemCheck
|
||||
self.priority = "low"
|
||||
|
||||
def call
|
||||
return no_problem if %w[localhost production.localhost].exclude?(Discourse.current_hostname)
|
||||
|
||||
problem
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def translation_key
|
||||
"dashboard.host_names_warning"
|
||||
end
|
||||
end
|
18
app/services/problem_check/image_magick.rb
Normal file
18
app/services/problem_check/image_magick.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProblemCheck::ImageMagick < ProblemCheck
|
||||
self.priority = "low"
|
||||
|
||||
def call
|
||||
return no_problem if !SiteSetting.create_thumbnails
|
||||
return no_problem if Kernel.system("command -v convert >/dev/null;")
|
||||
|
||||
problem
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def translation_key
|
||||
"dashboard.image_magick_warning"
|
||||
end
|
||||
end
|
19
app/services/problem_check/missing_mailgun_api_key.rb
Normal file
19
app/services/problem_check/missing_mailgun_api_key.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProblemCheck::MissingMailgunApiKey < ProblemCheck
|
||||
self.priority = "low"
|
||||
|
||||
def call
|
||||
return no_problem if !SiteSetting.reply_by_email_enabled
|
||||
return no_problem if !ActionMailer::Base.smtp_settings.dig(:address, "smtp.mailgun.org")
|
||||
return no_problem if SiteSetting.mailgun_api_key.present?
|
||||
|
||||
problem
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def translation_key
|
||||
"dashboard.missing_mailgun_api_key"
|
||||
end
|
||||
end
|
29
app/services/problem_check/out_of_date_themes.rb
Normal file
29
app/services/problem_check/out_of_date_themes.rb
Normal file
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProblemCheck::OutOfDateThemes < ProblemCheck
|
||||
self.priority = "low"
|
||||
|
||||
def call
|
||||
return no_problem if out_of_date_themes.empty?
|
||||
|
||||
problem
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def out_of_date_themes
|
||||
@out_of_date_themes ||= RemoteTheme.out_of_date_themes
|
||||
end
|
||||
|
||||
def message
|
||||
"#{I18n.t("dashboard.out_of_date_themes")}<ul>#{themes_list}</ul>"
|
||||
end
|
||||
|
||||
def themes_list
|
||||
out_of_date_themes
|
||||
.map do |name, id|
|
||||
"<li><a href=\"#{Discourse.base_path}/admin/customize/themes/#{id}\">#{CGI.escapeHTML(name)}</a></li>"
|
||||
end
|
||||
.join("\n")
|
||||
end
|
||||
end
|
21
app/services/problem_check/rails_env.rb
Normal file
21
app/services/problem_check/rails_env.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProblemCheck::RailsEnv < ProblemCheck
|
||||
self.priority = "low"
|
||||
|
||||
def call
|
||||
return no_problem if Rails.env.production?
|
||||
|
||||
problem
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def translation_key
|
||||
"dashboard.rails_env_warning"
|
||||
end
|
||||
|
||||
def translation_data
|
||||
{ env: Rails.env }
|
||||
end
|
||||
end
|
20
app/services/problem_check/ram.rb
Normal file
20
app/services/problem_check/ram.rb
Normal file
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProblemCheck::Ram < ProblemCheck
|
||||
self.priority = "low"
|
||||
|
||||
def call
|
||||
available_memory = MemInfo.new
|
||||
|
||||
return no_problem if available_memory.unknown?
|
||||
return no_problem if available_memory.mem_total > 950_000
|
||||
|
||||
problem
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def translation_key
|
||||
"dashboard.memory_warning"
|
||||
end
|
||||
end
|
25
app/services/problem_check/s3_backup_config.rb
Normal file
25
app/services/problem_check/s3_backup_config.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProblemCheck::S3BackupConfig < ProblemCheck
|
||||
self.priority = "low"
|
||||
|
||||
def call
|
||||
return no_problem if GlobalSetting.use_s3?
|
||||
return no_problem if SiteSetting.backup_location != BackupLocationSiteSetting::S3
|
||||
return no_problem if !missing_keys? && SiteSetting.s3_backup_bucket.present?
|
||||
|
||||
problem
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def missing_keys?
|
||||
return false if SiteSetting.s3_use_iam_profile
|
||||
|
||||
SiteSetting.s3_access_key_id.blank? || SiteSetting.s3_secret_access_key.blank?
|
||||
end
|
||||
|
||||
def translation_key
|
||||
"dashboard.s3_backup_config_warning"
|
||||
end
|
||||
end
|
18
app/services/problem_check/s3_cdn.rb
Normal file
18
app/services/problem_check/s3_cdn.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProblemCheck::S3Cdn < ProblemCheck
|
||||
self.priority = "low"
|
||||
|
||||
def call
|
||||
return no_problem if !GlobalSetting.use_s3? && !SiteSetting.enable_s3_uploads?
|
||||
return no_problem if SiteSetting.Upload.s3_cdn_url.present?
|
||||
|
||||
problem
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def translation_key
|
||||
"dashboard.s3_cdn_warning"
|
||||
end
|
||||
end
|
25
app/services/problem_check/s3_upload_config.rb
Normal file
25
app/services/problem_check/s3_upload_config.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProblemCheck::S3UploadConfig < ProblemCheck
|
||||
self.priority = "low"
|
||||
|
||||
def call
|
||||
return no_problem if GlobalSetting.use_s3?
|
||||
return no_problem if !SiteSetting.enable_s3_uploads?
|
||||
return no_problem if !missing_keys? && SiteSetting.s3_upload_bucket.present?
|
||||
|
||||
problem
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def missing_keys?
|
||||
return false if SiteSetting.s3_use_iam_profile
|
||||
|
||||
SiteSetting.s3_access_key_id.blank? || SiteSetting.s3_secret_access_key.blank?
|
||||
end
|
||||
|
||||
def translation_key
|
||||
"dashboard.s3_config_warning"
|
||||
end
|
||||
end
|
17
app/services/problem_check/subfolder_ends_in_slash.rb
Normal file
17
app/services/problem_check/subfolder_ends_in_slash.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProblemCheck::SubfolderEndsInSlash < ProblemCheck
|
||||
self.priority = "low"
|
||||
|
||||
def call
|
||||
return no_problem if !Discourse.base_path.end_with?("/")
|
||||
|
||||
problem
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def translation_key
|
||||
"dashboard.subfolder_ends_in_slash"
|
||||
end
|
||||
end
|
19
app/services/problem_check/translation_overrides.rb
Normal file
19
app/services/problem_check/translation_overrides.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProblemCheck::TranslationOverrides < ProblemCheck
|
||||
self.priority = "low"
|
||||
|
||||
def call
|
||||
if !TranslationOverride.exists?(status: %i[outdated invalid_interpolation_keys])
|
||||
return no_problem
|
||||
end
|
||||
|
||||
problem
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def translation_key
|
||||
"dashboard.outdated_translations_warning"
|
||||
end
|
||||
end
|
22
app/services/problem_check/twitter_config.rb
Normal file
22
app/services/problem_check/twitter_config.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProblemCheck::TwitterConfig < ProblemCheck
|
||||
self.priority = "low"
|
||||
|
||||
def call
|
||||
return no_problem if !SiteSetting.enable_twitter_logins
|
||||
return no_problem if twitter_credentials_present?
|
||||
|
||||
problem
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def translation_key
|
||||
"dashboard.twitter_config_warning"
|
||||
end
|
||||
|
||||
def twitter_credentials_present?
|
||||
SiteSetting.twitter_consumer_key.present? && SiteSetting.twitter_consumer_secret.present?
|
||||
end
|
||||
end
|
29
app/services/problem_check/unreachable_themes.rb
Normal file
29
app/services/problem_check/unreachable_themes.rb
Normal file
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProblemCheck::UnreachableThemes < ProblemCheck
|
||||
self.priority = "low"
|
||||
|
||||
def call
|
||||
return no_problem if unreachable_themes.empty?
|
||||
|
||||
problem
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def unreachable_themes
|
||||
@unreachable_themes ||= RemoteTheme.unreachable_themes
|
||||
end
|
||||
|
||||
def message
|
||||
"#{I18n.t("dashboard.unreachable_themes")}<ul>#{themes_list}</ul>"
|
||||
end
|
||||
|
||||
def themes_list
|
||||
unreachable_themes
|
||||
.map do |name, id|
|
||||
"<li><a href=\"/admin/customize/themes/#{id}\">#{CGI.escapeHTML(name)}</a></li>"
|
||||
end
|
||||
.join("\n")
|
||||
end
|
||||
end
|
31
app/services/problem_check/watched_words.rb
Normal file
31
app/services/problem_check/watched_words.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ProblemCheck::WatchedWords < ProblemCheck
|
||||
self.priority = "low"
|
||||
|
||||
def call
|
||||
return no_problem if invalid_regexp_actions.empty?
|
||||
|
||||
problem
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def translation_key
|
||||
"dashboard.watched_word_regexp_error"
|
||||
end
|
||||
|
||||
def translation_data
|
||||
{ action: invalid_regexp_actions.map { |w| "'#{w}'" }.join(", ") }
|
||||
end
|
||||
|
||||
def invalid_regexp_actions
|
||||
@invalid_regexp_actions ||=
|
||||
WatchedWord.actions.keys.filter_map do |action|
|
||||
WordWatcher.compiled_regexps_for_action(action, raise_errors: true)
|
||||
nil
|
||||
rescue RegexpError
|
||||
I18n.t("admin_js.admin.watched_words.actions.#{action}")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1557,7 +1557,7 @@ en:
|
|||
force_https_warning: "Your website is using SSL. But `<a href='%{base_path}/admin/site_settings/category/all_results?filter=force_https'>force_https</a>` is not yet enabled in your site settings."
|
||||
out_of_date_themes: "Updates are available for the following themes:"
|
||||
unreachable_themes: "We were unable to check for updates on the following themes:"
|
||||
watched_word_regexp_error: "The regular expression for '%{action}' watched words is invalid. Please check your <a href='%{base_path}/admin/customize/watched_words'>Watched Word settings</a>, or disable the 'watched words regular expressions' site setting."
|
||||
watched_word_regexp_error: "The regular expression for %{action} watched words is invalid. Please check your <a href='%{base_path}/admin/customize/watched_words'>Watched Word settings</a>, or disable the 'watched words regular expressions' site setting."
|
||||
v3_analytics_deprecated: "Your Discourse is currently using Google Analytics 3, which will no longer be supported after July 2023. <a href='https://meta.discourse.org/t/260498'>Upgrade to Google Analytics 4</a> now to continue receiving valuable insights and analytics for your website's performance."
|
||||
category_style_deprecated: "Your Discourse is currently using a deprecated category style which will be removed before the final beta release of Discourse 3.2. Please refer to <a href='https://meta.discourse.org/t/282441'>Moving to a Single Category Style Site Setting</a> for instructions on how to keep your selected category style."
|
||||
back_from_logster_text: "Back to site"
|
||||
|
|
|
@ -18,4 +18,8 @@ class MemInfo
|
|||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def unknown?
|
||||
mem_total.nil?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -30,27 +30,13 @@ RSpec.describe AdminDashboardData do
|
|||
problems = AdminDashboardData.fetch_problems
|
||||
expect(problems.map(&:to_s)).to include("a problem was found")
|
||||
end
|
||||
|
||||
it "calls the passed method" do
|
||||
klass =
|
||||
Class.new(AdminDashboardData) do
|
||||
def my_test_method
|
||||
"a problem was found"
|
||||
end
|
||||
end
|
||||
|
||||
klass.add_problem_check :my_test_method
|
||||
|
||||
problems = klass.fetch_problems
|
||||
expect(problems.map(&:to_s)).to include("a problem was found")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "adding scheduled checks" do
|
||||
it "does not add duplicate problems with the same identifier" do
|
||||
prob1 = AdminDashboardData::Problem.new("test problem", identifier: "test")
|
||||
prob2 = AdminDashboardData::Problem.new("test problem 2", identifier: "test")
|
||||
prob1 = ProblemCheck::Problem.new("test problem", identifier: "test")
|
||||
prob2 = ProblemCheck::Problem.new("test problem 2", identifier: "test")
|
||||
AdminDashboardData.add_found_scheduled_check_problem(prob1)
|
||||
AdminDashboardData.add_found_scheduled_check_problem(prob2)
|
||||
expect(AdminDashboardData.load_found_scheduled_check_problems.map(&:to_s)).to eq(
|
||||
|
@ -64,15 +50,15 @@ RSpec.describe AdminDashboardData do
|
|||
end
|
||||
|
||||
it "clears a specific problem by identifier" do
|
||||
prob1 = AdminDashboardData::Problem.new("test problem 1", identifier: "test")
|
||||
prob1 = ProblemCheck::Problem.new("test problem 1", identifier: "test")
|
||||
AdminDashboardData.add_found_scheduled_check_problem(prob1)
|
||||
AdminDashboardData.clear_found_problem("test")
|
||||
expect(AdminDashboardData.load_found_scheduled_check_problems).to eq([])
|
||||
end
|
||||
|
||||
it "defaults to low priority, and uses low priority if an invalid priority is passed" do
|
||||
prob1 = AdminDashboardData::Problem.new("test problem 1")
|
||||
prob2 = AdminDashboardData::Problem.new("test problem 2", priority: "superbad")
|
||||
prob1 = ProblemCheck::Problem.new("test problem 1")
|
||||
prob2 = ProblemCheck::Problem.new("test problem 2", priority: "superbad")
|
||||
expect(prob1.priority).to eq("low")
|
||||
expect(prob2.priority).to eq("low")
|
||||
end
|
||||
|
@ -106,44 +92,6 @@ RSpec.describe AdminDashboardData do
|
|||
end
|
||||
end
|
||||
|
||||
describe "rails_env_check" do
|
||||
subject(:check) { described_class.new.rails_env_check }
|
||||
|
||||
it "returns nil when running in production mode" do
|
||||
Rails.stubs(env: ActiveSupport::StringInquirer.new("production"))
|
||||
expect(check).to be_nil
|
||||
end
|
||||
|
||||
it "returns a string when running in development mode" do
|
||||
Rails.stubs(env: ActiveSupport::StringInquirer.new("development"))
|
||||
expect(check).to_not be_nil
|
||||
end
|
||||
|
||||
it "returns a string when running in test mode" do
|
||||
Rails.stubs(env: ActiveSupport::StringInquirer.new("test"))
|
||||
expect(check).to_not be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "host_names_check" do
|
||||
subject(:check) { described_class.new.host_names_check }
|
||||
|
||||
it "returns nil when host_names is set" do
|
||||
Discourse.stubs(:current_hostname).returns("something.com")
|
||||
expect(check).to be_nil
|
||||
end
|
||||
|
||||
it "returns a string when host_name is localhost" do
|
||||
Discourse.stubs(:current_hostname).returns("localhost")
|
||||
expect(check).to_not be_nil
|
||||
end
|
||||
|
||||
it "returns a string when host_name is production.localhost" do
|
||||
Discourse.stubs(:current_hostname).returns("production.localhost")
|
||||
expect(check).to_not be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "sidekiq_check" do
|
||||
subject(:check) { described_class.new.sidekiq_check }
|
||||
|
||||
|
@ -177,175 +125,4 @@ RSpec.describe AdminDashboardData do
|
|||
expect(check).to_not be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "ram_check" do
|
||||
subject(:check) { described_class.new.ram_check }
|
||||
|
||||
it "returns nil when total ram is 1 GB" do
|
||||
MemInfo.any_instance.stubs(:mem_total).returns(1_025_272)
|
||||
expect(check).to be_nil
|
||||
end
|
||||
|
||||
it "returns nil when total ram cannot be determined" do
|
||||
MemInfo.any_instance.stubs(:mem_total).returns(nil)
|
||||
expect(check).to be_nil
|
||||
end
|
||||
|
||||
it "returns a string when total ram is less than 1 GB" do
|
||||
MemInfo.any_instance.stubs(:mem_total).returns(512_636)
|
||||
expect(check).to_not be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "auth_config_checks" do
|
||||
shared_examples "problem detection for login providers" do
|
||||
context "when disabled" do
|
||||
it "returns nil" do
|
||||
SiteSetting.set(enable_setting, false)
|
||||
expect(check).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "when enabled" do
|
||||
before { SiteSetting.set(enable_setting, true) }
|
||||
|
||||
it "returns nil when key and secret are set" do
|
||||
SiteSetting.set(key, "12313213")
|
||||
SiteSetting.set(secret, "12312313123")
|
||||
expect(check).to be_nil
|
||||
end
|
||||
|
||||
it "returns a string when key is not set" do
|
||||
SiteSetting.set(key, "")
|
||||
SiteSetting.set(secret, "12312313123")
|
||||
expect(check).to_not be_nil
|
||||
end
|
||||
|
||||
it "returns a string when secret is not set" do
|
||||
SiteSetting.set(key, "123123")
|
||||
SiteSetting.set(secret, "")
|
||||
expect(check).to_not be_nil
|
||||
end
|
||||
|
||||
it "returns a string when key and secret are not set" do
|
||||
SiteSetting.set(key, "")
|
||||
SiteSetting.set(secret, "")
|
||||
expect(check).to_not be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "facebook" do
|
||||
subject(:check) { described_class.new.facebook_config_check }
|
||||
|
||||
let(:enable_setting) { :enable_facebook_logins }
|
||||
let(:key) { :facebook_app_id }
|
||||
let(:secret) { :facebook_app_secret }
|
||||
include_examples "problem detection for login providers"
|
||||
end
|
||||
|
||||
describe "twitter" do
|
||||
subject(:check) { described_class.new.twitter_config_check }
|
||||
|
||||
let(:enable_setting) { :enable_twitter_logins }
|
||||
let(:key) { :twitter_consumer_key }
|
||||
let(:secret) { :twitter_consumer_secret }
|
||||
include_examples "problem detection for login providers"
|
||||
end
|
||||
|
||||
describe "github" do
|
||||
subject(:check) { described_class.new.github_config_check }
|
||||
|
||||
let(:enable_setting) { :enable_github_logins }
|
||||
let(:key) { :github_client_id }
|
||||
let(:secret) { :github_client_secret }
|
||||
include_examples "problem detection for login providers"
|
||||
end
|
||||
end
|
||||
|
||||
describe "force_https_check" do
|
||||
subject(:check) { described_class.new(check_force_https: true).force_https_check }
|
||||
|
||||
it "returns nil if force_https site setting enabled" do
|
||||
SiteSetting.force_https = true
|
||||
expect(check).to be_nil
|
||||
end
|
||||
|
||||
it "returns nil if force_https site setting not enabled" do
|
||||
SiteSetting.force_https = false
|
||||
expect(check).to eq(I18n.t("dashboard.force_https_warning", base_path: Discourse.base_path))
|
||||
end
|
||||
end
|
||||
|
||||
describe "ignore force_https_check" do
|
||||
subject(:check) { described_class.new(check_force_https: false).force_https_check }
|
||||
|
||||
it "returns nil" do
|
||||
SiteSetting.force_https = true
|
||||
expect(check).to be_nil
|
||||
|
||||
SiteSetting.force_https = false
|
||||
expect(check).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe "#out_of_date_themes" do
|
||||
let(:remote) { RemoteTheme.create!(remote_url: "https://github.com/org/testtheme") }
|
||||
let!(:theme) { Fabricate(:theme, remote_theme: remote, name: "Test< Theme") }
|
||||
|
||||
it "outputs correctly formatted html" do
|
||||
remote.update!(local_version: "old version", remote_version: "new version", commits_behind: 2)
|
||||
dashboard_data = described_class.new
|
||||
expect(dashboard_data.out_of_date_themes).to eq(
|
||||
I18n.t("dashboard.out_of_date_themes") +
|
||||
"<ul><li><a href=\"/admin/customize/themes/#{theme.id}\">Test< Theme</a></li></ul>",
|
||||
)
|
||||
|
||||
remote.update!(local_version: "new version", commits_behind: 0)
|
||||
expect(dashboard_data.out_of_date_themes).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#unreachable_themes" do
|
||||
let(:remote) do
|
||||
RemoteTheme.create!(
|
||||
remote_url: "https://github.com/org/testtheme",
|
||||
last_error_text: "can't reach repo :'(",
|
||||
)
|
||||
end
|
||||
let!(:theme) { Fabricate(:theme, remote_theme: remote, name: "Test< Theme") }
|
||||
|
||||
it "outputs correctly formatted html" do
|
||||
dashboard_data = described_class.new
|
||||
expect(dashboard_data.unreachable_themes).to eq(
|
||||
I18n.t("dashboard.unreachable_themes") +
|
||||
"<ul><li><a href=\"/admin/customize/themes/#{theme.id}\">Test< Theme</a></li></ul>",
|
||||
)
|
||||
|
||||
remote.update!(last_error_text: nil)
|
||||
expect(dashboard_data.out_of_date_themes).to eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#translation_overrides_check" do
|
||||
subject(:dashboard_data) { described_class.new }
|
||||
|
||||
context "when there are outdated translations" do
|
||||
before { Fabricate(:translation_override, translation_key: "foo.bar", status: "outdated") }
|
||||
|
||||
it "outputs the correct message" do
|
||||
expect(dashboard_data.translation_overrides_check).to eq(
|
||||
I18n.t("dashboard.outdated_translations_warning", base_path: Discourse.base_path),
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when there are no outdated translations" do
|
||||
before { Fabricate(:translation_override, status: "up_to_date") }
|
||||
|
||||
it "outputs nothing" do
|
||||
expect(dashboard_data.translation_overrides_check).to eq(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,17 +4,17 @@ RSpec.describe ProblemCheck do
|
|||
# rubocop:disable RSpec/BeforeAfterAll
|
||||
before(:all) do
|
||||
ScheduledCheck = Class.new(described_class) { self.perform_every = 30.minutes }
|
||||
UnscheduledCheck = Class.new(described_class)
|
||||
RealtimeCheck = Class.new(described_class)
|
||||
end
|
||||
|
||||
after(:all) do
|
||||
Object.send(:remove_const, ScheduledCheck.name)
|
||||
Object.send(:remove_const, UnscheduledCheck.name)
|
||||
Object.send(:remove_const, RealtimeCheck.name)
|
||||
end
|
||||
# rubocop:enable RSpec/BeforeAfterAll
|
||||
|
||||
let(:scheduled_check) { ScheduledCheck }
|
||||
let(:unscheduled_check) { UnscheduledCheck }
|
||||
let(:realtime_check) { RealtimeCheck }
|
||||
|
||||
describe ".[]" do
|
||||
it { expect(described_class[:scheduled_check]).to eq(scheduled_check) }
|
||||
|
@ -26,16 +26,26 @@ RSpec.describe ProblemCheck do
|
|||
end
|
||||
|
||||
describe ".checks" do
|
||||
it { expect(described_class.checks).to include(scheduled_check, unscheduled_check) }
|
||||
it { expect(described_class.checks).to include(scheduled_check, realtime_check) }
|
||||
end
|
||||
|
||||
describe ".scheduled" do
|
||||
it { expect(described_class.scheduled).to include(scheduled_check) }
|
||||
it { expect(described_class.scheduled).not_to include(unscheduled_check) }
|
||||
it { expect(described_class.scheduled).not_to include(realtime_check) }
|
||||
end
|
||||
|
||||
describe ".realtime" do
|
||||
it { expect(described_class.realtime).to include(realtime_check) }
|
||||
it { expect(described_class.realtime).not_to include(scheduled_check) }
|
||||
end
|
||||
|
||||
describe ".scheduled?" do
|
||||
it { expect(scheduled_check).to be_scheduled }
|
||||
it { expect(unscheduled_check).to_not be_scheduled }
|
||||
it { expect(realtime_check).to_not be_scheduled }
|
||||
end
|
||||
|
||||
describe ".realtime?" do
|
||||
it { expect(realtime_check).to be_realtime }
|
||||
it { expect(scheduled_check).to_not be_realtime }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe ProblemCheck::EmailPollingErroredRecently do
|
||||
subject(:check) { described_class.new }
|
||||
|
||||
describe ".call" do
|
||||
before { Jobs::PollMailbox.stubs(errors_in_past_24_hours: error_count) }
|
||||
|
||||
context "when number of failing jobs is 0" do
|
||||
let(:error_count) { 0 }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when jobs are failing" do
|
||||
let(:error_count) { 1 }
|
||||
|
||||
it do
|
||||
expect(check).to(
|
||||
have_a_problem.with_priority("low").with_message(
|
||||
"Email polling has generated an error in the past 24 hours. Look at <a href='/logs' target='_blank'>the logs</a> for more details.",
|
||||
),
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
56
spec/services/problem_check/facebook_config_spec.rb
Normal file
56
spec/services/problem_check/facebook_config_spec.rb
Normal file
|
@ -0,0 +1,56 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe ProblemCheck::FacebookConfig do
|
||||
subject(:check) { described_class.new }
|
||||
|
||||
describe ".call" do
|
||||
before { SiteSetting.stubs(enable_facebook_logins: enabled) }
|
||||
|
||||
context "when Facebook authentication is disabled" do
|
||||
let(:enabled) { false }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when Facebook authentication is enabled and configured" do
|
||||
let(:enabled) { true }
|
||||
|
||||
before do
|
||||
SiteSetting.stubs(facebook_app_id: "foo")
|
||||
SiteSetting.stubs(facebook_app_secret: "bar")
|
||||
end
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when Facebook authentication is enabled but missing client ID" do
|
||||
let(:enabled) { true }
|
||||
|
||||
before do
|
||||
SiteSetting.stubs(facebook_app_id: nil)
|
||||
SiteSetting.stubs(facebook_app_secret: "bar")
|
||||
end
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
'The server is configured to allow signup and login with Facebook (enable_facebook_logins), but the app id and app secret values are not set. Go to <a href="/admin/site_settings">the Site Settings</a> and update the settings. <a href="https://meta.discourse.org/t/configuring-facebook-login-for-discourse/13394" target="_blank">See this guide to learn more</a>.',
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when Facebook authentication is enabled but missing client secret" do
|
||||
let(:enabled) { true }
|
||||
|
||||
before do
|
||||
SiteSetting.stubs(facebook_app_id: "foo")
|
||||
SiteSetting.stubs(facebook_app_secret: nil)
|
||||
end
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
'The server is configured to allow signup and login with Facebook (enable_facebook_logins), but the app id and app secret values are not set. Go to <a href="/admin/site_settings">the Site Settings</a> and update the settings. <a href="https://meta.discourse.org/t/configuring-facebook-login-for-discourse/13394" target="_blank">See this guide to learn more</a>.',
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
25
spec/services/problem_check/failing_emails_spec.rb
Normal file
25
spec/services/problem_check/failing_emails_spec.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe ProblemCheck::FailingEmails do
|
||||
subject(:check) { described_class.new }
|
||||
|
||||
describe ".call" do
|
||||
before { Jobs.stubs(num_email_retry_jobs: failing_jobs) }
|
||||
|
||||
context "when number of failing jobs is 0" do
|
||||
let(:failing_jobs) { 0 }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when jobs are failing" do
|
||||
let(:failing_jobs) { 1 }
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
'There are 1 email jobs that failed. Check your app.yml and ensure that the mail server settings are correct. <a href="/sidekiq/retries" target="_blank">See the failed jobs in Sidekiq</a>.',
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
36
spec/services/problem_check/force_https_spec.rb
Normal file
36
spec/services/problem_check/force_https_spec.rb
Normal file
|
@ -0,0 +1,36 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe ProblemCheck::ForceHttps do
|
||||
subject(:check) { described_class.new(data) }
|
||||
|
||||
describe ".call" do
|
||||
before { SiteSetting.stubs(force_https: configured) }
|
||||
|
||||
context "when configured to force SSL" do
|
||||
let(:configured) { true }
|
||||
let(:data) { { check_force_https: true } }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when not configured to force SSL" do
|
||||
let(:configured) { false }
|
||||
|
||||
context "when the request is coming over HTTPS" do
|
||||
let(:data) { { check_force_https: true } }
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
"Your website is using SSL. But `<a href='/admin/site_settings/category/all_results?filter=force_https'>force_https</a>` is not yet enabled in your site settings.",
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when the request is coming over HTTP" do
|
||||
let(:data) { { check_force_https: false } }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
56
spec/services/problem_check/github_config_spec.rb
Normal file
56
spec/services/problem_check/github_config_spec.rb
Normal file
|
@ -0,0 +1,56 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe ProblemCheck::GithubConfig do
|
||||
subject(:check) { described_class.new }
|
||||
|
||||
describe ".call" do
|
||||
before { SiteSetting.stubs(enable_github_logins: enabled) }
|
||||
|
||||
context "when GitHub authentication is disabled" do
|
||||
let(:enabled) { false }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when GitHub authentication is enabled and configured" do
|
||||
let(:enabled) { true }
|
||||
|
||||
before do
|
||||
SiteSetting.stubs(github_client_id: "foo")
|
||||
SiteSetting.stubs(github_client_secret: "bar")
|
||||
end
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when GitHub authentication is enabled but missing client ID" do
|
||||
let(:enabled) { true }
|
||||
|
||||
before do
|
||||
SiteSetting.stubs(github_client_id: nil)
|
||||
SiteSetting.stubs(github_client_secret: "bar")
|
||||
end
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
'The server is configured to allow signup and login with GitHub (enable_github_logins), but the client id and secret values are not set. Go to <a href="/admin/site_settings">the Site Settings</a> and update the settings. <a href="https://meta.discourse.org/t/configuring-github-login-for-discourse/13745" target="_blank">See this guide to learn more</a>.',
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when GitHub authentication is enabled but missing client secret" do
|
||||
let(:enabled) { true }
|
||||
|
||||
before do
|
||||
SiteSetting.stubs(github_client_id: "foo")
|
||||
SiteSetting.stubs(github_client_secret: nil)
|
||||
end
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
'The server is configured to allow signup and login with GitHub (enable_github_logins), but the client id and secret values are not set. Go to <a href="/admin/site_settings">the Site Settings</a> and update the settings. <a href="https://meta.discourse.org/t/configuring-github-login-for-discourse/13745" target="_blank">See this guide to learn more</a>.',
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
25
spec/services/problem_check/google_analytics_version_spec.rb
Normal file
25
spec/services/problem_check/google_analytics_version_spec.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe ProblemCheck::GoogleAnalyticsVersion do
|
||||
subject(:check) { described_class.new }
|
||||
|
||||
describe ".call" do
|
||||
before { SiteSetting.stubs(ga_version: version) }
|
||||
|
||||
context "when using Google Analytics V3" do
|
||||
let(:version) { "v3_analytics" }
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
"Your Discourse is currently using Google Analytics 3, which will no longer be supported after July 2023. <a href='https://meta.discourse.org/t/260498'>Upgrade to Google Analytics 4</a> now to continue receiving valuable insights and analytics for your website's performance.",
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when using Google Analytics V4" do
|
||||
let(:version) { "v4_analytics" }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
end
|
||||
end
|
56
spec/services/problem_check/google_oauth2_config_spec.rb
Normal file
56
spec/services/problem_check/google_oauth2_config_spec.rb
Normal file
|
@ -0,0 +1,56 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe ProblemCheck::GoogleOauth2Config do
|
||||
subject(:check) { described_class.new }
|
||||
|
||||
describe ".call" do
|
||||
before { SiteSetting.stubs(enable_google_oauth2_logins: enabled) }
|
||||
|
||||
context "when Google OAuth is disabled" do
|
||||
let(:enabled) { false }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when Google OAuth is enabled and configured" do
|
||||
let(:enabled) { true }
|
||||
|
||||
before do
|
||||
SiteSetting.stubs(google_oauth2_client_id: "foo")
|
||||
SiteSetting.stubs(google_oauth2_client_secret: "bar")
|
||||
end
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when Google OAuth is enabled but missing client ID" do
|
||||
let(:enabled) { true }
|
||||
|
||||
before do
|
||||
SiteSetting.stubs(google_oauth2_client_id: nil)
|
||||
SiteSetting.stubs(google_oauth2_client_secret: "bar")
|
||||
end
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
'The server is configured to allow signup and login with Google OAuth2 (enable_google_oauth2_logins), but the client id and client secret values are not set. Go to <a href="/admin/site_settings">the Site Settings</a> and update the settings. <a href="https://meta.discourse.org/t/configuring-google-login-for-discourse/15858" target="_blank">See this guide to learn more</a>.',
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when Google OAuth is enabled but missing client secret" do
|
||||
let(:enabled) { true }
|
||||
|
||||
before do
|
||||
SiteSetting.stubs(google_oauth2_client_id: "foo")
|
||||
SiteSetting.stubs(google_oauth2_client_secret: nil)
|
||||
end
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
'The server is configured to allow signup and login with Google OAuth2 (enable_google_oauth2_logins), but the client id and client secret values are not set. Go to <a href="/admin/site_settings">the Site Settings</a> and update the settings. <a href="https://meta.discourse.org/t/configuring-google-login-for-discourse/15858" target="_blank">See this guide to learn more</a>.',
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,6 +4,8 @@ require "net/smtp"
|
|||
require "net/imap"
|
||||
|
||||
RSpec.describe ProblemCheck::GroupEmailCredentials do
|
||||
subject(:check) { described_class.new }
|
||||
|
||||
fab!(:group1) { Fabricate(:group) }
|
||||
fab!(:group2) { Fabricate(:smtp_group) }
|
||||
fab!(:group3) { Fabricate(:imap_group) }
|
||||
|
@ -12,7 +14,7 @@ RSpec.describe ProblemCheck::GroupEmailCredentials do
|
|||
it "does nothing if SMTP is disabled for the site" do
|
||||
expect_no_validate_any
|
||||
SiteSetting.enable_smtp = false
|
||||
expect(described_class.new.call).to eq([])
|
||||
expect(check).to be_chill_about_it
|
||||
end
|
||||
|
||||
context "with smtp and imap enabled for the site" do
|
||||
|
@ -25,7 +27,7 @@ RSpec.describe ProblemCheck::GroupEmailCredentials do
|
|||
expect_no_validate_any
|
||||
group2.update!(smtp_enabled: false)
|
||||
group3.update!(smtp_enabled: false, imap_enabled: false)
|
||||
expect(described_class.new.call).to eq([])
|
||||
expect(check).to be_chill_about_it
|
||||
end
|
||||
|
||||
it "returns a problem with the group's SMTP settings error" do
|
||||
|
|
35
spec/services/problem_check/host_names_spec.rb
Normal file
35
spec/services/problem_check/host_names_spec.rb
Normal file
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe ProblemCheck::HostNames do
|
||||
subject(:check) { described_class.new }
|
||||
|
||||
describe ".call" do
|
||||
before { Discourse.stubs(current_hostname: hostname) }
|
||||
|
||||
context "when a production host name is configured" do
|
||||
let(:hostname) { "something.com" }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when host name is set to localhost" do
|
||||
let(:hostname) { "localhost" }
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
"Your config/database.yml file is using the default localhost hostname. Update it to use your site's hostname.",
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when host name is set to production.localhost" do
|
||||
let(:hostname) { "production.localhost" }
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
"Your config/database.yml file is using the default localhost hostname. Update it to use your site's hostname.",
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
39
spec/services/problem_check/image_magick_spec.rb
Normal file
39
spec/services/problem_check/image_magick_spec.rb
Normal file
|
@ -0,0 +1,39 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe ProblemCheck::ImageMagick do
|
||||
subject(:check) { described_class.new }
|
||||
|
||||
describe ".call" do
|
||||
before do
|
||||
SiteSetting.stubs(create_thumbnails: enabled)
|
||||
Kernel.stubs(system: installed)
|
||||
end
|
||||
|
||||
context "when thumbnail creation is enabled" do
|
||||
let(:enabled) { true }
|
||||
|
||||
context "when Image Magick is installed" do
|
||||
let(:installed) { true }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when Image Magick is not installed" do
|
||||
let(:installed) { false }
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
'The server is configured to create thumbnails of large images, but ImageMagick is not installed. Install ImageMagick using your favorite package manager or <a href="https://www.imagemagick.org/script/download.php" target="_blank">download the latest release</a>.',
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when thumbnail creation is disabled" do
|
||||
let(:enabled) { false }
|
||||
let(:installed) { false }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
end
|
||||
end
|
41
spec/services/problem_check/missing_mailgun_api_key_spec.rb
Normal file
41
spec/services/problem_check/missing_mailgun_api_key_spec.rb
Normal file
|
@ -0,0 +1,41 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe ProblemCheck::MissingMailgunApiKey do
|
||||
subject(:check) { described_class.new }
|
||||
|
||||
describe ".call" do
|
||||
before do
|
||||
SiteSetting.stubs(reply_by_email_enabled: replies_enabled)
|
||||
ActionMailer::Base.smtp_settings.stubs(dig: mailgun_address)
|
||||
SiteSetting.stubs(mailgun_api_key: api_key)
|
||||
end
|
||||
|
||||
context "when replies are disabled" do
|
||||
let(:replies_enabled) { false }
|
||||
let(:mailgun_address) { anything }
|
||||
let(:api_key) { anything }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when not using Mailgun for replies" do
|
||||
let(:replies_enabled) { false }
|
||||
let(:mailgun_address) { nil }
|
||||
let(:api_key) { anything }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when using Mailgun without an API key" do
|
||||
let(:replies_enabled) { true }
|
||||
let(:mailgun_address) { "foo" }
|
||||
let(:api_key) { nil }
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
"The server is configured to send emails via Mailgun but you haven't provided an API key used to verify the webhook messages.",
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
32
spec/services/problem_check/out_of_date_themes_spec.rb
Normal file
32
spec/services/problem_check/out_of_date_themes_spec.rb
Normal file
|
@ -0,0 +1,32 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe ProblemCheck::OutOfDateThemes do
|
||||
subject(:check) { described_class.new }
|
||||
|
||||
describe ".call" do
|
||||
let(:remote) do
|
||||
RemoteTheme.create!(
|
||||
remote_url: "https://github.com/org/testtheme",
|
||||
commits_behind: commits_behind,
|
||||
)
|
||||
end
|
||||
|
||||
before { Fabricate(:theme, id: 44, remote_theme: remote, name: "Test< Theme") }
|
||||
|
||||
context "when theme is out of date" do
|
||||
let(:commits_behind) { 2 }
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
'Updates are available for the following themes:<ul><li><a href="/admin/customize/themes/44">Test< Theme</a></li></ul>',
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when theme is up to date" do
|
||||
let(:commits_behind) { 0 }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
end
|
||||
end
|
35
spec/services/problem_check/rails_env_spec.rb
Normal file
35
spec/services/problem_check/rails_env_spec.rb
Normal file
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe ProblemCheck::RailsEnv do
|
||||
subject(:check) { described_class.new }
|
||||
|
||||
describe ".call" do
|
||||
before { Rails.stubs(env: ActiveSupport::StringInquirer.new(environment)) }
|
||||
|
||||
context "when running in production environment" do
|
||||
let(:environment) { "production" }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when running in development environment" do
|
||||
let(:environment) { "development" }
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
"Your server is running in development mode.",
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when running in test environment" do
|
||||
let(:environment) { "test" }
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
"Your server is running in test mode.",
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
29
spec/services/problem_check/ram_spec.rb
Normal file
29
spec/services/problem_check/ram_spec.rb
Normal file
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe ProblemCheck::Ram do
|
||||
subject(:check) { described_class.new }
|
||||
|
||||
before { MemInfo.any_instance.stubs(mem_total: total_ram) }
|
||||
|
||||
context "when total ram is 1 GB" do
|
||||
let(:total_ram) { 1_025_272 }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when total ram cannot be determined" do
|
||||
let(:total_ram) { nil }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when total ram is less than 1 GB" do
|
||||
let(:total_ram) { 512_636 }
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
"Your server is running with less than 1 GB of total memory. At least 1 GB of memory is recommended.",
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
82
spec/services/problem_check/s3_backup_config_spec.rb
Normal file
82
spec/services/problem_check/s3_backup_config_spec.rb
Normal file
|
@ -0,0 +1,82 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe ProblemCheck::S3BackupConfig do
|
||||
subject(:check) { described_class.new }
|
||||
|
||||
describe ".call" do
|
||||
let(:backup_location) { BackupLocationSiteSetting::S3 }
|
||||
let(:bucket_name) { "backups" }
|
||||
|
||||
before do
|
||||
GlobalSetting.stubs(use_s3?: globally_enabled)
|
||||
SiteSetting.stubs(backup_location: backup_location)
|
||||
SiteSetting.stubs(s3_backup_bucket: bucket_name)
|
||||
end
|
||||
|
||||
context "when S3 uploads are globally enabled" do
|
||||
let(:globally_enabled) { true }
|
||||
|
||||
it "relies on the check in GlobalSettings#use_s3?" do
|
||||
expect(check).to be_chill_about_it
|
||||
end
|
||||
end
|
||||
|
||||
context "when S3 backups are disabled" do
|
||||
let(:globally_enabled) { false }
|
||||
let(:backup_location) { nil }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when S3 backups are enabled" do
|
||||
let(:globally_enabled) { false }
|
||||
|
||||
before { SiteSetting.stubs(s3_use_iam_profile: use_iam_profile) }
|
||||
|
||||
context "when configured to use IAM profile" do
|
||||
let(:use_iam_profile) { true }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when not configured to use IAM profile" do
|
||||
let(:use_iam_profile) { false }
|
||||
|
||||
before do
|
||||
SiteSetting.stubs(s3_access_key_id: access_key)
|
||||
SiteSetting.stubs(s3_secret_access_key: secret_access_key)
|
||||
end
|
||||
|
||||
context "when credentials are present" do
|
||||
let(:access_key) { "foo" }
|
||||
let(:secret_access_key) { "bar" }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when credentials are missing" do
|
||||
let(:access_key) { "foo" }
|
||||
let(:secret_access_key) { nil }
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
'The server is configured to upload backups to S3, but at least one the following setting is not set: s3_access_key_id, s3_secret_access_key, s3_use_iam_profile, or s3_backup_bucket. Go to <a href="/admin/site_settings">the Site Settings</a> and update the settings. <a href="https://meta.discourse.org/t/how-to-set-up-image-uploads-to-s3/7229" target="_blank">See "How to set up image uploads to S3?" to learn more</a>.',
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when bucket name is missing" do
|
||||
let(:access_key) { "foo" }
|
||||
let(:secret_access_key) { "bar" }
|
||||
let(:bucket_name) { nil }
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
'The server is configured to upload backups to S3, but at least one the following setting is not set: s3_access_key_id, s3_secret_access_key, s3_use_iam_profile, or s3_backup_bucket. Go to <a href="/admin/site_settings">the Site Settings</a> and update the settings. <a href="https://meta.discourse.org/t/how-to-set-up-image-uploads-to-s3/7229" target="_blank">See "How to set up image uploads to S3?" to learn more</a>.',
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
42
spec/services/problem_check/s3_cdn_spec.rb
Normal file
42
spec/services/problem_check/s3_cdn_spec.rb
Normal file
|
@ -0,0 +1,42 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe ProblemCheck::S3Cdn do
|
||||
subject(:check) { described_class.new }
|
||||
|
||||
describe ".call" do
|
||||
before do
|
||||
GlobalSetting.stubs(use_s3?: globally_enabled)
|
||||
SiteSetting.stubs(enable_s3_uploads?: locally_enabled)
|
||||
SiteSetting::Upload.stubs(s3_cdn_url: cdn_url)
|
||||
end
|
||||
|
||||
context "when S3 uploads are enabled" do
|
||||
let(:globally_enabled) { false }
|
||||
let(:locally_enabled) { true }
|
||||
|
||||
context "when CDN URL is configured" do
|
||||
let(:cdn_url) { "https://cdn.codinghorror.com" }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when CDN URL is not configured" do
|
||||
let(:cdn_url) { nil }
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
'The server is configured to upload files to S3, but there is no S3 CDN configured. This can lead to expensive S3 costs and slower site performance. <a href="https://meta.discourse.org/t/-/148916" target="_blank">See "Using Object Storage for Uploads" to learn more</a>.',
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when S3 uploads are disabled" do
|
||||
let(:globally_enabled) { false }
|
||||
let(:locally_enabled) { false }
|
||||
let(:cdn_url) { nil }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
end
|
||||
end
|
83
spec/services/problem_check/s3_upload_config_spec.rb
Normal file
83
spec/services/problem_check/s3_upload_config_spec.rb
Normal file
|
@ -0,0 +1,83 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe ProblemCheck::S3UploadConfig do
|
||||
subject(:check) { described_class.new }
|
||||
|
||||
describe ".call" do
|
||||
before do
|
||||
GlobalSetting.stubs(use_s3?: globally_enabled)
|
||||
SiteSetting.stubs(enable_s3_uploads?: locally_enabled)
|
||||
end
|
||||
|
||||
context "when S3 uploads are globally enabled" do
|
||||
let(:globally_enabled) { true }
|
||||
let(:locally_enabled) { false }
|
||||
|
||||
it "relies on the check in GlobalSettings#use_s3?" do
|
||||
expect(check).to be_chill_about_it
|
||||
end
|
||||
end
|
||||
|
||||
context "when S3 uploads are disabled" do
|
||||
let(:globally_enabled) { false }
|
||||
let(:locally_enabled) { false }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when S3 uploads are locally enabled" do
|
||||
let(:globally_enabled) { false }
|
||||
let(:locally_enabled) { true }
|
||||
|
||||
before { SiteSetting.stubs(s3_use_iam_profile: use_iam_profile) }
|
||||
|
||||
context "when configured to use IAM profile" do
|
||||
let(:use_iam_profile) { true }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when not configured to use IAM profile" do
|
||||
let(:use_iam_profile) { false }
|
||||
|
||||
before do
|
||||
SiteSetting.stubs(s3_access_key_id: access_key)
|
||||
SiteSetting.stubs(s3_secret_access_key: secret_access_key)
|
||||
SiteSetting.stubs(s3_upload_bucket: bucket_name)
|
||||
end
|
||||
|
||||
context "when credentials are present" do
|
||||
let(:access_key) { "foo" }
|
||||
let(:secret_access_key) { "bar" }
|
||||
let(:bucket_name) { "baz" }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when credentials are missing" do
|
||||
let(:access_key) { "foo" }
|
||||
let(:secret_access_key) { nil }
|
||||
let(:bucket_name) { "baz" }
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
'The server is configured to upload files to S3, but at least one the following setting is not set: s3_access_key_id, s3_secret_access_key, s3_use_iam_profile, or s3_upload_bucket. Go to <a href="/admin/site_settings">the Site Settings</a> and update the settings. <a href="https://meta.discourse.org/t/how-to-set-up-image-uploads-to-s3/7229" target="_blank">See "How to set up image uploads to S3?" to learn more</a>.',
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when bucket name is missing" do
|
||||
let(:access_key) { "foo" }
|
||||
let(:secret_access_key) { "bar" }
|
||||
let(:bucket_name) { nil }
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
'The server is configured to upload files to S3, but at least one the following setting is not set: s3_access_key_id, s3_secret_access_key, s3_use_iam_profile, or s3_upload_bucket. Go to <a href="/admin/site_settings">the Site Settings</a> and update the settings. <a href="https://meta.discourse.org/t/how-to-set-up-image-uploads-to-s3/7229" target="_blank">See "How to set up image uploads to S3?" to learn more</a>.',
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
25
spec/services/problem_check/subfolder_ends_in_slash_spec.rb
Normal file
25
spec/services/problem_check/subfolder_ends_in_slash_spec.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe ProblemCheck::SubfolderEndsInSlash do
|
||||
subject(:check) { described_class.new }
|
||||
|
||||
describe ".call" do
|
||||
before { Discourse.stubs(base_path: path) }
|
||||
|
||||
context "when path doesn't end in a slash" do
|
||||
let(:path) { "cats" }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when path ends in a slash" do
|
||||
let(:path) { "cats/" }
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
"Your subfolder setup is incorrect; the DISCOURSE_RELATIVE_URL_ROOT ends in a slash.",
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
35
spec/services/problem_check/translation_overrides_spec.rb
Normal file
35
spec/services/problem_check/translation_overrides_spec.rb
Normal file
|
@ -0,0 +1,35 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe ProblemCheck::TranslationOverrides do
|
||||
subject(:check) { described_class.new }
|
||||
|
||||
describe ".call" do
|
||||
before { Fabricate(:translation_override, status: status) }
|
||||
|
||||
context "when there are outdated translation overrides" do
|
||||
let(:status) { "outdated" }
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
"Some of your translation overrides are out of date. Please check your <a href='/admin/customize/site_texts?outdated=true'>text customizations</a>.",
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when there are translation overrides with invalid interpolation keys" do
|
||||
let(:status) { "invalid_interpolation_keys" }
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
"Some of your translation overrides are out of date. Please check your <a href='/admin/customize/site_texts?outdated=true'>text customizations</a>.",
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when all translation overrides are fine" do
|
||||
let(:status) { "up_to_date" }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
end
|
||||
end
|
56
spec/services/problem_check/twitter_config_spec.rb
Normal file
56
spec/services/problem_check/twitter_config_spec.rb
Normal file
|
@ -0,0 +1,56 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe ProblemCheck::TwitterConfig do
|
||||
subject(:check) { described_class.new }
|
||||
|
||||
describe ".call" do
|
||||
before { SiteSetting.stubs(enable_twitter_logins: enabled) }
|
||||
|
||||
context "when Twitter authentication is disabled" do
|
||||
let(:enabled) { false }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when Twitter authentication is enabled and configured" do
|
||||
let(:enabled) { true }
|
||||
|
||||
before do
|
||||
SiteSetting.stubs(twitter_consumer_key: "foo")
|
||||
SiteSetting.stubs(twitter_consumer_secret: "bar")
|
||||
end
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when Twitter authentication is enabled but missing client ID" do
|
||||
let(:enabled) { true }
|
||||
|
||||
before do
|
||||
SiteSetting.stubs(twitter_consumer_key: nil)
|
||||
SiteSetting.stubs(twitter_consumer_secret: "bar")
|
||||
end
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
'The server is configured to allow signup and login with Twitter (enable_twitter_logins), but the key and secret values are not set. Go to <a href="/admin/site_settings">the Site Settings</a> and update the settings. <a href="https://meta.discourse.org/t/configuring-twitter-login-for-discourse/13395" target="_blank">See this guide to learn more</a>.',
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when Twitter authentication is enabled but missing client secret" do
|
||||
let(:enabled) { true }
|
||||
|
||||
before do
|
||||
SiteSetting.stubs(twitter_consumer_key: "foo")
|
||||
SiteSetting.stubs(twitter_consumer_secret: nil)
|
||||
end
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
'The server is configured to allow signup and login with Twitter (enable_twitter_logins), but the key and secret values are not set. Go to <a href="/admin/site_settings">the Site Settings</a> and update the settings. <a href="https://meta.discourse.org/t/configuring-twitter-login-for-discourse/13395" target="_blank">See this guide to learn more</a>.',
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe ProblemCheck::TwitterLogin do
|
||||
let(:problem_check) { described_class.new }
|
||||
let(:check) { described_class.new }
|
||||
|
||||
let(:authenticator) { mock("Auth::TwitterAuthenticator") }
|
||||
|
||||
|
@ -11,7 +11,7 @@ RSpec.describe ProblemCheck::TwitterLogin do
|
|||
context "when Twitter authentication isn't enabled" do
|
||||
before { authenticator.stubs(:enabled?).returns(false) }
|
||||
|
||||
it { expect(problem_check.call).to be_empty }
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when Twitter authentication appears to work" do
|
||||
|
@ -20,7 +20,7 @@ RSpec.describe ProblemCheck::TwitterLogin do
|
|||
authenticator.stubs(:healthy?).returns(true)
|
||||
end
|
||||
|
||||
it { expect(problem_check.call).to be_empty }
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when Twitter authentication appears not to work" do
|
||||
|
@ -31,13 +31,8 @@ RSpec.describe ProblemCheck::TwitterLogin do
|
|||
end
|
||||
|
||||
it do
|
||||
expect(problem_check.call).to contain_exactly(
|
||||
have_attributes(
|
||||
identifier: :twitter_login,
|
||||
priority: "high",
|
||||
message:
|
||||
expect(check).to have_a_problem.with_priority("high").with_message(
|
||||
'Twitter login appears to not be working at the moment. Check the credentials in <a href="foo.bar/admin/site_settings/category/login?filter=twitter">the Site Settings</a>.',
|
||||
),
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
32
spec/services/problem_check/unreachable_themes_spec.rb
Normal file
32
spec/services/problem_check/unreachable_themes_spec.rb
Normal file
|
@ -0,0 +1,32 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe ProblemCheck::UnreachableThemes do
|
||||
subject(:check) { described_class.new }
|
||||
|
||||
describe ".call" do
|
||||
let(:remote) do
|
||||
RemoteTheme.create!(
|
||||
remote_url: "https://github.com/org/testtheme",
|
||||
last_error_text: last_error,
|
||||
)
|
||||
end
|
||||
|
||||
before { Fabricate(:theme, id: 50, remote_theme: remote, name: "Test Theme") }
|
||||
|
||||
context "when theme is unreachable" do
|
||||
let(:last_error) { "Can't reach. Too short." }
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
'We were unable to check for updates on the following themes:<ul><li><a href="/admin/customize/themes/50">Test Theme</a></li></ul>',
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when theme is reachable" do
|
||||
let(:last_error) { nil }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
end
|
||||
end
|
21
spec/services/problem_check/watched_words_spec.rb
Normal file
21
spec/services/problem_check/watched_words_spec.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe ProblemCheck::WatchedWords do
|
||||
subject(:check) { described_class.new }
|
||||
|
||||
context "when all regular expressions are valid" do
|
||||
before { WordWatcher.stubs(:compiled_regexps_for_action).returns([]) }
|
||||
|
||||
it { expect(check).to be_chill_about_it }
|
||||
end
|
||||
|
||||
context "when regular expressions are invalid" do
|
||||
before { WordWatcher.stubs(:compiled_regexps_for_action).raises(RegexpError.new) }
|
||||
|
||||
it do
|
||||
expect(check).to have_a_problem.with_priority("low").with_message(
|
||||
"The regular expression for 'Block', 'Censor', 'Require Approval', 'Flag', 'Link', 'Replace', 'Tag', 'Silence' watched words is invalid. Please check your <a href='/admin/customize/watched_words'>Watched Word settings</a>, or disable the 'watched words regular expressions' site setting.",
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
45
spec/support/problem_check_matcher.rb
Normal file
45
spec/support/problem_check_matcher.rb
Normal file
|
@ -0,0 +1,45 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec::Matchers.define :be_chill_about_it do
|
||||
match { |service| expect(service.call).to be_empty }
|
||||
end
|
||||
|
||||
RSpec::Matchers.define :have_a_problem do
|
||||
chain :with_message do |message|
|
||||
@message = message
|
||||
end
|
||||
|
||||
chain :with_priority do |priority|
|
||||
@priority = priority
|
||||
end
|
||||
|
||||
match do |service|
|
||||
@result = service.call
|
||||
|
||||
aggregate_failures do
|
||||
expect(@result).to include(be_a(ProblemCheck::Problem))
|
||||
expect(@result.first.priority).to(eq(@priority.to_s)) if @priority.present?
|
||||
expect(@result.first.message).to(eq(@message)) if @message.present?
|
||||
end
|
||||
end
|
||||
|
||||
failure_message do |service|
|
||||
if @result.empty?
|
||||
"Expected check to have a problem, but it was chill about it."
|
||||
elsif !@result.all?(ProblemCheck::Problem)
|
||||
"Expected result to contain only instances of `Problem`."
|
||||
elsif @priority.present? && @result.first.priority != @priority
|
||||
"Expected problem to have priority `#{@priority}`, but got priority `#{@result.first.priority}`."
|
||||
elsif @message.present? && @result.first.message != @message
|
||||
<<~MESSAGE
|
||||
Expected problem to have message:
|
||||
|
||||
> #{@message}
|
||||
|
||||
but got message:
|
||||
|
||||
> #{@result.first.message}
|
||||
MESSAGE
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user