Skip to content

Commit e163b60

Browse files
davezuckermanDavid Zuckerman
andauthored
custom health check to verify mail password is set (#9)
added test for new mail password healthcheck removed stubbing for gpg key added connection checks for smtp in okcomputer renamed mail-connectivity check, generalized public errors moved okcomputer registration for mail-connectivity outside of class added tests for mail_connectivity rubocop syntax fixes Co-authored-by: David Zuckerman <dzuckerm@library.berkeley.edu>
1 parent ba0dbd0 commit e163b60

File tree

3 files changed

+201
-18
lines changed

3 files changed

+201
-18
lines changed

config/initializers/okcomputer.rb

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# frozen_string_literal: true
22

3+
require 'net/smtp'
4+
35
# Health check configuration
46

57
OkComputer.logger = Rails.logger
@@ -18,11 +20,49 @@ def check
1820
end
1921
end
2022

23+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
24+
class MailConnectivityCheck < OkComputer::Check
25+
26+
# Check that the mail password is set
27+
def check
28+
settings = ActionMailer::Base.smtp_settings
29+
begin
30+
Net::SMTP.start(
31+
settings[:address],
32+
settings[:port],
33+
settings[:domain],
34+
settings[:user_name],
35+
settings[:password],
36+
settings[:authentication],
37+
tls: true
38+
) { mark_message 'Connection for smtp successful' }
39+
rescue Net::SMTPAuthenticationError => e
40+
mark_failure
41+
Rails.logger.warn "SMTP authentication error: #{e}"
42+
mark_message 'SMTP Error: Authentication failed. Check logs for more details'
43+
rescue Net::SMTPServerBusy, Net::SMTPSyntaxError, Net::SMTPFatalError, Net::SMTPUnknownError => e
44+
mark_failure
45+
Rails.logger.warn "SMTP Error: #{e}"
46+
mark_message 'SMTP error. Check logs for more details'
47+
rescue IOError, Net::ReadTimeout => e
48+
mark_failure
49+
Rails.logger.warn "SMTP Timeout: #{e}"
50+
mark_message 'SMTP Connection error: Timeout. Check logs for more details'
51+
rescue StandardError => e
52+
# Catch any other unexpected errors
53+
mark_failure
54+
Rails.logger.warn "SMTP standard error: #{e}"
55+
mark_message 'SMTP ERROR: Could not connect. Check logs for more details'
56+
end
57+
end
58+
end
59+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
60+
2161
# Ensure Alma API is working.
2262
OkComputer::Registry.register 'alma-patron-lookup', AlmaPatronCheck.new
2363

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

27-
# Ensure connectivity to the mail system.
28-
OkComputer::Registry.register 'action-mailer', OkComputer::ActionMailerCheck.new
67+
# Ensure SMTP can connect
68+
OkComputer::Registry.register 'mail-connectivity', MailConnectivityCheck.new if ActionMailer::Base.delivery_method == :smtp
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
require 'rails_helper'
2+
require 'net/smtp'
3+
4+
RSpec.describe MailConnectivityCheck do
5+
subject(:check) { described_class.new }
6+
7+
let(:smtp_settings) do
8+
{
9+
address: 'smtp.example.com',
10+
port: 587,
11+
domain: 'example.com',
12+
user_name: 'user',
13+
password: 'password',
14+
authentication: 'plain',
15+
tls: true
16+
}
17+
end
18+
19+
before do
20+
allow(ActionMailer::Base).to receive(:smtp_settings).and_return(smtp_settings)
21+
allow(Rails.logger).to receive(:warn)
22+
end
23+
24+
describe '#check' do
25+
context 'when SMTP connection succeeds' do
26+
before do
27+
allow(Net::SMTP).to receive(:start).and_yield
28+
end
29+
30+
it 'marks the check as successful' do
31+
check.check
32+
33+
expect(check.success?).to be(true)
34+
expect(check.message).to eq('Connection for smtp successful')
35+
end
36+
end
37+
38+
context 'when authentication fails' do
39+
before do
40+
allow(Net::SMTP).to receive(:start)
41+
.and_raise(Net::SMTPAuthenticationError.new('auth failed'))
42+
end
43+
44+
it 'marks failure and logs authentication error' do
45+
check.check
46+
47+
expect(check.success?).to be(false)
48+
expect(check.message)
49+
.to eq('SMTP Error: Authentication failed. Check logs for more details')
50+
expect(Rails.logger)
51+
.to have_received(:warn).with(/SMTP authentication error/)
52+
end
53+
end
54+
55+
context 'when SMTP protocol errors occur' do
56+
[
57+
Net::SMTPServerBusy,
58+
Net::SMTPSyntaxError,
59+
Net::SMTPFatalError,
60+
Net::SMTPUnknownError
61+
].each do |error_class|
62+
it "handles #{error_class.name}" do
63+
allow(Net::SMTP).to receive(:start)
64+
.and_raise(error_class.new('smtp error'))
65+
66+
check.check
67+
68+
expect(check.success?).to be(false)
69+
expect(check.message)
70+
.to eq('SMTP error. Check logs for more details')
71+
expect(Rails.logger)
72+
.to have_received(:warn).with(/SMTP Error/)
73+
end
74+
end
75+
end
76+
77+
context 'when a timeout occurs' do
78+
[IOError, Net::ReadTimeout].each do |error_class|
79+
it "handles #{error_class.name}" do
80+
allow(Net::SMTP).to receive(:start)
81+
.and_raise(error_class.new('timeout'))
82+
83+
check.check
84+
85+
expect(check.success?).to be(false)
86+
expect(check.message)
87+
.to eq('SMTP Connection error: Timeout. Check logs for more details')
88+
expect(Rails.logger)
89+
.to have_received(:warn).with(/SMTP Timeout/)
90+
end
91+
end
92+
end
93+
94+
context 'when an unexpected error occurs' do
95+
before do
96+
allow(Net::SMTP).to receive(:start)
97+
.and_raise(StandardError.new('failed'))
98+
end
99+
100+
it 'marks failure and logs standard error' do
101+
check.check
102+
103+
expect(check.success?).to be(false)
104+
expect(check.message)
105+
.to eq('SMTP ERROR: Could not connect. Check logs for more details')
106+
expect(Rails.logger)
107+
.to have_received(:warn).with(/SMTP standard error/)
108+
end
109+
end
110+
end
111+
end

spec/request/okcomputer_spec.rb

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,61 @@
11
require 'rails_helper'
22

33
RSpec.describe 'OKComputer', type: :request do
4-
before { allow(Alma::User).to receive(:find).and_return(Alma::User.new) }
4+
before do
5+
allow(Alma::User).to receive(:find).and_return(Alma::User.new)
6+
end
57

68
it 'is mounted at /okcomputer' do
79
get '/okcomputer'
810
expect(response).to have_http_status :ok
911
end
1012

11-
it 'returns all checks to /health' do
12-
get '/health'
13-
expect(response.parsed_body.keys).to match_array %w[
14-
action-mailer
15-
alma-patron-lookup
16-
default
17-
database
18-
database-migrations
19-
]
20-
pending 'https://github.com/emmahsax/okcomputer/pull/21'
21-
expect(response).to have_http_status :ok
13+
context 'without SMTP enabled' do
14+
before do
15+
allow(ActionMailer::Base).to receive(:delivery_method).and_return(:test)
16+
17+
OkComputer::Registry.instance_variable_set(:@checks, {})
18+
load Rails.root.join('config/initializers/okcomputer.rb')
19+
end
20+
21+
it 'returns checks to /health' do
22+
get '/health'
23+
expect(response.parsed_body.keys).to match_array %w[
24+
default
25+
database
26+
alma-patron-lookup
27+
database-migrations
28+
]
29+
end
2230
end
2331

24-
it 'fails when Alma lookups fail' do
25-
expect(Alma::User).to receive(:find).and_raise('Uh oh!')
26-
get '/health'
27-
expect(response).not_to have_http_status :ok
32+
context 'with SMTP enabled' do
33+
before do
34+
allow(ActionMailer::Base).to receive(:delivery_method).and_return(:smtp)
35+
allow(Net::SMTP).to receive(:start)
36+
37+
OkComputer::Registry.instance_variable_set(:@checks, {})
38+
load Rails.root.join('config/initializers/okcomputer.rb')
39+
end
40+
41+
it 'returns all checks to /health' do
42+
get '/health'
43+
expect(response.parsed_body.keys).to match_array %w[
44+
default
45+
database
46+
alma-patron-lookup
47+
database-migrations
48+
mail-connectivity
49+
]
50+
end
51+
end
52+
53+
context 'when Alma lookups fail' do
54+
it 'returns a non-200 response' do
55+
expect(Alma::User).to receive(:find).and_raise('Uh oh!')
56+
get '/health'
57+
expect(response).not_to have_http_status :ok
58+
end
2859
end
60+
2961
end

0 commit comments

Comments
 (0)