Skip to content

Commit d13d617

Browse files
committed
Add unit tests for TwilioVerifyService
1 parent d98ffb6 commit d13d617

File tree

1 file changed

+125
-0
lines changed

1 file changed

+125
-0
lines changed
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe TwilioVerifyService, type: :service do
4+
let(:user) { create :twilio_verify_user }
5+
let(:phone_number) { user.mobile_phone }
6+
let(:formatted_phone_number) { "+1#{phone_number}" }
7+
let(:twilio_account_sid) { '123456789' }
8+
let(:twilio_auth_token) { '123456789' }
9+
let(:twilio_verify_service_sid) { '123456789' }
10+
11+
before do
12+
allow(Rails.application.credentials).to receive(:twilio_account_sid).and_return twilio_account_sid
13+
allow(Rails.application.credentials).to receive(:twilio_auth_token).and_return twilio_auth_token
14+
allow(Rails.application.credentials).to receive(:twilio_verify_service_sid).and_return twilio_verify_service_sid
15+
end
16+
17+
describe 'Missing Twilio credentials' do
18+
let(:twilio_auth_token) { '' }
19+
20+
it "raises 'Missing Twilio credentials' exception if any credentials are missing" do
21+
expect { described_class.new }.to raise_error 'Missing Twilio credentials'
22+
end
23+
end
24+
25+
# https://www.twilio.com/docs/verify/sms
26+
describe 'SMS 2FA' do
27+
let(:twilio_client) { Twilio::REST::Client.new(twilio_account_sid, twilio_auth_token) }
28+
let(:twilio_client_verify_service) { double('Twilio::REST::Verify::V2::ServiceContext') }
29+
30+
before do
31+
allow(Twilio::REST::Client).to receive(:new).with(twilio_account_sid, twilio_auth_token).and_return twilio_client
32+
allow(twilio_client).to receive_message_chain(:verify, :services).with(twilio_verify_service_sid).and_return twilio_client_verify_service
33+
end
34+
35+
describe '.send_sms_token' do
36+
it 'calls on the Twilio Verify API to send a one-time code to the given phone number via SMS' do
37+
expect(twilio_client_verify_service).to receive_message_chain(:verifications, :create).with(to: formatted_phone_number, channel: 'sms')
38+
described_class.send_sms_token(phone_number)
39+
end
40+
end
41+
42+
describe '.verify_sms_token' do
43+
let(:token) { '123456' }
44+
45+
it 'calls on the Twilio Verify API to verify a one-time code that was previously sent to the given phone number via SMS' do
46+
expect(twilio_client_verify_service).to receive_message_chain(:verification_checks, :create).with(to: formatted_phone_number, code: token)
47+
described_class.verify_sms_token(phone_number, token)
48+
end
49+
end
50+
end
51+
52+
# https://www.twilio.com/docs/verify/quickstarts/totp
53+
describe 'TOTP 2FA' do
54+
let(:twilio_client) { Twilio::REST::Client.new(twilio_account_sid, twilio_auth_token) }
55+
let(:twilio_client_verify_service) { double('Twilio::REST::Verify::V2::ServiceContext') }
56+
let(:twilio_client_entity) { double('Twilio::REST::Verify::V2::ServiceContext::EntityContext') }
57+
58+
before do
59+
allow(Twilio::REST::Client).to receive(:new).with(twilio_account_sid, twilio_auth_token).and_return twilio_client
60+
allow(twilio_client).to receive_message_chain(:verify, :v2, :services).with(twilio_verify_service_sid).and_return twilio_client_verify_service
61+
allow(twilio_client_verify_service).to receive(:entities).with("test-#{user.id}").and_return twilio_client_entity
62+
end
63+
64+
describe '.setup_totp_service' do
65+
it 'calls on the Twilio Verify API to setup TOTP for a given user' do
66+
new_factor = double(:twilio_verify_totp_new_factor, sid: '123ABC')
67+
expect(twilio_client_entity).to receive_message_chain(:new_factors, :create).with(friendly_name: user.to_s, factor_type: 'totp').and_return new_factor
68+
69+
expect(user.twilio_totp_factor_sid).to be_nil
70+
result = described_class.setup_totp_service(user)
71+
72+
expect(user.reload.twilio_totp_factor_sid).to eq result.sid
73+
end
74+
end
75+
76+
describe '.register_totp_service' do
77+
let(:token) { '123456' }
78+
let(:new_factor) { double(:twilio_verify_totp_new_factor, status: 'verified') }
79+
80+
before do
81+
user.update!(twilio_totp_factor_sid: '123ABC')
82+
allow(twilio_client_entity).to receive(:factors).with(user.twilio_totp_factor_sid).and_return new_factor
83+
end
84+
85+
it 'calls on the Twilio Verify API to register TOTP for a given user' do
86+
expect(new_factor).to receive(:update).with(auth_payload: token).and_return new_factor
87+
88+
result = described_class.register_totp_service(user, token)
89+
expect(result.status).to eq 'verified'
90+
end
91+
92+
context 'when an invalid token is provided' do
93+
let(:new_factor) { double(:twilio_verify_totp_new_factor, status: 'unverified') }
94+
95+
it 'returns an unverified status' do
96+
expect(new_factor).to receive(:update).with(auth_payload: token).and_return new_factor
97+
98+
result = described_class.register_totp_service(user, token)
99+
expect(result.status).to eq 'unverified'
100+
end
101+
end
102+
end
103+
104+
describe '.verify_totp_token' do
105+
let(:token) { '123456' }
106+
107+
before { user.update!(twilio_totp_factor_sid: '123ABC') }
108+
109+
it 'calls on the Twilio Verify API to verify a TOTP based one time code for a given user' do
110+
expect(twilio_client_entity).to receive_message_chain(:challenges, :create).with(auth_payload: token, factor_sid: user.twilio_totp_factor_sid)
111+
described_class.verify_totp_token(user, token)
112+
end
113+
end
114+
end
115+
116+
describe '.e164_format' do
117+
# https://en.wikipedia.org/wiki/E.164
118+
it 'formats supplied phone number to the e164 format' do
119+
expect(described_class.e164_format(phone_number)).to eq formatted_phone_number
120+
expect(described_class.e164_format('(123) 456-7890')).to eq '+11234567890'
121+
expect(described_class.e164_format('123-456-7890')).to eq '+11234567890'
122+
expect(described_class.e164_format('1234567890')).to eq '+11234567890'
123+
end
124+
end
125+
end

0 commit comments

Comments
 (0)