mote_sms 1.2.0 → 1.3.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,6 +3,8 @@ require 'net/http'
3
3
  require 'phony'
4
4
  require 'logger'
5
5
 
6
+ require 'mote_sms/transports/http_client'
7
+
6
8
  module MoteSMS
7
9
 
8
10
  # MoteSMS::MobileTechnicsTransport provides the implementation to
@@ -16,40 +18,14 @@ module MoteSMS
16
18
  # # => ['000-791234', '001-7987324']
17
19
  #
18
20
  class MobileTechnicsTransport
19
-
20
21
  # Maximum recipients allowed by API
21
22
  MAX_RECIPIENT = 100
22
23
 
23
- # Path to certificates
24
- CERTS_PATH = File.expand_path File.join(File.dirname(__FILE__), '..', 'ssl_certs')
25
-
26
24
  # Custom exception subclass.
27
25
  ServiceError = Class.new(::Exception)
28
26
 
29
27
  # Readable attributes
30
- attr_reader :endpoint, :username, :password, :options
31
-
32
- # Internal: The default certificate store, adds all *CA.pem files
33
- # from mote_sms/ssl_certs directory.
34
- #
35
- # Returns a OpenSSL::X509::Store
36
- def self.default_cert_store
37
- @cert_store ||= OpenSSL::X509::Store.new.tap do |store|
38
- Dir["#{CERTS_PATH}/*CA.pem"].each { |c| store.add_file c }
39
- end
40
- end
41
-
42
- # Internal: Load a X509::Certificate based on the hostname, used to handle
43
- # server certificate fingerprinting.
44
- #
45
- # host - The String with the hostname
46
- #
47
- # Returns OpenSSL::X509::Certificate or nil if no certificate for this host
48
- # is found
49
- def self.fingerprint_cert(host)
50
- cert = "#{CERTS_PATH}/#{host}.pem"
51
- OpenSSL::X509::Certificate.new(File.read(cert)) if File.exists?(cert)
52
- end
28
+ attr_reader :endpoint, :username, :password, :options, :http_client
53
29
 
54
30
  # Public: Global default parameters for sending messages, Procs/lambdas
55
31
  # are evaluated on #deliver. Ensure to use only symbols as keys. Contains
@@ -61,13 +37,8 @@ module MoteSMS
61
37
  #
62
38
  # Returns Hash with options.
63
39
  def self.defaults
