rapns_rails_2 3.5.1 → 3.6.1

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.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +2 -0
  3. data/lib/rapns/daemon/gcm/delivery.rb +4 -3
  4. data/lib/rapns/version.rb +1 -1
  5. metadata +46 -163
  6. data/config/database.yml +0 -44
  7. data/spec/support/cert_with_password.pem +0 -90
  8. data/spec/support/cert_without_password.pem +0 -59
  9. data/spec/support/simplecov_helper.rb +0 -13
  10. data/spec/support/simplecov_quality_formatter.rb +0 -8
  11. data/spec/tmp/.gitkeep +0 -0
  12. data/spec/unit/apns/app_spec.rb +0 -29
  13. data/spec/unit/apns/feedback_spec.rb +0 -9
  14. data/spec/unit/apns/notification_spec.rb +0 -215
  15. data/spec/unit/apns_feedback_spec.rb +0 -21
  16. data/spec/unit/app_spec.rb +0 -16
  17. data/spec/unit/configuration_spec.rb +0 -55
  18. data/spec/unit/daemon/apns/app_runner_spec.rb +0 -45
  19. data/spec/unit/daemon/apns/certificate_expired_error_spec.rb +0 -11
  20. data/spec/unit/daemon/apns/connection_spec.rb +0 -287
  21. data/spec/unit/daemon/apns/delivery_handler_spec.rb +0 -59
  22. data/spec/unit/daemon/apns/delivery_spec.rb +0 -101
  23. data/spec/unit/daemon/apns/disconnection_error_spec.rb +0 -18
  24. data/spec/unit/daemon/apns/feedback_receiver_spec.rb +0 -134
  25. data/spec/unit/daemon/app_runner_shared.rb +0 -83
  26. data/spec/unit/daemon/app_runner_spec.rb +0 -170
  27. data/spec/unit/daemon/batch_spec.rb +0 -219
  28. data/spec/unit/daemon/delivery_error_spec.rb +0 -13
  29. data/spec/unit/daemon/delivery_handler_collection_spec.rb +0 -37
  30. data/spec/unit/daemon/delivery_handler_shared.rb +0 -45
  31. data/spec/unit/daemon/feeder_spec.rb +0 -89
  32. data/spec/unit/daemon/gcm/app_runner_spec.rb +0 -19
  33. data/spec/unit/daemon/gcm/delivery_handler_spec.rb +0 -44
  34. data/spec/unit/daemon/gcm/delivery_spec.rb +0 -289
  35. data/spec/unit/daemon/interruptible_sleep_spec.rb +0 -68
  36. data/spec/unit/daemon/reflectable_spec.rb +0 -27
  37. data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +0 -114
  38. data/spec/unit/daemon/store/active_record_spec.rb +0 -281
  39. data/spec/unit/daemon_spec.rb +0 -157
  40. data/spec/unit/deprecatable_spec.rb +0 -32
  41. data/spec/unit/deprecation_spec.rb +0 -15
  42. data/spec/unit/embed_spec.rb +0 -50
  43. data/spec/unit/gcm/app_spec.rb +0 -4
  44. data/spec/unit/gcm/notification_spec.rb +0 -52
  45. data/spec/unit/logger_spec.rb +0 -180
  46. data/spec/unit/notification_shared.rb +0 -45
  47. data/spec/unit/notification_spec.rb +0 -4
  48. data/spec/unit/notifier_spec.rb +0 -52
  49. data/spec/unit/push_spec.rb +0 -44
  50. data/spec/unit/rapns_spec.rb +0 -9
  51. data/spec/unit/reflection_spec.rb +0 -30
  52. data/spec/unit/upgraded_spec.rb +0 -40
  53. data/spec/unit_spec_helper.rb +0 -137
