sms_safe 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/.travis.yml +18 -0
- data/Appraisals +19 -0
- data/Gemfile +6 -0
- data/LICENSE +22 -0
- data/README.md +173 -0
- data/Rakefile +15 -0
- data/gemfiles/mail_2_4_4.gemfile +8 -0
- data/gemfiles/mail_2_4_4.gemfile.lock +107 -0
- data/gemfiles/mail_2_5_3.gemfile +8 -0
- data/gemfiles/mail_2_5_3.gemfile.lock +107 -0
- data/gemfiles/mail_2_5_4.gemfile +8 -0
- data/gemfiles/mail_2_5_4.gemfile.lock +106 -0
- data/gemfiles/mail_2_6_1.gemfile +8 -0
- data/gemfiles/mail_2_6_1.gemfile.lock +101 -0
- data/gemfiles/mail_2_6_3.gemfile +8 -0
- data/gemfiles/mail_2_6_3.gemfile.lock +101 -0
- data/lib/sms_safe.rb +10 -0
- data/lib/sms_safe/config.rb +49 -0
- data/lib/sms_safe/hooks.rb +60 -0
- data/lib/sms_safe/interceptor.rb +151 -0
- data/lib/sms_safe/interceptors/action_texter.rb +32 -0
- data/lib/sms_safe/interceptors/nexmo.rb +31 -0
- data/lib/sms_safe/interceptors/twilio.rb +31 -0
- data/lib/sms_safe/message.rb +21 -0
- data/lib/sms_safe/version.rb +3 -0
- data/sms_safe.gemspec +49 -0
- data/test/hooks_test.rb +149 -0
- data/test/interceptor_test.rb +240 -0
- data/test/interceptors/action_texter_test.rb +76 -0
- data/test/interceptors/nexmo_test.rb +73 -0
- data/test/interceptors/twilio_test.rb +72 -0
- data/test/test_helper.rb +88 -0
- metadata +288 -0
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'mail'
|
2
|
+
|
3
|
+
module SmsSafe
|
4
|
+
|
5
|
+
# When a message is intercepted, Interceptor decides whether we need to do anything with it,
|
6
|
+
# and does it.
|
7
|
+
# The different adaptor classes in the Interceptors module provide mapping to each of the SMS libraries peculiarities.
|
8
|
+
class Interceptor
|
9
|
+
|
10
|
+
# Method called by all the sub-classes to process the SMS being sent
|
11
|
+
# @param [Object] original_message the message we intercepted from the texter gem. May be of varying types, depending
|
12
|
+
# on which texter gem is being used.
|
13
|
+
# @return [Object] the message to send (if modified recipient / text), of the same type we received
|
14
|
+
# or nil if no SMS should be sent
|
15
|
+
def process_message(original_message)
|
16
|
+
message = convert_message(original_message)
|
17
|
+
|
18
|
+
if intercept_message?(message)
|
19
|
+
intercept_message!(message)
|
20
|
+
else
|
21
|
+
original_message
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Decides whether to intercept the message that is being sent, or to let it go through
|
26
|
+
# @param [Message] message the message we are evaluating
|
27
|
+
# @return [Boolean] whether to intercept the message (true) or let it go through (false)
|
28
|
+
def intercept_message?(message)
|
29
|
+
matching_rules = [SmsSafe.configuration.internal_phone_numbers].flatten.compact
|
30
|
+
internal_recipient = matching_rules.any? do |rule|
|
31
|
+
case rule
|
32
|
+
when String then message.to == rule
|
33
|
+
when Regexp then !!(message.to =~ rule)
|
34
|
+
when Proc then rule.call(message)
|
35
|
+
else
|
36
|
+
raise InvalidConfigSettingError.new("Ensure internal_phone_numbers is a String, a Regexp or a Proc (or an array of them). It was: #{SmsSafe.configuration.internal_phone_numbers.inspect}")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
!internal_recipient # Intercept messages that are *not* going to one of the allowed numbers
|
40
|
+
end
|
41
|
+
|
42
|
+
# Once we've decided to intercept the message, act on it, based on the intercept_mechanism set
|
43
|
+
# @param [Message] message the message we are evaluating
|
44
|
+
# @return [Object] the message to send, of the type that corresponds to the texter gem (if :redirecting)
|
45
|
+
# or nil to cancel sending (if :email or :discard)
|
46
|
+
def intercept_message!(message)
|
47
|
+
case SmsSafe.configuration.intercept_mechanism
|
48
|
+
when :redirect then redirect(message)
|
49
|
+
when :email then email(message)
|
50
|
+
when :discard then discard
|
51
|
+
else
|
52
|
+
raise InvalidConfigSettingError.new("Ensure intercept_mechanism is either :redirect, :email or :discard. It was: #{SmsSafe.configuration.intercept_mechanism.inspect}")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Decides which phone number to redirect the message to
|
57
|
+
# @param [Message] message the message we are redirecting
|
58
|
+
# @return [String] the phone number to redirect the number to
|
59
|
+
def redirect_phone_number(message)
|
60
|
+
target = SmsSafe.configuration.redirect_target
|
61
|
+
case target
|
62
|
+
when String then target
|
63
|
+
when Proc then target.call(message)
|
64
|
+
else
|
65
|
+
raise InvalidConfigSettingError.new("Ensure redirect_target is a String or a Proc. It was: #{SmsSafe.configuration.redirect_target.inspect}")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Modifies the text of the message to indicate it was redirected
|
70
|
+
# Simply appends "(SmsSafe: original_recipient_number)", for brevity
|
71
|
+
#
|
72
|
+
# @param [Message] message the message we are redirecting
|
73
|
+
# @return [String] the new text for the SMS
|
74
|
+
def redirect_text(message)
|
75
|
+
"#{message.text} (SmsSafe: #{message.to})"
|
76
|
+
end
|
77
|
+
|
78
|
+
# Sends an e-mail to the specified address, instead of
|
79
|
+
def email(message)
|
80
|
+
message_body = <<-EOS
|
81
|
+
This email was originally an SMS that SmsSafe intercepted:
|
82
|
+
|
83
|
+
From: #{message.from}
|
84
|
+
To: #{message.to}
|
85
|
+
Text: #{message.text}
|
86
|
+
|
87
|
+
Full object: #{message.original_message.inspect}
|
88
|
+
EOS
|
89
|
+
|
90
|
+
recipient = email_recipient(message)
|
91
|
+
|
92
|
+
mail = Mail.new do
|
93
|
+
from recipient
|
94
|
+
to recipient
|
95
|
+
subject 'SmsSafe: #{message.to} - #{message.text}'
|
96
|
+
body message_body
|
97
|
+
end
|
98
|
+
mail.deliver!
|
99
|
+
|
100
|
+
# Must return nil to stop the sending
|
101
|
+
nil
|
102
|
+
end
|
103
|
+
|
104
|
+
# Decides which email address to send the SMS to
|
105
|
+
# @param [Message] message the message we are emailing
|
106
|
+
# @return [String] the email address to email it to
|
107
|
+
def email_recipient(message)
|
108
|
+
target = SmsSafe.configuration.email_target
|
109
|
+
case target
|
110
|
+
when String then target
|
111
|
+
when Proc then target.call(message)
|
112
|
+
else
|
113
|
+
raise InvalidConfigSettingError.new("Ensure email_target is a String or a Proc. It was: #{SmsSafe.configuration.email_target.inspect}")
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Discards the message. Essentially doesn't do anything. Will sleep for a bit, however, if
|
118
|
+
# configuration.discard_delay is set.
|
119
|
+
def discard
|
120
|
+
# Delay to simulate the time it takes to talk to the external service
|
121
|
+
if !SmsSafe.configuration.discard_delay.nil? && SmsSafe.configuration.discard_delay > 0
|
122
|
+
delay = SmsSafe.configuration.discard_delay.to_f / 1000 # delay is specified in ms
|
123
|
+
sleep delay
|
124
|
+
end
|
125
|
+
|
126
|
+
# Must return nil to stop the sending
|
127
|
+
nil
|
128
|
+
end
|
129
|
+
|
130
|
+
# Converts an SMS message from whatever object the texter gem uses into our generic Message
|
131
|
+
# Must be overridden by each gem's interceptor
|
132
|
+
#
|
133
|
+
# @param [Object] message that is being sent
|
134
|
+
# @return [Message] the message converted into our own Message class
|
135
|
+
def convert_message(message)
|
136
|
+
raise "Must override!"
|
137
|
+
end
|
138
|
+
|
139
|
+
# Returns a modified version of the original message with new recipient and text,
|
140
|
+
# to give back to the texter gem to send.
|
141
|
+
# Must be overridden by each gem's interceptor
|
142
|
+
# Call redirect_phone_number and redirect_text to get the new recipient and text, and
|
143
|
+
# modify message.original_message
|
144
|
+
#
|
145
|
+
# @param [Message] message that is being sent, unmodified
|
146
|
+
# @return [Object] modified message to send, of the type the texter gem uses
|
147
|
+
def redirect(message)
|
148
|
+
raise "Must override!"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module SmsSafe
|
2
|
+
module Interceptors
|
3
|
+
class ActionTexter < SmsSafe::Interceptor
|
4
|
+
# This method will be called differently for each Texter Gem, it's the one that the hook likes to call
|
5
|
+
# In all cases, it's a one-liner that calls process_message in the superclass
|
6
|
+
# It could even be an alias, for all practical purposes
|
7
|
+
def delivering_sms(message)
|
8
|
+
self.process_message(message)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Converts an ActionTexter::Message into an SmsSafe::Message
|
12
|
+
# @param [ActionTexter::Message] message that is being sent by ActionTexter gem
|
13
|
+
# @return [Message] the message converted into our own Message class
|
14
|
+
def convert_message(message)
|
15
|
+
SmsSafe::Message.new(from: message.from, to: message.to, text: message.text, original_message: message)
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
# Returns a modified version of the original message with new recipient and text,
|
20
|
+
# to give back to the texter gem to send.
|
21
|
+
#
|
22
|
+
# @param [Message] message that is being sent, unmodified
|
23
|
+
# @return [ActionTexter::Message] modified message to send
|
24
|
+
def redirect(message)
|
25
|
+
original_message = message.original_message
|
26
|
+
original_message.to = redirect_phone_number(message)
|
27
|
+
original_message.text = redirect_text(message)
|
28
|
+
original_message
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module SmsSafe
|
2
|
+
module Interceptors
|
3
|
+
class Nexmo < SmsSafe::Interceptor
|
4
|
+
# This method will be called differently for each Texter Gem, it's the one that the hook likes to call
|
5
|
+
# In all cases, it's a one-liner that calls process_message in the superclass
|
6
|
+
# It could even be an alias, for all practical purposes
|
7
|
+
# def delivering_sms(message)
|
8
|
+
# self.process_message(message)
|
9
|
+
# end
|
10
|
+
|
11
|
+
# Converts a hash of params (Nexmo doesn't use a class to represent their messages) into Message
|
12
|
+
# @param [Hash] message that is being sent by Nexmo gem
|
13
|
+
# @return [Message] the message converted into our own Message class
|
14
|
+
def convert_message(message)
|
15
|
+
SmsSafe::Message.new(from: message[:from], to: message[:to], text: message[:text], original_message: message)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns a modified version of the original message with new recipient and text,
|
19
|
+
# to give back to the texter gem to send.
|
20
|
+
#
|
21
|
+
# @param [Message] message that is being sent, unmodified
|
22
|
+
# @return [Hash] modified message to send
|
23
|
+
def redirect(message)
|
24
|
+
original_message = message.original_message
|
25
|
+
original_message[:to] = redirect_phone_number(message)
|
26
|
+
original_message[:text] = redirect_text(message)
|
27
|
+
original_message
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module SmsSafe
|
2
|
+
module Interceptors
|
3
|
+
class Twilio < SmsSafe::Interceptor
|
4
|
+
# This method will be called differently for each Texter Gem, it's the one that the hook likes to call
|
5
|
+
# In all cases, it's a one-liner that calls process_message in the superclass
|
6
|
+
# It could even be an alias, for all practical purposes
|
7
|
+
# def delivering_sms(message)
|
8
|
+
# self.process_message(message)
|
9
|
+
# end
|
10
|
+
|
11
|
+
# Converts a hash of params (Twilio's call is just a hash to Client.messages) into Message
|
12
|
+
# @param [Hash] message that is being sent by Twilio gem
|
13
|
+
# @return [Message] the message converted into our own Message class
|
14
|
+
def convert_message(message)
|
15
|
+
SmsSafe::Message.new(from: message[:from], to: message[:to], text: message[:body], original_message: message)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns a modified version of the original message with new recipient and text,
|
19
|
+
# to give back to the texter gem to send.
|
20
|
+
#
|
21
|
+
# @param [Message] message that is being sent, unmodified
|
22
|
+
# @return [Hash] modified message to send
|
23
|
+
def redirect(message)
|
24
|
+
original_message = message.original_message
|
25
|
+
original_message[:to] = redirect_phone_number(message)
|
26
|
+
original_message[:body] = redirect_text(message)
|
27
|
+
original_message
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module SmsSafe
|
2
|
+
|
3
|
+
# Different texter gems will have different classes for their messages.
|
4
|
+
# This is a common class that acts as an impedance adapter. Most of our methods use this class
|
5
|
+
#
|
6
|
+
# @!attribute from
|
7
|
+
# @return [String] name or phone number of the author of the message.
|
8
|
+
# @!attribute to
|
9
|
+
# @return [String] phone number of the recipient of the message.
|
10
|
+
# @!attribute text
|
11
|
+
# @return [String] actual message to send.
|
12
|
+
# @!attribute original_message
|
13
|
+
# @return [String] original message sent by the texter gem, unmapped.
|
14
|
+
class Message
|
15
|
+
attr_accessor :from, :to, :text, :original_message
|
16
|
+
|
17
|
+
def initialize(attrs)
|
18
|
+
attrs.each { |k, v| self.send "#{k.to_s}=".to_sym, v }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/sms_safe.gemspec
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "sms_safe/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'sms_safe'
|
7
|
+
s.version = '1.0.0'
|
8
|
+
s.summary = "Keep your SMS messages from escaping into the wild during development."
|
9
|
+
s.description = %q{SmsSafe provides a safety net while you're developing an application that uses ActionTexter
|
10
|
+
or other gems to send SMS. It keeps SMS messages from escaping into the wild.
|
11
|
+
|
12
|
+
Once you've installed and configured this gem, you can rest assures that your app won't send
|
13
|
+
SMS messages to external phone numbers. Instead, messages will be routed to a phone number
|
14
|
+
you specify, converted into e-mails to you, or simply not sent at all.
|
15
|
+
|
16
|
+
SmsSafe can also include an artificial delay to simulate the call to your SMS provider,
|
17
|
+
for realistic load testing.}
|
18
|
+
s.authors = ["Daniel Magliola"]
|
19
|
+
s.email = 'dmagliola@crystalgears.com'
|
20
|
+
s.homepage = 'https://github.com/dmagliola/sms_safe'
|
21
|
+
s.license = 'MIT'
|
22
|
+
|
23
|
+
s.files = `git ls-files`.split($/)
|
24
|
+
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
25
|
+
s.test_files = s.files.grep(%r{^(test|s.features)/})
|
26
|
+
s.require_paths = ["lib"]
|
27
|
+
|
28
|
+
s.required_ruby_version = ">= 1.9.3"
|
29
|
+
|
30
|
+
s.add_runtime_dependency "mail", '>= 2.4'
|
31
|
+
|
32
|
+
s.add_development_dependency "bundler"
|
33
|
+
s.add_development_dependency "rake"
|
34
|
+
|
35
|
+
s.add_development_dependency "minitest"
|
36
|
+
s.add_development_dependency "minitest-reporters"
|
37
|
+
s.add_development_dependency "shoulda"
|
38
|
+
s.add_development_dependency "mocha"
|
39
|
+
s.add_development_dependency "simplecov"
|
40
|
+
|
41
|
+
# All the Gems we integrate with, to be able to test the hooks
|
42
|
+
s.add_development_dependency "action_texter"
|
43
|
+
s.add_development_dependency "twilio-ruby"
|
44
|
+
s.add_development_dependency "nexmo"
|
45
|
+
|
46
|
+
s.add_development_dependency "appraisal"
|
47
|
+
s.add_development_dependency "coveralls"
|
48
|
+
s.add_development_dependency "codeclimate-test-reporter"
|
49
|
+
end
|
data/test/hooks_test.rb
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
require_relative "test_helper"
|
2
|
+
require "action_texter"
|
3
|
+
require "nexmo"
|
4
|
+
require "twilio-ruby"
|
5
|
+
|
6
|
+
# These are the real integration tests. Each one of these installs the hook, then tried sending an SMS
|
7
|
+
# that will and will not get intercepted, and check that that actually happens.
|
8
|
+
class HooksTest < MiniTest::Test
|
9
|
+
context "With a basic configuration for SmsSafe" do
|
10
|
+
setup do
|
11
|
+
SmsSafe.configure do |config|
|
12
|
+
config.internal_phone_numbers = INTERNAL_PHONE_NUMBERS
|
13
|
+
config.intercept_mechanism = :discard
|
14
|
+
config.redirect_target = DEFAULT_INTERNAL_PHONE_NUMBER
|
15
|
+
end
|
16
|
+
|
17
|
+
@action_texter_client = ActionTexter::TestClient.new
|
18
|
+
end
|
19
|
+
|
20
|
+
should "hook ActionTexter" do
|
21
|
+
ActionTexter::Client.setup("Test") # Excellent, no need to mock stuff up! Thank you ActionTexter!
|
22
|
+
SmsSafe.hook!(:action_texter)
|
23
|
+
|
24
|
+
# Try to send an external message
|
25
|
+
@action_texter_client.deliveries.clear
|
26
|
+
message = ActionTexter::Message.new(from: DEFAULT_INTERNAL_PHONE_NUMBER, to: EXTERNAL_PHONE_NUMBERS.first, text: "Foo", reference: "ref-1")
|
27
|
+
result = message.deliver
|
28
|
+
|
29
|
+
# Check that return is nil and that nothing got sent
|
30
|
+
assert_nil result
|
31
|
+
assert_equal 0, @action_texter_client.deliveries.length
|
32
|
+
|
33
|
+
# Change configuration to redirect
|
34
|
+
SmsSafe.configuration.intercept_mechanism = :redirect
|
35
|
+
|
36
|
+
# Try to send an external message
|
37
|
+
@action_texter_client.deliveries.clear
|
38
|
+
message = ActionTexter::Message.new(from: DEFAULT_INTERNAL_PHONE_NUMBER, to: EXTERNAL_PHONE_NUMBERS.first, text: "Foo", reference: "ref-1")
|
39
|
+
result = message.deliver
|
40
|
+
|
41
|
+
# Check that return is appropriate and that something got sent, redirected and with changed text
|
42
|
+
refute_nil result
|
43
|
+
assert_equal 1, @action_texter_client.deliveries.length
|
44
|
+
assert_equal DEFAULT_INTERNAL_PHONE_NUMBER, @action_texter_client.deliveries.last.to
|
45
|
+
assert_operator "Foo".length, :<, @action_texter_client.deliveries.last.text.length
|
46
|
+
|
47
|
+
# Try to send an internal message
|
48
|
+
@action_texter_client.deliveries.clear
|
49
|
+
message = ActionTexter::Message.new(from: DEFAULT_INTERNAL_PHONE_NUMBER, to: INTERNAL_PHONE_NUMBERS.last, text: "Foo", reference: "ref-1")
|
50
|
+
result = message.deliver
|
51
|
+
|
52
|
+
# Check that it got delivered, unchanged
|
53
|
+
refute_nil result
|
54
|
+
assert_equal 1, @action_texter_client.deliveries.length
|
55
|
+
assert_equal INTERNAL_PHONE_NUMBERS.last, @action_texter_client.deliveries.last.to
|
56
|
+
assert_equal "Foo", @action_texter_client.deliveries.last.text
|
57
|
+
end
|
58
|
+
|
59
|
+
should "hook Nexmo" do
|
60
|
+
nexmo = Nexmo::Client.new(key: "blah", secret: "bleh")
|
61
|
+
SmsSafe.hook!(:nexmo)
|
62
|
+
|
63
|
+
# Stub the "post" method so that it doesn't actually do a post
|
64
|
+
# I'm doing that instead of stubbing "send_message", since we're already monkeypatching send_message, and I don't want those two to collide
|
65
|
+
nexmo.expects(:post).never
|
66
|
+
|
67
|
+
# Try to send an external message
|
68
|
+
result = nexmo.send_message(from: DEFAULT_INTERNAL_PHONE_NUMBER, to: EXTERNAL_PHONE_NUMBERS.first, text: 'Foo')
|
69
|
+
|
70
|
+
# Check that return is nil and that nothing got sent
|
71
|
+
assert_nil result
|
72
|
+
|
73
|
+
# Change configuration to redirect
|
74
|
+
SmsSafe.configuration.intercept_mechanism = :redirect
|
75
|
+
|
76
|
+
# Stub again so that it validates the parameters we want
|
77
|
+
nexmo.expects(:post).
|
78
|
+
once.
|
79
|
+
with() { |path, params| params[:to] == DEFAULT_INTERNAL_PHONE_NUMBER && params[:text].length > 'Foo'.length && params[:text].include?('Foo') }.
|
80
|
+
returns({ 'messages' => ['status' => 0, 'message-id' => '123456']})
|
81
|
+
|
82
|
+
# Try to send an external message
|
83
|
+
result = nexmo.send_message(from: DEFAULT_INTERNAL_PHONE_NUMBER, to: EXTERNAL_PHONE_NUMBERS.first, text: 'Foo')
|
84
|
+
|
85
|
+
# Check that return is appropriate. The rest got checked in the stub
|
86
|
+
refute_nil result
|
87
|
+
|
88
|
+
# Stub again so that it validates the parameters we want
|
89
|
+
nexmo.expects(:post).
|
90
|
+
once.
|
91
|
+
with() { |path, params| params[:to] == INTERNAL_PHONE_NUMBERS.last && params[:text] = 'Foo' }.
|
92
|
+
returns({ 'messages' => ['status' => 0, 'message-id' => '123456']})
|
93
|
+
|
94
|
+
# Try to send an internal message
|
95
|
+
result = nexmo.send_message(from: DEFAULT_INTERNAL_PHONE_NUMBER, to: INTERNAL_PHONE_NUMBERS.last, text: 'Foo')
|
96
|
+
|
97
|
+
# Check that it got delivered. The rest got checked in the stub
|
98
|
+
refute_nil result
|
99
|
+
end
|
100
|
+
|
101
|
+
should "hook Twilio" do
|
102
|
+
twilio = Twilio::REST::Client.new 'blah', 'bleh'
|
103
|
+
SmsSafe.hook!(:twilio)
|
104
|
+
|
105
|
+
# Stub the "post" method so that it doesn't actually do a post
|
106
|
+
# I'm doing that instead of stubbing "send_message", since we're already monkeypatching send_message, and I don't want those two to collide
|
107
|
+
twilio.expects(:post).never
|
108
|
+
|
109
|
+
# Try to send an external message
|
110
|
+
result = twilio.messages.create(from: DEFAULT_INTERNAL_PHONE_NUMBER, to: EXTERNAL_PHONE_NUMBERS.first, body: 'Foo')
|
111
|
+
|
112
|
+
# Check that return is nil and that nothing got sent
|
113
|
+
assert_nil result
|
114
|
+
|
115
|
+
# Change configuration to redirect
|
116
|
+
SmsSafe.configuration.intercept_mechanism = :redirect
|
117
|
+
|
118
|
+
# Stub again so that it validates the parameters we want
|
119
|
+
twilio.expects(:post).
|
120
|
+
once.
|
121
|
+
with() { |path, params| params[:to] == DEFAULT_INTERNAL_PHONE_NUMBER && params[:body].length > 'Foo'.length && params[:body].include?('Foo') }.
|
122
|
+
returns({ 'sid' => 'Message01'})
|
123
|
+
|
124
|
+
# Try to send an external message
|
125
|
+
result = twilio.messages.create(from: DEFAULT_INTERNAL_PHONE_NUMBER, to: EXTERNAL_PHONE_NUMBERS.first, body: 'Foo')
|
126
|
+
|
127
|
+
# Check that return is appropriate. The rest got checked in the stub
|
128
|
+
refute_nil result
|
129
|
+
|
130
|
+
# Stub again so that it validates the parameters we want
|
131
|
+
twilio.expects(:post).
|
132
|
+
once.
|
133
|
+
with() { |path, params| params[:to] == INTERNAL_PHONE_NUMBERS.last && params[:body] = 'Foo' }.
|
134
|
+
returns({ 'sid' => 'Message01'})
|
135
|
+
|
136
|
+
# Try to send an internal message
|
137
|
+
result = twilio.messages.create(from: DEFAULT_INTERNAL_PHONE_NUMBER, to: INTERNAL_PHONE_NUMBERS.last, body: 'Foo')
|
138
|
+
|
139
|
+
# Check that it got delivered. The rest got checked in the stub
|
140
|
+
refute_nil result
|
141
|
+
end
|
142
|
+
|
143
|
+
should "raise if hooking an invalid library" do
|
144
|
+
assert_raises(SmsSafe::InvalidConfigSettingError) do
|
145
|
+
SmsSafe.hook!(:invalid)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|