64
- @@options ||= {
65
- allow_adaption: true,
66
- ssl: ->(http) {
67
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
68
- http.verify_depth = 9
69
- http.cert_store = self.default_cert_store
70
- }
40
+ @options ||= {
41
+ allow_adaption: true
71
42
  }
72
43
  end
73
44
 
@@ -76,7 +47,7 @@ module MoteSMS
76
47
  #
77
48
  # Returns Logger instance.
78
49
  def self.logger
79
- @@logger ||= ::Logger.new($stdout)
50
+ @logger ||= ::Logger.new($stdout)
80
51
  end
81
52
 
82
53
  # Public: Change the logger used to log all HTTP requests to
@@ -86,7 +57,7 @@ module MoteSMS
86
57
  #
87
58
  # Returns nothing.
88
59
  def self.logger=(logger)
89
- @@logger = logger
60
+ @logger = logger
90
61
  end
91
62
 
92
63
  # Public: Create a new instance using specified endpoint, username
@@ -99,11 +70,17 @@ module MoteSMS
99
70
  # :ssl - SSL client options
100
71
  #
101
72
  # Returns a new instance.
102
- def initialize(endpoint, username, password, options = nil)
73
+ def initialize(endpoint, username, password, options = {})
103
74
  @endpoint = URI.parse(endpoint)
104
75
  @username = username
105
76
  @password = password
106
- @options = options || {}
77
+
78
+ @options = self.class.defaults.merge(options)
79
+
80
+ @http_client = Transports::HttpClient.new(endpoint,
81
+ proxy_address: @options[:proxy_address],
82
+ proxy_port: @options[:proxy_port],
83
+ ssl: @options[:ssl])
107
84
  end
108
85
 
109
86
  # Public: Delivers message using mobile technics HTTP/S API.
@@ -112,84 +89,26 @@ module MoteSMS
112
89
  # options - The Hash with service specific options.
113
90
  #
114
91
  # Returns Array with sender ids.
115
- def deliver(message, options = {})
92
+ def deliver(message, deliver_options = {})
116
93
  raise ArgumentError, "too many recipients, max. is #{MAX_RECIPIENT} (current: #{message.to.length})" if message.to.length > MAX_RECIPIENT
117
94
 
118
- # Prepare request
119
- options = prepare_options options
120
- http = http_client options
121
- request = http_request post_params(message, options)
95
+ request = Net::HTTP::Post.new(endpoint.request_uri).tap do |req|
96
+ req.body = URI.encode_www_form post_params(message, options.merge(deliver_options))
97
+ req.content_type = 'application/x-www-form-urlencoded; charset=utf-8'
98
+ end
122
99
 
123
- # Log as `curl` request
124
100
  self.class.logger.debug "curl -X#{request.method} '#{endpoint}' -d '#{request.body}'"
125
101
 
126
- # Perform request
127
- resp = http.request(request)
102
+ resp = http_client.request(request)
128
103
 
129
- # Handle errors
130
104
  raise ServiceError, "endpoint did respond with #{resp.code}" unless resp.code.to_i == 200
131
105
  raise ServiceError, "unable to deliver message to all recipients (CAUSE: #{resp.body.strip})" unless resp.body.split("\n").all? { |l| l =~ /Result_code: 00/ }
132
106
 
133
- # extract Nth-SmsIds
134
107
  resp['X-Nth-SmsId'].split(',')
135
108
  end
136
109
 
137
110
  private
138
111
 
139
- # Internal: Prepare request including body, headers etc.
140
- #
141
- # uri - The URI from the endpoint.
142
- # params - The Array with the attributes.
143
- #
144
- # Returns Net::HTTP::Post instance.
145
- def http_request(params)
146
- Net::HTTP::Post.new(endpoint.request_uri).tap do |request|
147
- request.body = URI.encode_www_form params
148
- request.content_type = 'application/x-www-form-urlencoded; charset=utf-8'
149
- end
150
- end
151
-
152
- # Internal: Build new Net::HTTP instance, enables SSL if requested.
153
- #
154
- # options - The Hash with all options
155
- #
156
- # Returns Net::HTTP client instance.
157
- def http_client(options)
158
- Net::HTTP.new(endpoint.host, endpoint.port).tap do |http|
159
- if endpoint.instance_of?(URI::HTTPS)
160
- cert = self.class.fingerprint_cert(endpoint.host)
161
- http.use_ssl = true
162
- http.verify_callback = ->(ok, store) { verify_fingerprint(cert.serial, ok, store) } if cert
163
- options[:ssl].call(http) if options[:ssl].respond_to?(:call)
164
- end
165
- end
166
- end
167
-
168
- # Public: Verify SSL server certifcate when a certificate is available in
169
- # mote_sms/ssl_certs/{host}.pem. Implemented to return false if first
170
- # certificate in chain does not match the expected serial.
171
- #
172
- # serial - The expected server certificates serial
173
- # ok - The Boolean forwarded by verify_callback
174
- # store - The OpenSSL::X509::Store instance with the chain
175
- #
176
- # Returns Boolean
177
- def verify_fingerprint(serial, ok, store)
178
- return false unless store.chain.first.serial == serial
179
- ok
180
- end
181
-
182
- # Internal: Merge defaults from class and instance with options
183
- # supplied to #deliver. Removes `:http` options, because those
184
- # are only for the HTTP client to set ssl verify mode et all.
185
- #
186
- # options - The Hash to merge with #defaults and #options.
187
- #
188
- # Returns Hash.
189
- def prepare_options(options)
190
- options = self.class.defaults.merge(self.options).merge(options)
191
- end
192
-
193
112
  # Internal: Prepare parameters for sending POST to endpoint, merges defaults,
194
113
  # local and per call options, adds message related informations etc etc.
195
114
  #
@@ -198,7 +117,7 @@ module MoteSMS
198
117
  #
199
118
  # Returns Array with params.
200
119
  def post_params(message, options)
201
- params = options.reject { |key, v| key == :ssl }
120
+ params = options.reject { |key, v| [:proxy_address, :proxy_port, :ssl].include?(key) }
202
121
  params.merge! username: self.username,
203
122
  password: self.password,
204
123
  origin: message.from ? message.from.to_number : params[:origin],
@@ -0,0 +1,106 @@
1
+ require 'phony'
2
+ require 'logger'
3
+
4
+ require 'mote_sms/transports/http_client'
5
+
6
+ module MoteSMS
7
+
8
+ # MoteSMS::MobileTechnicsTransport provides the implementation to
9
+ # send messages using nth.ch bulk SMS HTTP/S API. Each customer has
10
+ # custom endpoint (with port) and username/password.
11
+ #
12
+ # Examples:
13
+ #
14
+ # transport = MoteSMS::SwisscomTransport.new 'https://api.swisscom.com/', 'ApIkEy'
15
+ # transport.deliver message
16
+ # # => ['000-791234', '001-7987324']
17
+ #
18
+ class SwisscomTransport
19
+ # Maximum recipients allowed by API
20
+ MAX_RECIPIENT = 1
21
+
22
+ # Custom exception subclass.
23
+ ServiceError = Class.new(::Exception)
24
+
25
+ # Readable attributes
26
+
27
+ # Public: Logger used to log HTTP requests to mobile
28
+ # technics API endpoint.
29
+ #
30
+ # Returns Logger instance.
31
+ def self.logger
32
+ @@logger ||= ::Logger.new($stdout)
33
+ end
34
+
35
+ # Public: Change the logger used to log all HTTP requests to
36
+ # the endpoint.
37
+ #
38
+ # logger - The Logger instance, should at least respond to #debug, #error.
39
+ #
40
+ # Returns nothing.
41
+ def self.logger=(logger)
42
+ @@logger = logger
43
+ end
44
+
45
+ attr_reader :endpoint, :api_key, :from_number, :options, :http_client
46
+
47
+ # Public: Create a new instance using specified endpoint, api_key
48
+ # and password.
49
+ #
50
+ # endpoint - The swisscom base url of the API
51
+ # api_key - The String with the API key.
52
+ # from_number - The phone number to send from (mandatory @ swisscom)
53
+ # options - The Hash with additional URL params passed to mobile techics endpoint
54
+ #
55
+ # Returns a new instance.
56
+ def initialize(endpoint, api_key, from_number = nil, options = {})
57
+ @endpoint = URI.parse(endpoint)
58
+ @api_key = api_key
59
+ @from_number = from_number
60
+ @options = options
61
+
62
+ @http_client = Transports::HttpClient.new(endpoint,
63
+ proxy_address: options[:proxy_address],
64
+ proxy_port: options[:proxy_port],
65
+ ssl: options[:ssl])
66
+ end
67
+
68
+ # Public: Delivers message using mobile technics HTTP/S API.
69
+ #
70
+ # message - The MoteSMS::Message instance to send.
71
+ # options - The Hash with service specific options.
72
+ #
73
+ # Returns Array with sender ids.
74
+ def deliver(message, options = {})
75
+ raise ArgumentError, "too many recipients, max. is #{MAX_RECIPIENT} (current: #{message.to.length})" if message.to.length > MAX_RECIPIENT
76
+
77
+ # Prepare request
78
+ request = Net::HTTP::Post.new("/messaging/v1/sms").tap do |request|
79
+ request.body = post_params(message)
80
+ request.content_type = 'application/json; charset=utf-8'
81
+ request['Accept'] = 'application/json; charset=utf-8'
82
+ request['client_id'] = api_key
83
+ end
84
+
85
+ # Log as `curl` request
86
+ self.class.logger.debug "curl -X#{request.method} '#{endpoint}' -d '#{request.body}'"
87
+
88
+ # Perform request
89
+ resp = http.request(request)
90
+
91
+ # Handle errors
92
+ raise ServiceError, "endpoint did respond with #{resp.code} and #{resp.body}" unless resp.code.to_i == 201
93
+ self.class.logger.debug resp.body
94
+ end
95
+
96
+ private
97
+
98
+ def post_params(message)
99
+ { to: prepare_numbers(message.to), text: message.body }
100
+ end
101
+
102
+ def prepare_numbers(number_list)
103
+ number_list.normalized_numbers.map { |n| Phony.formatted(n, format: :international_absolute, spaces: '') }.first
104
+ end
105
+ end
106
+ end
@@ -2,7 +2,9 @@ module MoteSMS
2
2
 
3
3
  # All transports live within mote_sms/transports, though should be
4
4
  # available in ruby as `MoteSMS::<Some>Transport`.
5
+ autoload :SslTransport, 'mote_sms/transports/concerns/ssl_transport'
5
6
  autoload :TestTransport, 'mote_sms/transports/test_transport'
6
7
  autoload :MobileTechnicsTransport, 'mote_sms/transports/mobile_technics_transport'
7
8
  autoload :ActionMailerTransport, 'mote_sms/transports/action_mailer_transport'
9
+ autoload :SwisscomTransport, 'mote_sms/transports/swisscom_transport'
8
10
  end
@@ -1,3 +1,3 @@
1
1
  module MoteSMS
2
- VERSION = "1.2.0"
2
+ VERSION = "1.3.0.rc1"
3
3
  end
data/lib/mote_sms.rb CHANGED
@@ -1,14 +1,33 @@
1
1
  require 'mote_sms/transports'
2
2
 
3
3
  module MoteSMS
4
- autoload :Number, 'mote_sms/number'
5
- autoload :NumberList, 'mote_sms/number_list'
6
- autoload :Message, 'mote_sms/message'
4
+ autoload :Number, 'mote_sms/number'
5
+ autoload :NumberList, 'mote_sms/number_list'
6
+ autoload :Message, 'mote_sms/message'
7
+ autoload :DeliveryJob, 'mote_sms/delivery_job'
7
8
 
8
9
  autoload :VERSION, 'mote_sms/version'
9
10
 
10
11
  # No default transport.
11
12
  @@transport = nil
13
+ @@delayed_delivery_queue = :default
14
+
15
+ # Public: Get globally defined queue name for ActiveJob, if any.
16
+ # Defaults to `nil`.
17
+ #
18
+ # Returns global ActiveJob queue name.
19
+ def self.delayed_delivery_queue
20
+ @@delayed_delivery_queue
21
+ end
22
+
23
+ # Public: Set global queue name for ActiveJob
24
+ #
25
+ # queue - A string or symbol that represents the queue to use for ActiveJob
26
+ #
27
+ # Returns nothing.
28
+ def self.delayed_delivery_queue=(queue)
29
+ @@delayed_delivery_queue = queue
30
+ end
12
31
 
13
32
  # Public: Get globally defined transport method, if any.
14
33
  # Defaults to `nil`.
@@ -40,6 +59,6 @@ module MoteSMS
40
59
  # Returns result of #deliver.
41
60
  def self.deliver(&block)
42
61
  raise ArgumentError, 'Block missing' unless block_given?
43
- Message.new(&block).deliver
62
+ Message.new(&block).deliver_now
44
63
  end
45
64
  end
data/mote_sms.gemspec CHANGED
@@ -3,26 +3,29 @@ require File.expand_path('../lib/mote_sms/version', __FILE__)
3
3
 
4
4
  Gem::Specification.new do |gem|
5
5
  gem.name = 'mote_sms'
6
- gem.authors = ['Lukas Westermann']
7
- gem.email = ['lukas.westermann@at-point.ch']
8
- gem.summary = %q{Deliver SMS using MobileTechnics HTTP API.}
9
- gem.description = %q{Unofficial ruby adapter for MobileTechnics HTTP Bulk SMS API.
6
+ gem.authors = ['Lukas Westermann', 'Loris Gavillet']
7
+ gem.email = ['lukas.westermann@at-point.ch', 'loris@at-point.ch']
8
+ gem.summary = %q{Deliver SMS using Swisscom / MobileTechnics REST API.}
9
+ gem.description = %q{Unofficial ruby adapter for Swisscom and MobileTechnics Bulk SMS APIs.
10
10
  Tries to mimick mail API, so users can switch e.g. ActionMailer
11
11
  with this SMS provider.}
12
- gem.homepage = 'https://at-point.ch/opensource'
12
+ gem.homepage = 'https://github.com/at-point/mote_sms'
13
13
 
14
14
  gem.files = %w{.gitignore Gemfile Rakefile README.md mote_sms.gemspec} + Dir['**/*.{rb,pem}']
15
- gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
15
+ gem.bindir = 'exe'
16
+ gem.executables = gem.files.grep(%r{^exe/}) { |f| File.basename(f) }
16
17
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
17
18
  gem.require_paths = %w{lib}
18
19
  gem.version = MoteSMS::VERSION
19
20
 
20
- gem.required_ruby_version = '>= 1.9'
21
+ gem.required_ruby_version = '>= 2.0'
21
22
 
22
23
  gem.add_dependency 'phony', ['>= 1.7', '< 3.0']
24
+ gem.add_dependency 'activesupport', ['>= 4.2', '< 6']
23
25
 
24
26
  gem.add_development_dependency 'rake'
25
- gem.add_development_dependency 'rspec', ['~> 2.14']
27
+ gem.add_development_dependency 'rspec', ['~> 2.4']
26
28
  gem.add_development_dependency 'webmock', ['~> 1.8.0']
27
- gem.add_development_dependency 'actionmailer', ['>= 3.2']
29
+ gem.add_development_dependency 'actionmailer', ['>= 4.2', '< 6']
30
+ gem.add_development_dependency 'activejob', ['>= 4.2', '< 6']
28
31
  end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+ require 'mote_sms'
3
+
4
+ describe MoteSMS::DeliveryJob do
5
+ subject { described_class.new }
6
+
7
+ context '#perform' do
8
+ it 'creates a new message based on the params and delivers it' do
9
+ expect_any_instance_of(MoteSMS::Message).to receive(:deliver_now).with(d: 123)
10
+ subject.perform('SENDER', ['41791231212'], 'This is the SMS content', d: 123)
11
+ end
12
+ end
13
+ end
@@ -1,5 +1,6 @@
1
1
  require 'spec_helper'
2
- require 'mote_sms/message'
2
+ require 'mote_sms'
3
+ require 'active_job'
3
4
 
4
5
  describe MoteSMS::Message do
5
6
  it 'can be constructed using a block' do
@@ -32,29 +33,71 @@ describe MoteSMS::Message do
32
33
  end
33
34
 
34
35
  context "#deliver" do
36
+ subject { described_class.new }
37
+
38
+ it "delegates to deliver_now and deprecates it" do
39
+ expect(subject).to receive(:deliver_now)
40
+ expect(Kernel).to receive(:warn).with('Message#deliver is deprecated and will be removed from MoteSMS. Please use #deliver_now')
41
+ subject.deliver
42
+ end
43
+ end
44
+
45
+ context "#deliver_now" do
35
46
  let(:transport) { double("Some Transport") }
36
- subject { described_class.new(transport) }
47
+ subject {
48
+ expect(Kernel).to receive(:warn).with('Message#new(transport) is deprecated and will be removed from MoteSMS')
49
+ described_class.new(transport)
50
+ }
37
51
 
38
52
  it "sends messages to transport" do
39
53
  expect(transport).to receive(:deliver).with(subject, {})
40
- subject.deliver
54
+ subject.deliver_now
41
55
  end
42
56
 
43
57
  it "can pass additional attributes to transport" do
44
58
  expect(transport).to receive(:deliver).with(subject, serviceid: "myapplication")
45
- subject.deliver serviceid: "myapplication"
59
+ subject.deliver_now serviceid: "myapplication"
46
60
  end
47
61
 
48
- it "can override per message transport using :transport option" do
62
+ it "can override per message transport using :transport option and it deprecates it" do
49
63
  expect(transport).to_not receive(:deliver)
50
- subject.deliver transport: double(deliver: true)
64
+ expect(Kernel).to receive(:warn).with('options[:transport] in Message#deliver_now is deprecated and will be removed from MoteSMS')
65
+ subject.deliver_now transport: double(deliver: true)
51
66
  end
52
67
 
53
68
  it "uses global MoteSMS.transport if no per message transport defined" do
54
69
  message = described_class.new
55
70
  expect(transport).to receive(:deliver).with(message, {})
56
71
  expect(MoteSMS).to receive(:transport) { transport }
57
- message.deliver
72
+ message.deliver_now
73
+ end
74
+ end
75
+
76
+ context "#deliver_later" do
77
+ before { MoteSMS.transport = MoteSMS::TestTransport }
78
+ after { MoteSMS.transport = nil }
79
+
80
+ subject do
81
+ described_class.new do
82
+ from 'SENDER'
83
+ to '+41 79 123 12 12'
84
+ body 'This is the SMS content'
85
+ end
86
+ end
87
+
88
+ it "can not override per message transport using :transport option and it deprecates it" do
89
+ expect(Kernel).to receive(:warn).with('options[:transport] is not supported in Message#deliveer_later')
90
+ expect(MoteSMS::DeliveryJob).to_not receive(:perform_later)
91
+ subject.deliver_later transport: double(deliver: true)
92
+ end
93
+
94
+ it "queues the delivery in the DeliveryJob" do
95
+ subject.deliver_later
96
+ job = ActiveJob::Base.queue_adapter.enqueued_jobs.first
97
+ expect(job[:job]).to eq MoteSMS::DeliveryJob
98
+ expect(job[:args]).to include 'SENDER'
99
+ expect(job[:args]).to include ['41791231212']
100
+ expect(job[:args]).to include 'This is the SMS content'
58
101
  end
59
102
  end
60
103
  end
@@ -2,36 +2,38 @@ require 'spec_helper'
2
2
  require 'mote_sms/number'
3
3
 
4
4
  describe MoteSMS::Number do
5
+ shared_examples_for 'parsed number' do |formatted, e164|
6
+ it '#to_s returns a formatted number' do
7
+ expect(subject.to_s).to eq formatted
8
+ end
9
+
10
+ it '#number returns an E164 number' do
11
+ expect(subject.number).to eq e164
12
+ end
13
+
14
+ it '#to_number returns an E164 number' do
15
+ expect(subject.to_number).to eq e164
16
+ end
17
+ end
18
+
5
19
  context 'normalized number' do
6
20
  subject { described_class.new('41443643533') }
7
-
8
- its(:to_s) { should == '+41 44 364 35 33' }
9
- its(:number) { should == '41443643533' }
10
- its(:to_number) { should == '41443643533' }
21
+ it_behaves_like 'parsed number', '+41 44 364 35 33', '41443643533'
11
22
  end
12
23
 
13
24
  context 'E164 conforming number' do
14
25
  subject { described_class.new('+41 44 3643533') }
15
-
16
- its(:to_s) { should == '+41 44 364 35 33' }
17
- its(:number) { should == '41443643533' }
18
- its(:to_number) { should == '41443643533' }
26
+ it_behaves_like 'parsed number', '+41 44 364 35 33', '41443643533'
19
27
  end
20
28
 
21
29
  context 'handles local numbers' do
22
30
  subject { described_class.new('079 700 50 90', cc: '41') }
23
-
24
- its(:to_s) { should == '+41 79 700 50 90' }
25
- its(:number) { should == '41797005090'}
26
- its(:to_number) { should == '41797005090' }
31
+ it_behaves_like 'parsed number', '+41 79 700 50 90', '41797005090'
27
32
  end
28
33
 
29
34
  context 'handles numbers with NDC regexp' do
30
35
  subject { described_class.new('079 700 50 90', cc: '41', ndc: /(44|79)/) }
31
-
32
- its(:to_s) { should == '+41 79 700 50 90' }
33
- its(:number) { should == '41797005090' }
34
- its(:to_number) { should == '41797005090' }
36
+ it_behaves_like 'parsed number', '+41 79 700 50 90', '41797005090'
35
37
  end
36
38
 
37
39
  context 'non conforming number' do
@@ -50,11 +52,11 @@ describe MoteSMS::Number do
50
52
 
51
53
  context 'vanity numbers' do
52
54
  subject { described_class.new('0800-vanity', vanity: true) }
55
+ it_behaves_like 'parsed number', '0800VANITY', '0800VANITY'
53
56
 
54
- its(:to_s) { should == '0800VANITY' }
55
- its(:number) { should == '0800VANITY' }
56
- its(:to_number) { should == '0800VANITY' }
57
- its(:vanity?) { should be_truthy }
57
+ it 'is a #vanity? number' do
58
+ expect(subject.vanity?).to be_truthy
59
+ end
58
60
 
59
61
  it 'raises error if more than 11 alpha numeric chars' do
60
62
  expect { described_class.new('1234567890AB', vanity: true) }.to raise_error(ArgumentError, /invalid vanity/i)
@@ -0,0 +1,87 @@
1
+ require 'spec_helper'
2
+ require 'mote_sms/transports/http_client'
3
+
4
+ describe Transports::HttpClient do
5
+ subject { described_class.new('https://example.org/') }
6
+
7
+ context 'Certificate checks', http: true do
8
+ context 'api.swisscom.com' do
9
+ subject { described_class.new('https://api.swisscom.com/', enable_fingerprint: true) }
10
+
11
+ it 'makes a "successful" request, i.e. no HTTPS issues' do
12
+ request = Net::HTTP::Get.new('/')
13
+ response = subject.request(request)
14
+ expect(response).to be_a Net::HTTPInternalServerError
15
+ end
16
+ end
17
+
18
+ context 'https://bulk.mobile-gw.com:9012' do
19
+ subject { described_class.new('https://bulk.mobile-gw.com:9012', enable_fingerprint: true) }
20
+
21
+ it 'makes a "successful" request, i.e. no HTTPS issues' do
22
+ request = Net::HTTP::Get.new('/')
23
+ response = subject.request(request)
24
+ expect(response).to be_a Net::HTTPOK
25
+ end
26
+ end
27
+ end
28
+
29
+ context '#initialize' do
30
+ before { ENV.delete('MOTE_SMS_EXAMPLE_ORG_FINGERPRINT') }
31
+
32
+ it 'has a default user agent' do
33
+ expect(subject.user_agent).to eq "Ruby/mote_sms #{MoteSMS::VERSION}"
34
+ end
35
+
36
+ it 'has no proxy by default' do
37
+ expect(subject.proxy_address).to be_nil
38
+ expect(subject.proxy_port).to be_nil
39
+ end
40
+
41
+ it 'tries to load a fingerprint via hostname, when ENV is not set' do
42
+ expect(described_class).to receive(:fingerprint_host).with('example.org') { 'pem-fingerprint' }
43
+ expect(subject.fingerprint).to eq 'pem-fingerprint'
44
+ end
45
+
46
+ it 'tries to use the ENV for a fingerprint lookup' do
47
+ ENV['MOTE_SMS_EXAMPLE_ORG_FINGERPRINT'] = 'env-fingerprint'
48
+ allow(described_class).to receive(:fingerprint_host) { 'pem-fingerprint' }
49
+ expect(subject.fingerprint).to eq 'env-fingerprint'
50
+ end
51
+
52
+ context 'with enable_fingerprint: false' do
53
+ subject { described_class.new('https://example.org', enable_fingerprint: false) }
54
+
55
+ it 'can skip fingerprinting by setting enable_fingerprint: false' do
56
+ ENV['MOTE_SMS_EXAMPLE_ORG_FINGERPRINT'] = 'env-fingerprint'
57
+ allow(described_class).to receive(:fingerprint_host) { 'pem-fingerprint' }
58
+ expect(subject.fingerprint).to be_nil
59
+ end
60
+ end
61
+ end
62
+
63
+ context '#https?' do
64
+ it 'returns true when it is a HTTPS url' do
65
+ expect(subject.https?).to be_truthy
66
+ end
67
+
68
+ it 'returns false (of course) when it is just HTTP' do
69
+ expect(described_class.new('http://foo.example.org').https?).to be_falsey
70
+ end
71
+ end
72
+
73
+ context '#request' do
74
+ let(:request) { Net::HTTP::Get.new('/') }
75
+
76
+ before {
77
+ stub_request(:get, "https://example.org/").
78
+ with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent' => 'Ruby/mote_sms 1.2.0' }).
79
+ to_return(status: 200, body: "", headers: {})
80
+ }
81
+
82
+ it 'submits a request and overrides the UA' do
83
+ response = subject.request(request)
84
+ expect(response).to be_a Net::HTTPOK
85
+ end
86
+ end
87
+ end