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.
- checksums.yaml +7 -0
- data/README.md +2 -0
- data/lib/rapns/daemon/gcm/delivery.rb +4 -3
- data/lib/rapns/version.rb +1 -1
- metadata +46 -163
- data/config/database.yml +0 -44
- data/spec/support/cert_with_password.pem +0 -90
- data/spec/support/cert_without_password.pem +0 -59
- data/spec/support/simplecov_helper.rb +0 -13
- data/spec/support/simplecov_quality_formatter.rb +0 -8
- data/spec/tmp/.gitkeep +0 -0
- data/spec/unit/apns/app_spec.rb +0 -29
- data/spec/unit/apns/feedback_spec.rb +0 -9
- data/spec/unit/apns/notification_spec.rb +0 -215
- data/spec/unit/apns_feedback_spec.rb +0 -21
- data/spec/unit/app_spec.rb +0 -16
- data/spec/unit/configuration_spec.rb +0 -55
- data/spec/unit/daemon/apns/app_runner_spec.rb +0 -45
- data/spec/unit/daemon/apns/certificate_expired_error_spec.rb +0 -11
- data/spec/unit/daemon/apns/connection_spec.rb +0 -287
- data/spec/unit/daemon/apns/delivery_handler_spec.rb +0 -59
- data/spec/unit/daemon/apns/delivery_spec.rb +0 -101
- data/spec/unit/daemon/apns/disconnection_error_spec.rb +0 -18
- data/spec/unit/daemon/apns/feedback_receiver_spec.rb +0 -134
- data/spec/unit/daemon/app_runner_shared.rb +0 -83
- data/spec/unit/daemon/app_runner_spec.rb +0 -170
- data/spec/unit/daemon/batch_spec.rb +0 -219
- data/spec/unit/daemon/delivery_error_spec.rb +0 -13
- data/spec/unit/daemon/delivery_handler_collection_spec.rb +0 -37
- data/spec/unit/daemon/delivery_handler_shared.rb +0 -45
- data/spec/unit/daemon/feeder_spec.rb +0 -89
- data/spec/unit/daemon/gcm/app_runner_spec.rb +0 -19
- data/spec/unit/daemon/gcm/delivery_handler_spec.rb +0 -44
- data/spec/unit/daemon/gcm/delivery_spec.rb +0 -289
- data/spec/unit/daemon/interruptible_sleep_spec.rb +0 -68
- data/spec/unit/daemon/reflectable_spec.rb +0 -27
- data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +0 -114
- data/spec/unit/daemon/store/active_record_spec.rb +0 -281
- data/spec/unit/daemon_spec.rb +0 -157
- data/spec/unit/deprecatable_spec.rb +0 -32
- data/spec/unit/deprecation_spec.rb +0 -15
- data/spec/unit/embed_spec.rb +0 -50
- data/spec/unit/gcm/app_spec.rb +0 -4
- data/spec/unit/gcm/notification_spec.rb +0 -52
- data/spec/unit/logger_spec.rb +0 -180
- data/spec/unit/notification_shared.rb +0 -45
- data/spec/unit/notification_spec.rb +0 -4
- data/spec/unit/notifier_spec.rb +0 -52
- data/spec/unit/push_spec.rb +0 -44
- data/spec/unit/rapns_spec.rb +0 -9
- data/spec/unit/reflection_spec.rb +0 -30
- data/spec/unit/upgraded_spec.rb +0 -40
- 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
|
data/spec/tmp/.gitkeep
DELETED
File without changes
|
data/spec/unit/apns/app_spec.rb
DELETED
@@ -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
|
data/spec/unit/app_spec.rb
DELETED
@@ -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
|