Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 42 additions & 2 deletions config/initializers/okcomputer.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# frozen_string_literal: true

require 'net/smtp'

# Health check configuration

OkComputer.logger = Rails.logger
Expand All @@ -18,11 +20,49 @@ def check
end
end

# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
class MailConnectivityCheck < OkComputer::Check

# Check that the mail password is set
def check
settings = ActionMailer::Base.smtp_settings
begin
Net::SMTP.start(
settings[:address],
settings[:port],
settings[:domain],
settings[:user_name],
settings[:password],
settings[:authentication],
tls: true
) { mark_message 'Connection for smtp successful' }
rescue Net::SMTPAuthenticationError => e
mark_failure
Rails.logger.warn "SMTP authentication error: #{e}"
mark_message 'SMTP Error: Authentication failed. Check logs for more details'
rescue Net::SMTPServerBusy, Net::SMTPSyntaxError, Net::SMTPFatalError, Net::SMTPUnknownError => e
mark_failure
Rails.logger.warn "SMTP Error: #{e}"
mark_message 'SMTP error. Check logs for more details'
rescue IOError, Net::ReadTimeout => e
mark_failure
Rails.logger.warn "SMTP Timeout: #{e}"
mark_message 'SMTP Connection error: Timeout. Check logs for more details'
rescue StandardError => e
# Catch any other unexpected errors
mark_failure
Rails.logger.warn "SMTP standard error: #{e}"
mark_message 'SMTP ERROR: Could not connect. Check logs for more details'
end
end
end
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize

# Ensure Alma API is working.
OkComputer::Registry.register 'alma-patron-lookup', AlmaPatronCheck.new

# Ensure database migrations have been run.
OkComputer::Registry.register 'database-migrations', OkComputer::ActiveRecordMigrationsCheck.new

# Ensure connectivity to the mail system.
OkComputer::Registry.register 'action-mailer', OkComputer::ActionMailerCheck.new
# Ensure SMTP can connect
OkComputer::Registry.register 'mail-connectivity', MailConnectivityCheck.new if ActionMailer::Base.delivery_method == :smtp
111 changes: 111 additions & 0 deletions spec/request/mail_connectivity_check_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
require 'rails_helper'
require 'net/smtp'

RSpec.describe MailConnectivityCheck do
subject(:check) { described_class.new }

let(:smtp_settings) do
{
address: 'smtp.example.com',
port: 587,
domain: 'example.com',
user_name: 'user',
password: 'password',
authentication: 'plain',
tls: true
}
end

before do
allow(ActionMailer::Base).to receive(:smtp_settings).and_return(smtp_settings)
allow(Rails.logger).to receive(:warn)
end

describe '#check' do
context 'when SMTP connection succeeds' do
before do
allow(Net::SMTP).to receive(:start).and_yield
end

it 'marks the check as successful' do
check.check

expect(check.success?).to be(true)
expect(check.message).to eq('Connection for smtp successful')
end
end

context 'when authentication fails' do
before do
allow(Net::SMTP).to receive(:start)
.and_raise(Net::SMTPAuthenticationError.new('auth failed'))
end

it 'marks failure and logs authentication error' do
check.check

expect(check.success?).to be(false)
expect(check.message)
.to eq('SMTP Error: Authentication failed. Check logs for more details')
expect(Rails.logger)
.to have_received(:warn).with(/SMTP authentication error/)
end
end

context 'when SMTP protocol errors occur' do
[
Net::SMTPServerBusy,
Net::SMTPSyntaxError,
Net::SMTPFatalError,
Net::SMTPUnknownError
].each do |error_class|
it "handles #{error_class.name}" do
allow(Net::SMTP).to receive(:start)
.and_raise(error_class.new('smtp error'))

check.check

expect(check.success?).to be(false)
expect(check.message)
.to eq('SMTP error. Check logs for more details')
expect(Rails.logger)
.to have_received(:warn).with(/SMTP Error/)
end
end
end

context 'when a timeout occurs' do
[IOError, Net::ReadTimeout].each do |error_class|
it "handles #{error_class.name}" do
allow(Net::SMTP).to receive(:start)
.and_raise(error_class.new('timeout'))

check.check

expect(check.success?).to be(false)
expect(check.message)
.to eq('SMTP Connection error: Timeout. Check logs for more details')
expect(Rails.logger)
.to have_received(:warn).with(/SMTP Timeout/)
end
end
end

context 'when an unexpected error occurs' do
before do
allow(Net::SMTP).to receive(:start)
.and_raise(StandardError.new('failed'))
end

it 'marks failure and logs standard error' do
check.check

expect(check.success?).to be(false)
expect(check.message)
.to eq('SMTP ERROR: Could not connect. Check logs for more details')
expect(Rails.logger)
.to have_received(:warn).with(/SMTP standard error/)
end
end
end
end
64 changes: 48 additions & 16 deletions spec/request/okcomputer_spec.rb
Original file line number Diff line number Diff line change
@@ -1,29 +1,61 @@
require 'rails_helper'

RSpec.describe 'OKComputer', type: :request do
before { allow(Alma::User).to receive(:find).and_return(Alma::User.new) }
before do
allow(Alma::User).to receive(:find).and_return(Alma::User.new)
end

it 'is mounted at /okcomputer' do
get '/okcomputer'
expect(response).to have_http_status :ok
end

it 'returns all checks to /health' do
get '/health'
expect(response.parsed_body.keys).to match_array %w[
action-mailer
alma-patron-lookup
default
database
database-migrations
]
pending 'https://github.com/emmahsax/okcomputer/pull/21'
expect(response).to have_http_status :ok
context 'without SMTP enabled' do
before do
allow(ActionMailer::Base).to receive(:delivery_method).and_return(:test)

OkComputer::Registry.instance_variable_set(:@checks, {})
load Rails.root.join('config/initializers/okcomputer.rb')
end

it 'returns checks to /health' do
get '/health'
expect(response.parsed_body.keys).to match_array %w[
default
database
alma-patron-lookup
database-migrations
]
end
end

it 'fails when Alma lookups fail' do
expect(Alma::User).to receive(:find).and_raise('Uh oh!')
get '/health'
expect(response).not_to have_http_status :ok
context 'with SMTP enabled' do
before do
allow(ActionMailer::Base).to receive(:delivery_method).and_return(:smtp)
allow(Net::SMTP).to receive(:start)

OkComputer::Registry.instance_variable_set(:@checks, {})
load Rails.root.join('config/initializers/okcomputer.rb')
end

it 'returns all checks to /health' do
get '/health'
expect(response.parsed_body.keys).to match_array %w[
default
database
alma-patron-lookup
database-migrations
mail-connectivity
]
end
end

context 'when Alma lookups fail' do
it 'returns a non-200 response' do
expect(Alma::User).to receive(:find).and_raise('Uh oh!')
get '/health'
expect(response).not_to have_http_status :ok
end
end

end