@@ -1,13 +0,0 @@
1
- require 'simplecov'
2
- require './spec/support/simplecov_quality_formatter'
3
-
4
- module SimpleCovHelper
5
- def start_simple_cov(name)
6
- SimpleCov.start do
7
- add_filter '/spec/'
8
- add_filter '/lib/generators'
9
- command_name name
10
- formatter SimpleCov::Formatter::QualityFormatter
11
- end
12
- end
13
- end
@@ -1,8 +0,0 @@
1
- class SimpleCov::Formatter::QualityFormatter
2
- def format(result)
3
- SimpleCov::Formatter::HTMLFormatter.new.format(result)
4
- File.open("coverage/covered_percent", "w") do |f|
5
- f.puts result.source_files.covered_percent.to_f
6
- end
7
- end
8
- end
File without changes
@@ -1,29 +0,0 @@
1
- require File.expand_path("spec/unit_spec_helper")
2
-
3
- describe Rapns::App do
4
- it 'does not validate an app with an invalid certificate' do
5
- app = Rapns::Apns::App.new(:name => 'test', :environment => 'development', :certificate => 'foo')
6
- app.valid?
7
- app.errors[:certificate].should == 'Certificate value must contain a certificate and a private key.'
8
- end
9
-
10
- it 'validates a certificate without a password' do
11
- app = Rapns::Apns::App.new :name => 'test', :environment => 'development', :certificate => TEST_CERT
12
- app.valid?
13
- app.errors[:certificate].should be_nil
14
- end
15
-
16
- it 'validates a certificate with a password' do
17
- app = Rapns::Apns::App.new :name => 'test', :environment => 'development',
18
- :certificate => TEST_CERT_WITH_PASSWORD, :password => 'fubar'
19
- app.valid?
20
- app.errors[:certificate].should be_nil
21
- end
22
-
23
- it 'validates a certificate with an incorrect password' do
24
- app = Rapns::Apns::App.new :name => 'test', :environment => 'development',
25
- :certificate => TEST_CERT_WITH_PASSWORD, :password => 'incorrect'
26
- app.valid?
27
- app.errors[:certificate].should == "Certificate value must contain a certificate and a private key."
28
- end
29
- end
@@ -1,9 +0,0 @@
1
- require File.expand_path("spec/unit_spec_helper")
2
-
3
- describe Rapns::Apns::Feedback do
4
- it "should validate the format of the device_token" do
5
- notification = Rapns::Apns::Feedback.new(:device_token => "{$%^&*()}")
6
- notification.valid?.should be_false
7
- notification.errors[:device_token].should include("is invalid")
8
- end
9
- end
@@ -1,215 +0,0 @@
1
- # encoding: US-ASCII
2
-
3
- require File.expand_path("spec/unit_spec_helper")
4
- require 'spec/unit/notification_shared.rb'
5
-
6
- describe Rapns::Apns::Notification do
7
- it_should_behave_like 'an Notification subclass'
8
-
9
- let(:notification_class) { Rapns::Apns::Notification }
10
- let(:notification) { notification_class.new }
11
- let(:data_setter) { 'attributes_for_device=' }
12
- let(:data_getter) { 'attributes_for_device' }
13
-
14
- it "should validate the format of the device_token" do
15
- notification = Rapns::Apns::Notification.new(:device_token => "{$%^&*()}")
16
- notification.valid?.should be_false
17
- notification.errors[:device_token].include?("is invalid").should be_true
18
- end
19
-
20
- it "should validate the length of the binary conversion of the notification" do
21
- notification.device_token = "a" * 256
22
- notification.alert = "way too long!" * 100
23
- notification.valid?.should be_false
24
- notification.errors[:base].include?("APN notification cannot be larger than 256 bytes. Try condensing your alert and device attributes.").should be_true
25
- end
26
-
27
- it "should default the sound to 'default'" do
28
- notification.sound.should == 'default'
29
- end
30
-
31
- it "should default the expiry to 1 day" do
32
- notification.expiry.should == 1.day.to_i
33
- end
34
- end
35
-
36
- describe Rapns::Apns::Notification, "when assigning the device token" do
37
- it "should strip spaces from the given string" do
38
- notification = Rapns::Apns::Notification.new(:device_token => "o m g")
39
- notification.device_token.should == "omg"
40
- end
41
-
42
- it "should strip chevrons from the given string" do
43
- notification = Rapns::Apns::Notification.new(:device_token => "<omg>")
44
- notification.device_token.should == "omg"
45
- end
46
- end
47
-
48
- describe Rapns::Apns::Notification, "as_json" do
49
- it "should include the alert if present" do
50
- notification = Rapns::Apns::Notification.new(:alert => "hi mom")
51
- notification.as_json["aps"]["alert"].should == "hi mom"
52
- end
53
-
54
- it "should not include the alert key if the alert is not present" do
55
- notification = Rapns::Apns::Notification.new(:alert => nil)
56
- notification.as_json["aps"].key?("alert").should be_false
57
- end
58
-
59
- it "should encode the alert as JSON if it is a Hash" do
60
- notification = Rapns::Apns::Notification.new(:alert => { 'body' => "hi mom", 'alert-loc-key' => "View" })
61
- notification.as_json["aps"]["alert"].should == { 'body' => "hi mom", 'alert-loc-key' => "View" }
62
- end
63
-
64
- it "should include the badge if present" do
65
- notification = Rapns::Apns::Notification.new(:badge => 6)
66
- notification.as_json["aps"]["badge"].should == 6
67
- end
68
-
69
- it "should not include the badge key if the badge is not present" do
70
- notification = Rapns::Apns::Notification.new(:badge => nil)
71
- notification.as_json["aps"].key?("badge").should be_false
72
- end
73
-
74
- it "should include the sound if present" do
75
- notification = Rapns::Apns::Notification.new(:alert => "my_sound.aiff")
76
- notification.as_json["aps"]["alert"].should == "my_sound.aiff"
77
- end
78
-
79
- it "should not include the sound key if the sound is not present" do
80
- notification = Rapns::Apns::Notification.new(:sound => false)
81
- notification.as_json["aps"].key?("sound").should be_false
82
- end
83
-
84
- it "should include attributes for the device" do
85
- notification = Rapns::Apns::Notification.new
86
- notification.attributes_for_device = {:omg => :lol, :wtf => :dunno}
87
- notification.as_json["omg"].should == "lol"
88
- notification.as_json["wtf"].should == "dunno"
89
- end
90
-
91
- it "should allow attributes to include a hash" do
92
- notification = Rapns::Apns::Notification.new
93
- notification.attributes_for_device = {:omg => {:ilike => :hashes}}
94
- notification.as_json["omg"]["ilike"].should == "hashes"
95
- end
96
-
97
- end
98
-
99
- describe Rapns::Apns::Notification, 'MDM' do
100
- let(:magic) { 'abc123' }
101
- let(:notification) { Rapns::Apns::Notification.new }
102
-
103
- it 'includes the mdm magic in the payload' do
104
- notification.mdm = magic
105
- notification.as_json.should == {'mdm' => magic}
106
- end
107
-
108
- it 'does not include aps attribute' do
109
- notification.alert = "i'm doomed"
110
- notification.mdm = magic
111
- notification.as_json.key?('aps').should be_false
112
- end
113
- end
114
-
115
- describe Rapns::Apns::Notification, 'content-available' do
116
- let(:notification) { Rapns::Apns::Notification.new }
117
-
118
- it 'includes content-available in the payload' do
119
- notification.content_available = true
120
- notification.as_json['aps']['content-available'].should == 1
121
- end
122
-
123
- it 'does not include content-available in the payload if not set' do
124
- notification.as_json['aps'].key?('content-available').should be_false
125
- end
126
-
127
- it 'does not include content-available as a non-aps attribute' do
128
- notification.content_available = true
129
- notification.as_json.key?('content-available').should be_false
130
- end
131
-
132
- it 'does not overwrite existing attributes for the device' do
133
- notification.data = {:hi => :mom}
134
- notification.content_available = true
135
- notification.as_json['aps']['content-available'].should == 1
136
- notification.as_json['hi'].should == 'mom'
137
- end
138
-
139
- it 'does not overwrite the content-available flag when setting attributes for the device' do
140
- notification.content_available = true
141
- notification.data = {:hi => :mom}
142
- notification.as_json['aps']['content-available'].should == 1
143
- notification.as_json['hi'].should == 'mom'
144
- end
145
-
146
- end
147
-
148
- describe Rapns::Apns::Notification, "to_binary" do
149
- it "should correctly convert the notification to binary" do
150
- notification = Rapns::Apns::Notification.new
151
- notification.device_token = "a" * 256
152
- notification.sound = "1.aiff"
153
- notification.badge = 3
154
- notification.alert = "Don't panic Mr Mainwaring, don't panic!"
155
- notification.attributes_for_device = {:hi => :mom}
156
- notification.expiry = 86400 # 1 day, \x00\x01Q\x80
157
- notification.app = Rapns::Apns::App.create!(:name => 'my_app', :environment => 'development', :certificate => TEST_CERT)
158
- notification.save!
159
- notification.stub(:id).and_return(1234)
160
- notification.to_binary.should == "\x01\x00\x00\x04\xD2\x00\x01Q\x80\x00 \xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\x00a{\"aps\":{\"alert\":\"Don't panic Mr Mainwaring, don't panic!\",\"badge\":3,\"sound\":\"1.aiff\"},\"hi\":\"mom\"}"
161
- end
162
- end
163
-
164
- describe Rapns::Apns::Notification, "bug #31" do
165
- it 'does not confuse a JSON looking string as JSON' do
166
- notification = Rapns::Apns::Notification.new
167
- notification.alert = "{\"one\":2}"
168
- notification.alert.should == "{\"one\":2}"
169
- end
170
-
171
- it 'does confuse a JSON looking string as JSON if the alert_is_json attribute is not present' do
172
- notification = Rapns::Apns::Notification.new
173
- notification.stub(:has_attribute? => false)
174
- notification.alert = "{\"one\":2}"
175
- notification.alert.should == {"one" => 2}
176
- end
177
- end
178
-
179
- describe Rapns::Apns::Notification, "bug #35" do
180
- it "should limit payload size to 256 bytes but not the entire packet" do
181
- notification = Rapns::Apns::Notification.new do |n|
182
- n.device_token = "a" * 256
183
- n.alert = "a" * 210
184
- n.app = Rapns::Apns::App.create!(:name => 'my_app', :environment => 'development', :certificate => TEST_CERT)
185
- end
186
-
187
- notification.to_binary(:for_validation => true).bytesize.should > 256
188
- notification.payload_size.should < 256
189
- notification.should be_valid
190
- end
191
- end
192
-
193
- describe Rapns::Apns::Notification, "multi_json usage" do
194
- describe Rapns::Apns::Notification, "alert" do
195
- it "should call MultiJson.load when multi_json version is 1.3.0" do
196
- Object.stub_constants(:MultiJson => mock) do
197
- MultiJson.should_receive(:encode).with(any_args())
198
- MultiJson.should_receive(:load).with(any_args())
199
- notification = Rapns::Apns::Notification.new(:alert => { :a => 1 }, :alert_is_json => true)
200
- Gem.stub(:loaded_specs).and_return( { 'multi_json' => Gem::Specification.new('multi_json', '1.3.0') } )
201
- notification.alert
202
- end
203
- end
204
-
205
- it "should call MultiJson.decode when multi_json version is 1.2.9" do
206
- Object.stub_constants(:MultiJson => mock) do
207
- MultiJson.should_receive(:encode).with(any_args())
208
- MultiJson.should_receive(:decode).with(any_args())
209
- notification = Rapns::Apns::Notification.new(:alert => { :a => 1 }, :alert_is_json => true)
210
- Gem.stub(:loaded_specs).and_return( { 'multi_json' => Gem::Specification.new('multi_json', '1.2.9') } )
211
- notification.alert
212
- end
213
- end
214
- end
215
- end
@@ -1,21 +0,0 @@
1
- require File.expand_path("spec/unit_spec_helper")
2
-
3
- describe Rapns, 'apns_feedback' do
4
- let!(:app) { Rapns::Apns::App.create!(:name => 'test', :environment => 'production', :certificate => TEST_CERT) }
5
- let(:receiver) { double(:check_for_feedback => nil) }
6
-
7
- before do
8
- Rapns::Daemon::Apns::FeedbackReceiver.stub(:new => receiver)
9
- end
10
-
11
- it 'initializes the store' do
12
- Rapns::Daemon.should_receive(:initialize_store)
13
- Rapns.apns_feedback
14
- end
15
-
16
- it 'checks feedback for each app' do
17
- Rapns::Daemon::Apns::FeedbackReceiver.should_receive(:new).with(app, 0).and_return(receiver)
18
- receiver.should_receive(:check_for_feedback)
19
- Rapns.apns_feedback
20
- end
21
- end
@@ -1,16 +0,0 @@
1
- require File.expand_path("spec/unit_spec_helper")
2
-
3
- describe Rapns::App do
4
- it 'validates the uniqueness of name within type and environment' do
5
- Rapns::Apns::App.create!(:name => 'test', :environment => 'production', :certificate => TEST_CERT)
6
- app = Rapns::Apns::App.new(:name => 'test', :environment => 'production', :certificate => TEST_CERT)
7
- app.valid?.should be_false
8
- app.errors[:name].should == 'has already been taken'
9
-
10
- app = Rapns::Apns::App.new(:name => 'test', :environment => 'development', :certificate => TEST_CERT)
11
- app.valid?.should be_true
12
-
13
- app = Rapns::Gcm::App.new(:name => 'test', :environment => 'production', :auth_key => TEST_CERT)
14
- app.valid?.should be_true
15
- end
16
- end
@@ -1,55 +0,0 @@
1
- require File.expand_path("spec/unit_spec_helper")
2
-
3
- describe Rapns do
4
- let(:config) { double }
5
-
6
- before { Rapns.stub(:config => config) }
7
-
8
- it 'can yields a config block' do
9
- yielded_args = nil
10
- Rapns.configure do |*a| yielded_args = a end
11
- yielded_args.should == [config]
12
- end
13
- end
14
-
15
- describe Rapns::Configuration do
16
- let(:config) do
17
- Rapns::Deprecation.muted do
18
- Rapns::Configuration.new
19
- end
20
- end
21
-
22
- it 'configures a feedback callback' do
23
- b = Proc.new {}
24
- Rapns::Deprecation.muted do
25
- config.on_apns_feedback(&b)
26
- end
27
- config.apns_feedback_callback.should == b
28
- end
29
-
30
- it 'can be updated' do
31
- Rapns::Deprecation.muted do
32
- new_config = Rapns::Configuration.new
33
- new_config.batch_size = 100
34
- expect { config.update(new_config) }.to change(config, :batch_size).to(100)
35
- end
36
- end
37
-
38
- it 'sets the pid_file relative if not absolute' do
39
- Rails.stub(:root => '/rails')
40
- config.pid_file = 'tmp/rapns.pid'
41
- config.pid_file.should == '/rails/tmp/rapns.pid'
42
- end
43
-
44
- it 'does not alter an absolute pid_file path' do
45
- config.pid_file = '/tmp/rapns.pid'
46
- config.pid_file.should == '/tmp/rapns.pid'
47
- end
48
-
49
- it 'does not allow foreground to be set to false if the platform is JRuby' do
50
- config.foreground = true
51
- Rapns.stub(:jruby? => true)
52
- config.foreground = false
53
- config.foreground.should be_true
54
- end
55
- end
@@ -1,45 +0,0 @@
1
- require File.expand_path("spec/unit_spec_helper")
2
- require File.dirname(__FILE__) + '/../app_runner_shared.rb'
3
-
4
- describe Rapns::Daemon::Apns::AppRunner do
5
- it_should_behave_like 'an AppRunner subclass'
6
-
7
- let(:app_class) { Rapns::Apns::App }
8
- let(:app) { app_class.create!(:name => 'my_app', :environment => 'development',
9
- :certificate => TEST_CERT, :password => 'pass') }
10
- let(:runner) { Rapns::Daemon::Apns::AppRunner.new(app) }
11
- let(:handler) { double(:start => nil, :queue= => nil, :wakeup => nil, :wait => nil, :stop => nil) }
12
- let(:handler_collection) { double(:handler_collection, :push => nil, :size => 1, :stop => nil) }
13
- let(:receiver) { double(:start => nil, :stop => nil) }
14
- let(:config) { double(:feedback_poll => 60, :push => false) }
15
- let(:logger) { double(:info => nil, :warn => nil, :error => nil) }
16
-
17
- before do
18
- Rapns.stub(:logger => logger, :config => config)
19
- Rapns::Daemon::Apns::DeliveryHandler.stub(:new => handler)
20
- Rapns::Daemon::DeliveryHandlerCollection.stub(:new => handler_collection)
21
- Rapns::Daemon::Apns::FeedbackReceiver.stub(:new => receiver)
22
- end
23
-
24
- it 'instantiates a new feedback receiver when started' do
25
- Rapns::Daemon::Apns::FeedbackReceiver.should_receive(:new).with(app, 60)
26
- runner.start
27
- end
28
-
29
- it 'starts the feedback receiver' do
30
- receiver.should_receive(:start)
31
- runner.start
32
- end
33
-
34
- it 'stops the feedback receiver' do
35
- runner.start
36
- receiver.should_receive(:stop)
37
- runner.stop
38
- end
39
-
40
- it 'does not check for feedback when in push mode' do
41
- config.stub(:push => true)
42
- Rapns::Daemon::Apns::FeedbackReceiver.should_not_receive(:new)
43
- runner.start
44
- end
45
- end
@@ -1,11 +0,0 @@
1
- require File.expand_path("spec/unit_spec_helper")
2
-
3
- describe Rapns::Apns::CertificateExpiredError do
4
- let(:app) { double(:name => 'test') }
5
- let(:error) { Rapns::Apns::CertificateExpiredError.new(app, Time.now) }
6
-
7
- it 'returns a message' do
8
- error.message
9
- error.to_s
10
- end
11
- end
@@ -1,287 +0,0 @@
1
- require File.expand_path("spec/unit_spec_helper")
2
-
3
- describe Rapns::Daemon::Apns::Connection do
4
- let(:rsa_key) { double }
5
- let(:certificate) { double }
6
- let(:password) { double }
7
- let(:x509_certificate) { OpenSSL::X509::Certificate.new(TEST_CERT) }
8
- let(:ssl_context) { double(:key= => nil, :cert= => nil, :cert => x509_certificate) }
9
- let(:host) { 'gateway.push.apple.com' }
10
- let(:port) { '2195' }
11
- let(:tcp_socket) { double(:setsockopt => nil, :close => nil) }
12
- let(:ssl_socket) { double(:sync= => nil, :connect => nil, :close => nil, :write => nil, :flush => nil) }
13
- let(:logger) { double(:info => nil, :error => nil, :warn => nil) }
14
- let(:app) { double(:name => 'Connection 0', :certificate => certificate, :password => password)}
15
- let(:connection) { Rapns::Daemon::Apns::Connection.new(app, host, port) }
16
-
17
- before do
18
- OpenSSL::SSL::SSLContext.stub(:new => ssl_context)
19
- OpenSSL::PKey::RSA.stub(:new => rsa_key)
20
- OpenSSL::X509::Certificate.stub(:new => x509_certificate)
21
- TCPSocket.stub(:new => tcp_socket)
22
- OpenSSL::SSL::SSLSocket.stub(:new => ssl_socket)
23
- Rapns.stub(:logger => logger)
24
- connection.stub(:reflect)
25
- end
26
-
27
- it "reads the number of bytes from the SSL socket" do
28
- ssl_socket.should_receive(:read).with(123)
29
- connection.connect
30
- connection.read(123)
31
- end
32
-
33
- it "selects on the SSL socket until the given timeout" do
34
- IO.should_receive(:select).with([ssl_socket], nil, nil, 10)
35
- connection.connect
36
- connection.select(10)
37
- end
38
-
39
- describe "when setting up the SSL context" do
40
- it "sets the key on the context" do
41
- OpenSSL::PKey::RSA.should_receive(:new).with(certificate, password).and_return(rsa_key)
42
- ssl_context.should_receive(:key=).with(rsa_key)
43
- connection.connect
44
- end
45
-
46
- it "sets the cert on the context" do
47
- OpenSSL::X509::Certificate.should_receive(:new).with(certificate).and_return(x509_certificate)
48
- ssl_context.should_receive(:cert=).with(x509_certificate)
49
- connection.connect
50
- end
51
- end
52
-
53
- describe "when connecting the socket" do
54
- it "creates a TCP socket using the configured host and port" do
55
- TCPSocket.should_receive(:new).with(host, port).and_return(tcp_socket)
56
- connection.connect
57
- end
58
-
59
- it "creates a new SSL socket using the TCP socket and SSL context" do
60
- OpenSSL::SSL::SSLSocket.should_receive(:new).with(tcp_socket, ssl_context).and_return(ssl_socket)
61
- connection.connect
62
- end
63
-
64
- it "sets the sync option on the SSL socket" do
65
- ssl_socket.should_receive(:sync=).with(true)
66
- connection.connect
67
- end
68
-
69
- it "connects the SSL socket" do
70
- ssl_socket.should_receive(:connect)
71
- connection.connect
72
- end
73
-
74
- it "sets the socket option TCP_NODELAY" do
75
- tcp_socket.should_receive(:setsockopt).with(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
76
- connection.connect
77
- end
78
-
79
- it "sets the socket option SO_KEEPALIVE" do
80
- tcp_socket.should_receive(:setsockopt).with(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1)
81
- connection.connect
82
- end
83
-
84
- describe 'certificate expiry' do
85
- it 'reflects if the certificate will expire soon' do
86
- cert = OpenSSL::X509::Certificate.new(app.certificate)
87
- connection.should_receive(:reflect).with(:apns_certificate_will_expire, app, cert.not_after)
88
- Timecop.freeze(cert.not_after.to_datetime - 3.days) { connection.connect }
89
- end
90
-
91
- it 'logs that the certificate will expire soon' do
92
- cert = OpenSSL::X509::Certificate.new(app.certificate)
93
- logger.should_receive(:warn).with("[#{app.name}] Certificate will expire at 2022-09-07 03:18:32 UTC.")
94
- Timecop.freeze(cert.not_after.to_datetime - 3.days) { connection.connect }
95
- end
96
-
97
- it 'does not reflect if the certificate will not expire soon' do
98
- cert = OpenSSL::X509::Certificate.new(app.certificate)
99
- connection.should_not_receive(:reflect).with(:apns_certificate_will_expire, app, kind_of(Time))
100
- Timecop.freeze(cert.not_after.to_datetime - 2.months) { connection.connect }
101
- end
102
-
103
- it 'logs that the certificate has expired' do
104
- cert = OpenSSL::X509::Certificate.new(app.certificate)
105
- logger.should_receive(:error).with("[#{app.name}] Certificate expired at 2022-09-07 03:18:32 UTC.")
106
- Timecop.freeze(cert.not_after.to_datetime + 1.day) { connection.connect rescue Rapns::Apns::CertificateExpiredError }
107
- end
108
-
109
- it 'raises an error if the certificate has expired' do
110
- cert = OpenSSL::X509::Certificate.new(app.certificate)
111
- Timecop.freeze(cert.not_after.to_datetime + 1.day) do
112
- expect { connection.connect }.to raise_error(Rapns::Apns::CertificateExpiredError)
113
- end
114
- end
115
- end
116
- end
117
-
118
- describe "when shuting down the connection" do
119
- it "closes the TCP socket" do
120
- connection.connect
121
- tcp_socket.should_receive(:close)
122
- connection.close
123
- end
124
-
125
- it "does not attempt to close the TCP socket if it is not connected" do
126
- connection.connect
127
- tcp_socket.should_not_receive(:close)
128
- connection.instance_variable_set("@tcp_socket", nil)
129
- connection.close
130
- end
131
-
132
- it "closes the SSL socket" do
133
- connection.connect
134
- ssl_socket.should_receive(:close)
135
- connection.close
136
- end
137
-
138
- it "does not attempt to close the SSL socket if it is not connected" do
139
- connection.connect
140
- ssl_socket.should_not_receive(:close)
141
- connection.instance_variable_set("@ssl_socket", nil)
142
- connection.close
143
- end
144
-
145
- it "ignores IOError when the socket is already closed" do
146
- tcp_socket.stub(:close).and_raise(IOError)
147
- connection.connect
148
- connection.close
149
- end
150
- end
151
-
152
- shared_examples_for "when the write fails" do
153
- before do
154
- connection.stub(:sleep)
155
- connection.connect
156
- ssl_socket.stub(:write).and_raise(error_type)
157
- end
158
-
159
- it 'reflects the connection has been lost' do
160
- connection.should_receive(:reflect).with(:apns_connection_lost, app, kind_of(error_type))
161
- begin
162
- connection.write(nil)
163
- rescue Rapns::Daemon::Apns::ConnectionError
164
- end
165
- end
166
-
167
- it "logs that the connection has been lost once only" do
168
- logger.should_receive(:error).with("[Connection 0] Lost connection to gateway.push.apple.com:2195 (#{error_type.name}), reconnecting...").once
169
- begin
170
- connection.write(nil)
171
- rescue Rapns::Daemon::Apns::ConnectionError
172
- end
173
- end
174
-
175
- it "retries to make a connection 3 times" do
176
- connection.should_receive(:reconnect).exactly(3).times
177
- begin
178
- connection.write(nil)
179
- rescue Rapns::Daemon::Apns::ConnectionError
180
- end
181
- end
182
-
183
- it "raises a ConnectionError after 3 attempts at reconnecting" do
184
- expect do
185
- connection.write(nil)
186
- end.to raise_error(Rapns::Daemon::Apns::ConnectionError, "Connection 0 tried 3 times to reconnect but failed (#{error_type.name}).")
187
- end
188
-
189
- it "sleeps 1 second before retrying the connection" do
190
- connection.should_receive(:sleep).with(1)
191
- begin
192
- connection.write(nil)
193
- rescue Rapns::Daemon::Apns::ConnectionError
194
- end
195
- end
196
- end
197
-
198
- describe "when write raises an Errno::EPIPE" do
199
- it_should_behave_like "when the write fails"
200
-
201
- def error_type
202
- Errno::EPIPE
203
- end
204
- end
205
-
206
- describe "when write raises an Errno::ETIMEDOUT" do
207
- it_should_behave_like "when the write fails"
208
-
209
- def error_type
210
- Errno::ETIMEDOUT
211
- end
212
- end
213
-
214
- describe "when write raises an OpenSSL::SSL::SSLError" do
215
- it_should_behave_like "when the write fails"
216
-
217
- def error_type
218
- OpenSSL::SSL::SSLError
219
- end
220
- end
221
-
222
- describe "when write raises an IOError" do
223
- it_should_behave_like "when the write fails"
224
-
225
- def error_type
226
- IOError
227
- end
228
- end
229
-
230
- describe "when reconnecting" do
231
- before { connection.connect }
232
-
233
- it 'closes the socket' do
234
- connection.should_receive(:close)
235
- connection.send(:reconnect)
236
- end
237
-
238
- it 'connects the socket' do
239
- connection.should_receive(:connect_socket)
240
- connection.send(:reconnect)
241
- end
242
- end
243
-
244
- describe "when sending a notification" do
245
- before { connection.connect }
246
-
247
- it "writes the data to the SSL socket" do
248
- ssl_socket.should_receive(:write).with("blah")
249
- connection.write("blah")
250
- end
251
-
252
- it "flushes the SSL socket" do
253
- ssl_socket.should_receive(:flush)
254
- connection.write("blah")
255
- end
256
- end
257
-
258
- describe 'idle period' do
259
- before { connection.connect }
260
-
261
- it 'reconnects if the connection has been idle for more than the defined period' do
262
- Rapns::Daemon::Apns::Connection.stub(:idle_period => 0.1)
263
- sleep 0.2
264
- connection.should_receive(:reconnect)
265
- connection.write('blah')
266
- end
267
-
268
- it 'resets the last write time' do
269
- now = Time.now
270
- Time.stub(:now => now)
271
- connection.write('blah')
272
- connection.last_write.should == now
273
- end
274
-
275
- it 'does not reconnect if the connection has not been idle for more than the defined period' do
276
- connection.should_not_receive(:reconnect)
277
- connection.write('blah')
278
- end
279
-
280
- it 'logs the the connection is idle' do
281
- Rapns::Daemon::Apns::Connection.stub(:idle_period => 0.1)
282
- sleep 0.2
283
- Rapns.logger.should_receive(:info).with('[Connection 0] Idle period exceeded, reconnecting...')
284
- connection.write('blah')
285
- end
286
- end
287
- end