rapns 0.2.3 → 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.
- data/README.md +35 -10
- data/lib/generators/rapns_generator.rb +9 -1
- data/lib/generators/templates/create_rapns_feedback.rb +15 -0
- data/lib/generators/templates/rapns.yml +22 -9
- data/lib/rapns/daemon/configuration.rb +32 -14
- data/lib/rapns/daemon/connection.rb +35 -50
- data/lib/rapns/daemon/delivery_handler.rb +65 -15
- data/lib/rapns/daemon/delivery_handler_pool.rb +1 -5
- data/lib/rapns/daemon/delivery_queue.rb +10 -27
- data/lib/rapns/daemon/feedback_receiver.rb +57 -0
- data/lib/rapns/daemon/feeder.rb +9 -7
- data/lib/rapns/daemon/interruptible_sleep.rb +14 -0
- data/lib/rapns/daemon/pool.rb +0 -5
- data/lib/rapns/daemon.rb +9 -9
- data/lib/rapns/device_token_format_validator.rb +10 -0
- data/lib/rapns/feedback.rb +10 -0
- data/lib/rapns/notification.rb +15 -1
- data/lib/rapns/version.rb +1 -1
- data/lib/rapns.rb +3 -1
- data/spec/rapns/daemon/certificate_spec.rb +6 -0
- data/spec/rapns/daemon/configuration_spec.rb +124 -40
- data/spec/rapns/daemon/connection_spec.rb +81 -129
- data/spec/rapns/daemon/delivery_handler_pool_spec.rb +1 -6
- data/spec/rapns/daemon/delivery_handler_spec.rb +117 -30
- data/spec/rapns/daemon/delivery_queue_spec.rb +29 -0
- data/spec/rapns/daemon/feedback_receiver_spec.rb +86 -0
- data/spec/rapns/daemon/feeder_spec.rb +25 -9
- data/spec/rapns/daemon/interruptible_sleep_spec.rb +32 -0
- data/spec/rapns/daemon/logger_spec.rb +34 -14
- data/spec/rapns/daemon_spec.rb +34 -31
- data/spec/rapns/feedback_spec.rb +12 -0
- data/spec/rapns/notification_spec.rb +5 -0
- data/spec/spec_helper.rb +5 -2
- metadata +16 -5
- data/lib/rapns/daemon/connection_pool.rb +0 -31
- data/spec/rapns/daemon/connection_pool_spec.rb +0 -40
@@ -10,10 +10,10 @@ describe Rapns::Daemon::Connection, "when setting up the SSL context" do
|
|
10
10
|
Rapns::Daemon.stub(:certificate).and_return(@certificate)
|
11
11
|
@x509_certificate = mock("X509 Certificate")
|
12
12
|
OpenSSL::X509::Certificate.stub(:new).and_return(@x509_certificate)
|
13
|
-
@connection = Rapns::Daemon::Connection.new(
|
13
|
+
@connection = Rapns::Daemon::Connection.new('Connection 0', 'gateway.push.apple.com', 2195)
|
14
14
|
@connection.stub(:connect_socket)
|
15
15
|
@connection.stub(:setup_at_exit_hook)
|
16
|
-
configuration = mock("Configuration", :
|
16
|
+
configuration = mock("Configuration", :certificate_password => "abc123")
|
17
17
|
Rapns::Daemon.stub(:configuration).and_return(configuration)
|
18
18
|
end
|
19
19
|
|
@@ -32,24 +32,20 @@ end
|
|
32
32
|
|
33
33
|
describe Rapns::Daemon::Connection, "when connecting the socket" do
|
34
34
|
before do
|
35
|
-
@connection = Rapns::Daemon::Connection.new(
|
35
|
+
@connection = Rapns::Daemon::Connection.new('Connection 0', 'gateway.push.apple.com', 2195)
|
36
36
|
@connection.stub(:setup_at_exit_hook)
|
37
37
|
@ssl_context = mock("SSLContext")
|
38
38
|
@connection.stub(:setup_ssl_context).and_return(@ssl_context)
|
39
39
|
@tcp_socket = mock("TCPSocket", :close => nil, :setsockopt => nil)
|
40
40
|
TCPSocket.stub(:new).and_return(@tcp_socket)
|
41
|
-
Rapns::Daemon::Configuration.stub(:host).and_return("localhost")
|
42
|
-
Rapns::Daemon::Configuration.stub(:port).and_return(123)
|
43
41
|
@ssl_socket = mock("SSLSocket", :sync= => nil, :connect => nil, :close => nil)
|
44
42
|
OpenSSL::SSL::SSLSocket.stub(:new).and_return(@ssl_socket)
|
45
|
-
configuration = mock("Configuration", :host => "localhost", :port => 123)
|
46
|
-
Rapns::Daemon.stub(:configuration).and_return(configuration)
|
47
43
|
@logger = mock("Logger", :info => nil)
|
48
44
|
Rapns::Daemon.stub(:logger).and_return(@logger)
|
49
45
|
end
|
50
46
|
|
51
47
|
it "should create a TCP socket using the configured host and port" do
|
52
|
-
TCPSocket.should_receive(:new).with(
|
48
|
+
TCPSocket.should_receive(:new).with('gateway.push.apple.com', 2195).and_return(@tcp_socket)
|
53
49
|
@connection.connect
|
54
50
|
end
|
55
51
|
|
@@ -81,7 +77,7 @@ end
|
|
81
77
|
|
82
78
|
describe Rapns::Daemon::Connection, "when shuting down the connection" do
|
83
79
|
before do
|
84
|
-
@connection = Rapns::Daemon::Connection.new(
|
80
|
+
@connection = Rapns::Daemon::Connection.new('Connection 0', 'gateway.push.apple.com', 2195)
|
85
81
|
@connection.stub(:setup_ssl_context)
|
86
82
|
@ssl_socket = mock("SSLSocket", :close => nil)
|
87
83
|
@tcp_socket = mock("TCPSocket", :close => nil)
|
@@ -113,69 +109,58 @@ describe Rapns::Daemon::Connection, "when shuting down the connection" do
|
|
113
109
|
@connection.instance_variable_set("@ssl_socket", nil)
|
114
110
|
@connection.close
|
115
111
|
end
|
116
|
-
end
|
117
112
|
|
118
|
-
|
119
|
-
|
120
|
-
@connection
|
121
|
-
@
|
122
|
-
@connection.instance_variable_set("@ssl_socket", @ssl_socket)
|
123
|
-
@connection.stub(:connect_socket).and_return([mock("TCPSocket"), @ssl_socket])
|
124
|
-
@ssl_socket.stub(:write).and_raise(Errno::EPIPE)
|
125
|
-
@logger = mock("Logger", :error => nil)
|
126
|
-
Rapns::Daemon.stub(:logger).and_return(@logger)
|
127
|
-
@connection.stub(:sleep)
|
128
|
-
configuration = mock("Configuration", :host => "localhost", :port => 123)
|
129
|
-
Rapns::Daemon.stub(:configuration).and_return(configuration)
|
113
|
+
it "should ignore IOError when the socket is already closed" do
|
114
|
+
@tcp_socket.stub(:close).and_raise(IOError)
|
115
|
+
@connection.connect
|
116
|
+
expect {@connection.close }.should_not raise_error(IOError)
|
130
117
|
end
|
118
|
+
end
|
131
119
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
120
|
+
describe Rapns::Daemon::Connection, "read" do
|
121
|
+
before do
|
122
|
+
@connection = Rapns::Daemon::Connection.new('Connection 0', 'gateway.push.apple.com', 2195)
|
123
|
+
@connection.stub(:setup_ssl_context)
|
124
|
+
@ssl_socket = mock("SSLSocket", :close => nil)
|
125
|
+
@tcp_socket = mock("TCPSocket", :close => nil)
|
126
|
+
@connection.stub(:connect_socket).and_return([@tcp_socket, @ssl_socket])
|
138
127
|
end
|
139
128
|
|
140
|
-
it "
|
141
|
-
@
|
142
|
-
|
143
|
-
|
144
|
-
rescue Rapns::Daemon::ConnectionError
|
145
|
-
end
|
129
|
+
it "reads the number of bytes from the SSL socket" do
|
130
|
+
@ssl_socket.should_receive(:read).with(123)
|
131
|
+
@connection.connect
|
132
|
+
@connection.read(123)
|
146
133
|
end
|
134
|
+
end
|
147
135
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
136
|
+
describe Rapns::Daemon::Connection, "select" do
|
137
|
+
before do
|
138
|
+
@connection = Rapns::Daemon::Connection.new('Connection 0', 'gateway.push.apple.com', 2195)
|
139
|
+
@connection.stub(:setup_ssl_context)
|
140
|
+
@ssl_socket = mock("SSLSocket", :close => nil)
|
141
|
+
@tcp_socket = mock("TCPSocket", :close => nil)
|
142
|
+
@connection.stub(:connect_socket).and_return([@tcp_socket, @ssl_socket])
|
152
143
|
end
|
153
144
|
|
154
|
-
it "
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
rescue Rapns::Daemon::ConnectionError
|
159
|
-
end
|
145
|
+
it "selects on the SSL socket until the given timeout" do
|
146
|
+
IO.should_receive(:select).with([@ssl_socket], nil, nil, 10)
|
147
|
+
@connection.connect
|
148
|
+
@connection.select(10)
|
160
149
|
end
|
161
150
|
end
|
162
151
|
|
163
|
-
|
152
|
+
shared_examples_for "when the write fails" do
|
164
153
|
before do
|
165
|
-
@connection = Rapns::Daemon::Connection.new(
|
166
|
-
@ssl_socket = mock("SSLSocket")
|
167
|
-
@connection.instance_variable_set("@ssl_socket", @ssl_socket)
|
168
|
-
@connection.stub(:connect_socket).and_return([mock("TCPSocket"), @ssl_socket])
|
169
|
-
@ssl_socket.stub(:write).and_raise(OpenSSL::SSL::SSLError)
|
154
|
+
@connection = Rapns::Daemon::Connection.new('Connection 0', 'gateway.push.apple.com', 2195)
|
170
155
|
@logger = mock("Logger", :error => nil)
|
171
156
|
Rapns::Daemon.stub(:logger).and_return(@logger)
|
157
|
+
@connection.stub(:reconnect)
|
158
|
+
@connection.stub(:write_data).and_raise(error_type)
|
172
159
|
@connection.stub(:sleep)
|
173
|
-
configuration = mock("Configuration", :host => "localhost", :port => 123)
|
174
|
-
Rapns::Daemon.stub(:configuration).and_return(configuration)
|
175
160
|
end
|
176
161
|
|
177
|
-
it "should log
|
178
|
-
Rapns::Daemon.logger.should_receive(:error).with("[Connection
|
162
|
+
it "should log that the connection has been lost once only" do
|
163
|
+
Rapns::Daemon.logger.should_receive(:error).with("[Connection 0] Lost connection to gateway.push.apple.com:2195 (#{error_type.name}), reconnecting...").once
|
179
164
|
begin
|
180
165
|
@connection.write(nil)
|
181
166
|
rescue Rapns::Daemon::ConnectionError
|
@@ -183,7 +168,7 @@ describe Rapns::Daemon::Connection, "when an SSL error occurs during write" do
|
|
183
168
|
end
|
184
169
|
|
185
170
|
it "should retry to make a connection 3 times" do
|
186
|
-
@connection.should_receive(:
|
171
|
+
@connection.should_receive(:reconnect).exactly(3).times
|
187
172
|
begin
|
188
173
|
@connection.write(nil)
|
189
174
|
rescue Rapns::Daemon::ConnectionError
|
@@ -193,7 +178,7 @@ describe Rapns::Daemon::Connection, "when an SSL error occurs during write" do
|
|
193
178
|
it "should raise a ConnectionError after 3 attempts at reconnecting" do
|
194
179
|
expect do
|
195
180
|
@connection.write(nil)
|
196
|
-
end.to raise_error(Rapns::Daemon::ConnectionError, "Connection
|
181
|
+
end.to raise_error(Rapns::Daemon::ConnectionError, "Connection 0 tried 3 times to reconnect but failed (#{error_type.name}).")
|
197
182
|
end
|
198
183
|
|
199
184
|
it "should sleep 1 second before retrying the connection" do
|
@@ -205,98 +190,65 @@ describe Rapns::Daemon::Connection, "when an SSL error occurs during write" do
|
|
205
190
|
end
|
206
191
|
end
|
207
192
|
|
208
|
-
describe Rapns::Daemon::Connection, "when
|
209
|
-
|
210
|
-
@connection = Rapns::Daemon::Connection.new("Connection 1")
|
211
|
-
@ssl_socket = mock("SSLSocket", :write => nil, :flush => nil, :close => nil)
|
212
|
-
@tcp_socket = mock("TCPSocket", :close => nil)
|
213
|
-
@connection.stub(:setup_ssl_context)
|
214
|
-
@connection.stub(:connect_socket).and_return([@tcp_socket, @ssl_socket])
|
215
|
-
@connection.stub(:check_for_error)
|
216
|
-
@connection.connect
|
217
|
-
end
|
218
|
-
|
219
|
-
it "should write the data to the SSL socket" do
|
220
|
-
@ssl_socket.should_receive(:write).with("blah")
|
221
|
-
@connection.write("blah")
|
222
|
-
end
|
223
|
-
|
224
|
-
it "should flush the SSL socket" do
|
225
|
-
@ssl_socket.should_receive(:flush)
|
226
|
-
@connection.write("blah")
|
227
|
-
end
|
193
|
+
describe Rapns::Daemon::Connection, "when write raises an Errno::EPIPE" do
|
194
|
+
it_should_behave_like "when the write fails"
|
228
195
|
|
229
|
-
|
230
|
-
|
231
|
-
@connection.write("blah")
|
196
|
+
def error_type
|
197
|
+
Errno::EPIPE
|
232
198
|
end
|
233
199
|
end
|
234
200
|
|
235
|
-
describe Rapns::Daemon::Connection, "when
|
236
|
-
|
237
|
-
@notification = Rapns::Notification.create!(:device_token => "a" * 64)
|
238
|
-
@notification.stub(:save!)
|
239
|
-
@connection = Rapns::Daemon::Connection.new("Connection 1")
|
240
|
-
@ssl_socket = mock("SSLSocket", :write => nil, :flush => nil, :close => nil, :read => [8, 4, @notification.id].pack("ccN"))
|
241
|
-
@connection.stub(:setup_ssl_context)
|
242
|
-
@connection.stub(:connect_socket).and_return([@tcp_socket, @ssl_socket])
|
243
|
-
IO.stub(:select).and_return([@ssl_socket, [], []])
|
244
|
-
logger = mock("Logger", :error => nil, :warn => nil)
|
245
|
-
Rapns::Daemon.stub(:logger).and_return(logger)
|
246
|
-
@connection.connect
|
247
|
-
end
|
201
|
+
describe Rapns::Daemon::Connection, "when write raises an Errno::ETIMEDOUT" do
|
202
|
+
it_should_behave_like "when the write fails"
|
248
203
|
|
249
|
-
|
250
|
-
|
204
|
+
def error_type
|
205
|
+
Errno::ETIMEDOUT
|
251
206
|
end
|
207
|
+
end
|
252
208
|
|
253
|
-
|
254
|
-
|
255
|
-
expect { @connection.write("msg with an error") }.should_not raise_error(Rapns::DeliveryError)
|
256
|
-
end
|
209
|
+
describe Rapns::Daemon::Connection, "when write raises an OpenSSL::SSL::SSLError" do
|
210
|
+
it_should_behave_like "when the write fails"
|
257
211
|
|
258
|
-
|
259
|
-
|
260
|
-
expect { @connection.write("msg with an error") }.should_not raise_error(Rapns::DeliveryError)
|
212
|
+
def error_type
|
213
|
+
OpenSSL::SSL::SSLError
|
261
214
|
end
|
215
|
+
end
|
262
216
|
|
263
|
-
|
264
|
-
|
265
|
-
@connection.
|
217
|
+
describe Rapns::Daemon::Connection, "when reconnecting" do
|
218
|
+
before do
|
219
|
+
@connection = Rapns::Daemon::Connection.new('Connection 0', 'gateway.push.apple.com', 2195)
|
220
|
+
@connection.stub(:close)
|
221
|
+
@connection.stub(:connect_socket)
|
266
222
|
end
|
267
223
|
|
268
|
-
it
|
269
|
-
|
270
|
-
@
|
271
|
-
@connection.write("msg with an error")
|
224
|
+
it 'closes the socket' do
|
225
|
+
@connection.should_receive(:close)
|
226
|
+
@connection.send(:reconnect)
|
272
227
|
end
|
273
228
|
|
274
|
-
it
|
275
|
-
@
|
276
|
-
|
229
|
+
it 'connects the socket' do
|
230
|
+
@connection.should_receive(:connect_socket)
|
231
|
+
@connection.send(:reconnect)
|
277
232
|
end
|
233
|
+
end
|
278
234
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
235
|
+
describe Rapns::Daemon::Connection, "when sending a notification" do
|
236
|
+
before do
|
237
|
+
@connection = Rapns::Daemon::Connection.new('Connection 0', 'gateway.push.apple.com', 2195)
|
238
|
+
@ssl_socket = mock("SSLSocket", :write => nil, :flush => nil, :close => nil)
|
239
|
+
@tcp_socket = mock("TCPSocket", :close => nil)
|
240
|
+
@connection.stub(:setup_ssl_context)
|
241
|
+
@connection.stub(:connect_socket).and_return([@tcp_socket, @ssl_socket])
|
242
|
+
@connection.connect
|
285
243
|
end
|
286
244
|
|
287
|
-
it "should
|
288
|
-
@
|
289
|
-
|
290
|
-
@connection.write("msg with an error")
|
291
|
-
rescue Rapns::DeliveryError
|
292
|
-
end
|
245
|
+
it "should write the data to the SSL socket" do
|
246
|
+
@ssl_socket.should_receive(:write).with("blah")
|
247
|
+
@connection.write("blah")
|
293
248
|
end
|
294
249
|
|
295
|
-
it "should
|
296
|
-
|
297
|
-
|
298
|
-
@connection.write("msg with an error")
|
299
|
-
rescue Rapns::DeliveryError
|
300
|
-
end
|
250
|
+
it "should flush the SSL socket" do
|
251
|
+
@ssl_socket.should_receive(:flush)
|
252
|
+
@connection.write("blah")
|
301
253
|
end
|
302
254
|
end
|
@@ -13,14 +13,9 @@ describe Rapns::Daemon::DeliveryHandlerPool do
|
|
13
13
|
@pool.populate
|
14
14
|
end
|
15
15
|
|
16
|
-
it "
|
16
|
+
it "waits for each handle to stop" do
|
17
17
|
@pool.populate
|
18
18
|
@handler.should_receive(:stop).exactly(3).times
|
19
19
|
@pool.drain
|
20
20
|
end
|
21
|
-
|
22
|
-
it "should initiate the topping process for each DeliveryHandler before the pool is drained" do
|
23
|
-
Rapns::Daemon.delivery_queue.should_receive(:push).with(0x666).exactly(3).times
|
24
|
-
@pool.drain
|
25
|
-
end
|
26
21
|
end
|
@@ -1,115 +1,202 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe Rapns::Daemon::DeliveryHandler do
|
4
|
+
let(:delivery_handler) { Rapns::Daemon::DeliveryHandler.new(0) }
|
5
|
+
|
4
6
|
before do
|
5
7
|
@notification = Rapns::Notification.create!(:device_token => "a" * 64)
|
6
|
-
Rapns::Daemon.stub(:delivery_queue).and_return(Rapns::Daemon::DeliveryQueue.new
|
8
|
+
Rapns::Daemon.stub(:delivery_queue).and_return(Rapns::Daemon::DeliveryQueue.new)
|
7
9
|
Rapns::Daemon.delivery_queue.push(@notification)
|
8
|
-
@connection = mock("Connection", :connect => nil, :write => nil)
|
10
|
+
@connection = mock("Connection", :connect => nil, :write => nil, :close => nil, :select => nil, :read => nil)
|
9
11
|
Rapns::Daemon::Connection.stub(:new).and_return(@connection)
|
10
|
-
|
11
|
-
|
12
|
-
Rapns::Daemon.stub(:connection_pool).and_return(@connection_pool)
|
13
|
-
@delivery_handler = Rapns::Daemon::DeliveryHandler.new
|
12
|
+
configuration = mock("Configuration", :push => stub(:host => "gateway.push.apple.com", :port => 2195))
|
13
|
+
Rapns::Daemon.stub(:configuration).and_return(configuration)
|
14
14
|
@logger = mock("Logger", :error => nil, :info => nil)
|
15
15
|
Rapns::Daemon.stub(:logger).and_return(@logger)
|
16
16
|
end
|
17
17
|
|
18
|
+
it "instantiates a new connection" do
|
19
|
+
Rapns::Daemon::Connection.should_receive(:new).with('DeliveryHandler 0', 'gateway.push.apple.com', 2195)
|
20
|
+
delivery_handler
|
21
|
+
end
|
22
|
+
|
23
|
+
it "connects the socket when started" do
|
24
|
+
@connection.should_receive(:connect)
|
25
|
+
delivery_handler.start
|
26
|
+
delivery_handler.stop
|
27
|
+
end
|
28
|
+
|
29
|
+
it "pushes a STOP instruction into the queue when told to stop" do
|
30
|
+
Rapns::Daemon.delivery_queue.should_receive(:push).with(Rapns::Daemon::DeliveryHandler::STOP)
|
31
|
+
delivery_handler.stop
|
32
|
+
end
|
33
|
+
|
34
|
+
it "closes the connection when a STOP instruction is received" do
|
35
|
+
Rapns::Daemon.delivery_queue.push(Rapns::Daemon::DeliveryHandler::STOP)
|
36
|
+
delivery_handler.send(:handle_next_notification)
|
37
|
+
end
|
38
|
+
|
18
39
|
it "should pop a new notification from the delivery queue" do
|
19
40
|
Rapns::Daemon.delivery_queue.should_receive(:pop)
|
20
|
-
|
41
|
+
delivery_handler.send(:handle_next_notification)
|
21
42
|
end
|
22
43
|
|
23
|
-
it "
|
24
|
-
Rapns::Daemon.
|
25
|
-
|
44
|
+
it "does not attempt to deliver a notification when a STOP instruction is received" do
|
45
|
+
Rapns::Daemon.delivery_queue.pop # empty the queue
|
46
|
+
delivery_handler.should_not_receive(:deliver)
|
47
|
+
Rapns::Daemon.delivery_queue.push(Rapns::Daemon::DeliveryHandler::STOP)
|
48
|
+
delivery_handler.send(:handle_next_notification)
|
26
49
|
end
|
27
50
|
|
28
51
|
it "should send the binary version of the notification" do
|
29
52
|
@notification.stub((:to_binary)).and_return("hi mom")
|
30
53
|
@connection.should_receive(:write).with("hi mom")
|
31
|
-
|
54
|
+
delivery_handler.send(:handle_next_notification)
|
32
55
|
end
|
33
56
|
|
34
57
|
it "should log the notification delivery" do
|
35
58
|
Rapns::Daemon.logger.should_receive(:info).with("Notification #{@notification.id} delivered to #{@notification.device_token}")
|
36
|
-
|
59
|
+
delivery_handler.send(:handle_next_notification)
|
37
60
|
end
|
38
61
|
|
39
62
|
it "should mark the notification as delivered" do
|
40
|
-
expect {
|
63
|
+
expect { delivery_handler.send(:handle_next_notification); @notification.reload }.to change(@notification, :delivered).to(true)
|
41
64
|
end
|
42
65
|
|
43
66
|
it "should set the time the notification was delivered" do
|
44
67
|
@notification.delivered_at.should be_nil
|
45
|
-
|
68
|
+
delivery_handler.send(:handle_next_notification)
|
46
69
|
@notification.reload
|
47
70
|
@notification.delivered_at.should be_kind_of(Time)
|
48
71
|
end
|
49
72
|
|
50
73
|
it "should not trigger validations when saving the notification" do
|
51
74
|
@notification.should_receive(:save!).with(:validate => false)
|
52
|
-
|
75
|
+
delivery_handler.send(:handle_next_notification)
|
53
76
|
end
|
54
77
|
|
55
78
|
it "should log if an error is raised when updating the notification" do
|
56
79
|
e = StandardError.new("bork!")
|
57
80
|
@notification.stub(:save!).and_raise(e)
|
58
81
|
Rapns::Daemon.logger.should_receive(:error).with(e)
|
59
|
-
|
82
|
+
delivery_handler.send(:handle_next_notification)
|
60
83
|
end
|
61
84
|
|
62
|
-
it "should notify the delivery queue
|
63
|
-
Rapns::Daemon.delivery_queue.should_receive(:
|
64
|
-
|
85
|
+
it "should notify the delivery queue the notification has been processed" do
|
86
|
+
Rapns::Daemon.delivery_queue.should_receive(:notification_processed)
|
87
|
+
delivery_handler.send(:handle_next_notification)
|
65
88
|
end
|
66
89
|
|
67
90
|
describe "when delivery fails" do
|
68
91
|
before do
|
69
|
-
@
|
70
|
-
@
|
92
|
+
@connection.stub(:select => true, :read => [8, 4, 69].pack("ccN"), :reconnect => nil)
|
93
|
+
@error = Rapns::DeliveryError.new(4, "Missing payload", 69)
|
94
|
+
Rapns::DeliveryError.stub(:new => @error)
|
71
95
|
end
|
72
96
|
|
73
97
|
it "should set the notification as not delivered" do
|
74
98
|
@notification.should_receive(:delivered=).with(false)
|
75
|
-
|
99
|
+
delivery_handler.send(:handle_next_notification)
|
76
100
|
end
|
77
101
|
|
78
102
|
it "should set the notification delivered_at timestamp to nil" do
|
79
103
|
@notification.should_receive(:delivered_at=).with(nil)
|
80
|
-
|
104
|
+
delivery_handler.send(:handle_next_notification)
|
81
105
|
end
|
82
106
|
|
83
107
|
it "should set the notification as failed" do
|
84
108
|
@notification.should_receive(:failed=).with(true)
|
85
|
-
|
109
|
+
delivery_handler.send(:handle_next_notification)
|
86
110
|
end
|
87
111
|
|
88
112
|
it "should set the notification failed_at timestamp" do
|
89
113
|
now = Time.now
|
90
114
|
Time.stub(:now).and_return(now)
|
91
115
|
@notification.should_receive(:failed_at=).with(now)
|
92
|
-
|
116
|
+
delivery_handler.send(:handle_next_notification)
|
93
117
|
end
|
94
118
|
|
95
119
|
it "should set the notification error code" do
|
96
120
|
@notification.should_receive(:error_code=).with(4)
|
97
|
-
|
121
|
+
delivery_handler.send(:handle_next_notification)
|
98
122
|
end
|
99
123
|
|
100
124
|
it "should log the delivery error" do
|
101
125
|
Rapns::Daemon.logger.should_receive(:error).with(@error)
|
102
|
-
|
126
|
+
delivery_handler.send(:handle_next_notification)
|
103
127
|
end
|
104
128
|
|
105
129
|
it "should set the notification error description" do
|
106
130
|
@notification.should_receive(:error_description=).with("Missing payload")
|
107
|
-
|
131
|
+
delivery_handler.send(:handle_next_notification)
|
108
132
|
end
|
109
133
|
|
110
134
|
it "should skip validation when saving the notification" do
|
111
135
|
@notification.should_receive(:save!).with(:validate => false)
|
112
|
-
|
136
|
+
delivery_handler.send(:handle_next_notification)
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should not raise a DeliveryError if the packet cmd value is not 8" do
|
140
|
+
@connection.stub(:read).and_return([6, 4, 12].pack("ccN"))
|
141
|
+
expect { delivery_handler.send(:handle_next_notification) }.should_not raise_error(Rapns::DeliveryError)
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should not raise a DeliveryError if the status code is 0 (no error)" do
|
145
|
+
@connection.stub(:read).and_return([8, 0, 12].pack("ccN"))
|
146
|
+
expect { delivery_handler.send(:handle_next_notification) }.should_not raise_error(Rapns::DeliveryError)
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should read 6 bytes from the socket" do
|
150
|
+
@connection.should_receive(:read).with(6).and_return(nil)
|
151
|
+
delivery_handler.send(:handle_next_notification)
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should not attempt to read from the socket if the socket was not selected for reading after the timeout" do
|
155
|
+
@connection.stub(:select => nil)
|
156
|
+
@connection.should_not_receive(:read)
|
157
|
+
delivery_handler.send(:handle_next_notification)
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should not raise a DeliveryError if the socket read returns nothing" do
|
161
|
+
@connection.stub(:read).with(6).and_return(nil)
|
162
|
+
expect { delivery_handler.send(:handle_next_notification) }.should_not raise_error(Rapns::DeliveryError)
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should reconnect the socket" do
|
166
|
+
@connection.should_receive(:reconnect)
|
167
|
+
begin
|
168
|
+
delivery_handler.send(:handle_next_notification)
|
169
|
+
rescue Rapns::DeliveryError
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should log that the connection is being reconnected" do
|
174
|
+
Rapns::Daemon.logger.should_receive(:error).with("[DeliveryHandler 0] Error received, reconnecting...")
|
175
|
+
begin
|
176
|
+
delivery_handler.send(:handle_next_notification)
|
177
|
+
rescue Rapns::DeliveryError
|
178
|
+
end
|
113
179
|
end
|
114
180
|
end
|
115
|
-
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# describe Rapns::Daemon::Connection, "when receiving an error packet" do
|
184
|
+
# before do
|
185
|
+
# @notification = Rapns::Notification.create!(:device_token => "a" * 64)
|
186
|
+
# @notification.stub(:save!)
|
187
|
+
# @connection = Rapns::Daemon::Connection.new('Connection 0', 'gateway.push.apple.com', 2195)
|
188
|
+
# @ssl_socket = mock("SSLSocket", :write => nil, :flush => nil, :close => nil, :read => [8, 4, @notification.id].pack("ccN"))
|
189
|
+
# @connection.stub(:setup_ssl_context)
|
190
|
+
# @connection.stub(:connect_socket).and_return([@tcp_socket, @ssl_socket])
|
191
|
+
# IO.stub(:select).and_return([@ssl_socket, [], []])
|
192
|
+
# logger = mock("Logger", :error => nil, :warn => nil)
|
193
|
+
# Rapns::Daemon.stub(:logger).and_return(logger)
|
194
|
+
# @connection.connect
|
195
|
+
# end
|
196
|
+
#
|
197
|
+
# it "should raise a DeliveryError when an error is received" do
|
198
|
+
# expect { @connection.write("msg with an error") }.should raise_error(Rapns::DeliveryError)
|
199
|
+
# end
|
200
|
+
#
|
201
|
+
|
202
|
+
# end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Rapns::Daemon::DeliveryQueue do
|
4
|
+
let(:queue) { Rapns::Daemon::DeliveryQueue.new }
|
5
|
+
|
6
|
+
it 'behaves likes a normal qeue' do
|
7
|
+
obj = stub
|
8
|
+
queue.push obj
|
9
|
+
queue.pop.should == obj
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'returns false if notifications have not all been processed' do
|
13
|
+
queue.push stub
|
14
|
+
queue.notifications_processed?.should be_false
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'returns false if the queue is empty but notifications have not all been processed' do
|
18
|
+
queue.push stub
|
19
|
+
queue.pop
|
20
|
+
queue.notifications_processed?.should be_false
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'returns true if all notifications have been processed' do
|
24
|
+
queue.push stub
|
25
|
+
queue.pop
|
26
|
+
queue.notification_processed
|
27
|
+
queue.notifications_processed?.should be_true
|
28
|
+
end
|
29
|
+
end
|