rpush 1.0.0-java
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/CHANGELOG.md +99 -0
- data/LICENSE +7 -0
- data/README.md +189 -0
- data/bin/rpush +36 -0
- data/config/database.yml +44 -0
- data/lib/generators/rpush_generator.rb +44 -0
- data/lib/generators/templates/add_adm.rb +23 -0
- data/lib/generators/templates/add_alert_is_json_to_rapns_notifications.rb +9 -0
- data/lib/generators/templates/add_app_to_rapns.rb +11 -0
- data/lib/generators/templates/add_fail_after_to_rpush_notifications.rb +9 -0
- data/lib/generators/templates/add_gcm.rb +102 -0
- data/lib/generators/templates/add_rpush.rb +349 -0
- data/lib/generators/templates/add_wpns.rb +16 -0
- data/lib/generators/templates/create_rapns_apps.rb +16 -0
- data/lib/generators/templates/create_rapns_feedback.rb +18 -0
- data/lib/generators/templates/create_rapns_notifications.rb +29 -0
- data/lib/generators/templates/rename_rapns_to_rpush.rb +63 -0
- data/lib/generators/templates/rpush.rb +104 -0
- data/lib/rpush/TODO +3 -0
- data/lib/rpush/adm/app.rb +15 -0
- data/lib/rpush/adm/data_validator.rb +11 -0
- data/lib/rpush/adm/notification.rb +29 -0
- data/lib/rpush/apns/app.rb +29 -0
- data/lib/rpush/apns/binary_notification_validator.rb +12 -0
- data/lib/rpush/apns/device_token_format_validator.rb +12 -0
- data/lib/rpush/apns/feedback.rb +16 -0
- data/lib/rpush/apns/notification.rb +84 -0
- data/lib/rpush/apns_feedback.rb +13 -0
- data/lib/rpush/app.rb +18 -0
- data/lib/rpush/configuration.rb +75 -0
- data/lib/rpush/daemon/adm/delivery.rb +222 -0
- data/lib/rpush/daemon/adm.rb +9 -0
- data/lib/rpush/daemon/apns/certificate_expired_error.rb +20 -0
- data/lib/rpush/daemon/apns/delivery.rb +64 -0
- data/lib/rpush/daemon/apns/disconnection_error.rb +20 -0
- data/lib/rpush/daemon/apns/feedback_receiver.rb +79 -0
- data/lib/rpush/daemon/apns.rb +16 -0
- data/lib/rpush/daemon/app_runner.rb +187 -0
- data/lib/rpush/daemon/batch.rb +115 -0
- data/lib/rpush/daemon/constants.rb +59 -0
- data/lib/rpush/daemon/delivery.rb +28 -0
- data/lib/rpush/daemon/delivery_error.rb +19 -0
- data/lib/rpush/daemon/dispatcher/http.rb +21 -0
- data/lib/rpush/daemon/dispatcher/tcp.rb +30 -0
- data/lib/rpush/daemon/dispatcher_loop.rb +54 -0
- data/lib/rpush/daemon/dispatcher_loop_collection.rb +33 -0
- data/lib/rpush/daemon/feeder.rb +68 -0
- data/lib/rpush/daemon/gcm/delivery.rb +222 -0
- data/lib/rpush/daemon/gcm.rb +9 -0
- data/lib/rpush/daemon/interruptible_sleep.rb +61 -0
- data/lib/rpush/daemon/loggable.rb +31 -0
- data/lib/rpush/daemon/reflectable.rb +13 -0
- data/lib/rpush/daemon/retry_header_parser.rb +23 -0
- data/lib/rpush/daemon/retryable_error.rb +20 -0
- data/lib/rpush/daemon/service_config_methods.rb +33 -0
- data/lib/rpush/daemon/store/active_record/reconnectable.rb +68 -0
- data/lib/rpush/daemon/store/active_record.rb +154 -0
- data/lib/rpush/daemon/tcp_connection.rb +143 -0
- data/lib/rpush/daemon/too_many_requests_error.rb +20 -0
- data/lib/rpush/daemon/wpns/delivery.rb +132 -0
- data/lib/rpush/daemon/wpns.rb +9 -0
- data/lib/rpush/daemon.rb +140 -0
- data/lib/rpush/deprecatable.rb +23 -0
- data/lib/rpush/deprecation.rb +23 -0
- data/lib/rpush/embed.rb +28 -0
- data/lib/rpush/gcm/app.rb +11 -0
- data/lib/rpush/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +11 -0
- data/lib/rpush/gcm/notification.rb +30 -0
- data/lib/rpush/logger.rb +63 -0
- data/lib/rpush/multi_json_helper.rb +16 -0
- data/lib/rpush/notification.rb +69 -0
- data/lib/rpush/notifier.rb +52 -0
- data/lib/rpush/payload_data_size_validator.rb +10 -0
- data/lib/rpush/push.rb +16 -0
- data/lib/rpush/railtie.rb +11 -0
- data/lib/rpush/reflection.rb +58 -0
- data/lib/rpush/registration_ids_count_validator.rb +10 -0
- data/lib/rpush/version.rb +3 -0
- data/lib/rpush/wpns/app.rb +9 -0
- data/lib/rpush/wpns/notification.rb +26 -0
- data/lib/rpush.rb +62 -0
- data/lib/tasks/cane.rake +18 -0
- data/lib/tasks/rpush.rake +16 -0
- data/lib/tasks/test.rake +38 -0
- data/spec/functional/adm_spec.rb +43 -0
- data/spec/functional/apns_spec.rb +58 -0
- data/spec/functional/embed_spec.rb +49 -0
- data/spec/functional/gcm_spec.rb +42 -0
- data/spec/functional/wpns_spec.rb +41 -0
- data/spec/support/cert_with_password.pem +90 -0
- data/spec/support/cert_without_password.pem +59 -0
- data/spec/support/install.sh +32 -0
- data/spec/support/simplecov_helper.rb +20 -0
- data/spec/support/simplecov_quality_formatter.rb +8 -0
- data/spec/tmp/.gitkeep +0 -0
- data/spec/unit/adm/app_spec.rb +58 -0
- data/spec/unit/adm/notification_spec.rb +45 -0
- data/spec/unit/apns/app_spec.rb +29 -0
- data/spec/unit/apns/feedback_spec.rb +9 -0
- data/spec/unit/apns/notification_spec.rb +208 -0
- data/spec/unit/apns_feedback_spec.rb +21 -0
- data/spec/unit/app_spec.rb +30 -0
- data/spec/unit/configuration_spec.rb +45 -0
- data/spec/unit/daemon/adm/delivery_spec.rb +243 -0
- data/spec/unit/daemon/apns/certificate_expired_error_spec.rb +11 -0
- data/spec/unit/daemon/apns/delivery_spec.rb +101 -0
- data/spec/unit/daemon/apns/disconnection_error_spec.rb +18 -0
- data/spec/unit/daemon/apns/feedback_receiver_spec.rb +117 -0
- data/spec/unit/daemon/app_runner_spec.rb +292 -0
- data/spec/unit/daemon/batch_spec.rb +232 -0
- data/spec/unit/daemon/delivery_error_spec.rb +13 -0
- data/spec/unit/daemon/delivery_spec.rb +38 -0
- data/spec/unit/daemon/dispatcher/http_spec.rb +33 -0
- data/spec/unit/daemon/dispatcher/tcp_spec.rb +38 -0
- data/spec/unit/daemon/dispatcher_loop_collection_spec.rb +37 -0
- data/spec/unit/daemon/dispatcher_loop_spec.rb +71 -0
- data/spec/unit/daemon/feeder_spec.rb +98 -0
- data/spec/unit/daemon/gcm/delivery_spec.rb +310 -0
- data/spec/unit/daemon/interruptible_sleep_spec.rb +68 -0
- data/spec/unit/daemon/reflectable_spec.rb +27 -0
- data/spec/unit/daemon/retryable_error_spec.rb +14 -0
- data/spec/unit/daemon/service_config_methods_spec.rb +33 -0
- data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +114 -0
- data/spec/unit/daemon/store/active_record_spec.rb +357 -0
- data/spec/unit/daemon/tcp_connection_spec.rb +287 -0
- data/spec/unit/daemon/too_many_requests_error_spec.rb +14 -0
- data/spec/unit/daemon/wpns/delivery_spec.rb +159 -0
- data/spec/unit/daemon_spec.rb +159 -0
- data/spec/unit/deprecatable_spec.rb +32 -0
- data/spec/unit/deprecation_spec.rb +15 -0
- data/spec/unit/embed_spec.rb +50 -0
- data/spec/unit/gcm/app_spec.rb +4 -0
- data/spec/unit/gcm/notification_spec.rb +36 -0
- data/spec/unit/logger_spec.rb +127 -0
- data/spec/unit/notification_shared.rb +105 -0
- data/spec/unit/notification_spec.rb +15 -0
- data/spec/unit/notifier_spec.rb +49 -0
- data/spec/unit/push_spec.rb +43 -0
- data/spec/unit/reflection_spec.rb +30 -0
- data/spec/unit/rpush_spec.rb +9 -0
- data/spec/unit/wpns/app_spec.rb +4 -0
- data/spec/unit/wpns/notification_spec.rb +30 -0
- data/spec/unit_spec_helper.rb +101 -0
- metadata +304 -0
@@ -0,0 +1,292 @@
|
|
1
|
+
require 'unit_spec_helper'
|
2
|
+
|
3
|
+
module Rpush
|
4
|
+
module AppRunnerSpecService
|
5
|
+
class App < Rpush::App
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module Daemon
|
10
|
+
module AppRunnerSpecService
|
11
|
+
extend ServiceConfigMethods
|
12
|
+
|
13
|
+
class ServiceLoop
|
14
|
+
def initialize(app)
|
15
|
+
end
|
16
|
+
|
17
|
+
def start
|
18
|
+
end
|
19
|
+
|
20
|
+
def stop
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
dispatcher :http
|
25
|
+
loops ServiceLoop
|
26
|
+
|
27
|
+
class Delivery
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe Rpush::Daemon::AppRunner, 'stop' do
|
34
|
+
let(:runner) { double }
|
35
|
+
before { Rpush::Daemon::AppRunner.runners['app'] = runner }
|
36
|
+
after { Rpush::Daemon::AppRunner.runners.clear }
|
37
|
+
|
38
|
+
it 'stops all runners' do
|
39
|
+
runner.should_receive(:stop)
|
40
|
+
Rpush::Daemon::AppRunner.stop
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe Rpush::Daemon::AppRunner, 'enqueue' do
|
45
|
+
let(:runner) { double(:enqueue => nil) }
|
46
|
+
let(:notification1) { double(:app_id => 1) }
|
47
|
+
let(:notification2) { double(:app_id => 2) }
|
48
|
+
let(:logger) { double(Rpush::Logger, :error => nil) }
|
49
|
+
|
50
|
+
before do
|
51
|
+
Rpush.stub(:logger => logger)
|
52
|
+
Rpush::Daemon::AppRunner.runners[1] = runner
|
53
|
+
end
|
54
|
+
|
55
|
+
after { Rpush::Daemon::AppRunner.runners.clear }
|
56
|
+
|
57
|
+
it 'batches notifications by app' do
|
58
|
+
batch = double.as_null_object
|
59
|
+
Rpush::Daemon::Batch.stub(:new => batch)
|
60
|
+
Rpush::Daemon::Batch.should_receive(:new).with([notification1])
|
61
|
+
Rpush::Daemon::Batch.should_receive(:new).with([notification2])
|
62
|
+
Rpush::Daemon::AppRunner.enqueue([notification1, notification2])
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'enqueues each batch' do
|
66
|
+
runner.should_receive(:enqueue).with(kind_of(Rpush::Daemon::Batch))
|
67
|
+
Rpush::Daemon::AppRunner.enqueue([notification1])
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'logs an error if there is no runner to deliver the notification' do
|
71
|
+
notification1.stub(:app_id => 2, :id => 123)
|
72
|
+
notification2.stub(:app_id => 2, :id => 456)
|
73
|
+
logger.should_receive(:error).with("No such app '#{notification1.app_id}' for notifications 123, 456.")
|
74
|
+
Rpush::Daemon::AppRunner.enqueue([notification1, notification2])
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe Rpush::Daemon::AppRunner, 'sync' do
|
79
|
+
let(:app) { double(Rpush::AppRunnerSpecService::App, :name => 'test') }
|
80
|
+
let(:new_app) { double(Rpush::AppRunnerSpecService::App, :name => 'new_test') }
|
81
|
+
let(:runner) { double(:sync => nil, :stop => nil, :start => nil) }
|
82
|
+
let(:logger) { double(Rpush::Logger, :error => nil, :warn => nil) }
|
83
|
+
let(:queue) { Queue.new }
|
84
|
+
|
85
|
+
before do
|
86
|
+
app.stub(:id => 1)
|
87
|
+
new_app.stub(:id => 2)
|
88
|
+
Queue.stub(:new => queue)
|
89
|
+
Rpush::Daemon::AppRunner.runners[app.id] = runner
|
90
|
+
Rpush::App.stub(:all => [app])
|
91
|
+
Rpush.stub(:logger => logger)
|
92
|
+
end
|
93
|
+
|
94
|
+
after { Rpush::Daemon::AppRunner.runners.clear }
|
95
|
+
|
96
|
+
it 'loads all apps' do
|
97
|
+
Rpush::App.should_receive(:all)
|
98
|
+
Rpush::Daemon::AppRunner.sync
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'instructs existing runners to sync' do
|
102
|
+
runner.should_receive(:sync).with(app)
|
103
|
+
Rpush::Daemon::AppRunner.sync
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'starts a runner for a new app' do
|
107
|
+
Rpush::App.stub(:all => [app, new_app])
|
108
|
+
new_runner = double
|
109
|
+
Rpush::Daemon::AppRunner.should_receive(:new).with(new_app).and_return(new_runner)
|
110
|
+
new_runner.should_receive(:start)
|
111
|
+
Rpush::Daemon::AppRunner.sync
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'deletes old runners' do
|
115
|
+
Rpush::App.stub(:all => [])
|
116
|
+
runner.should_receive(:stop)
|
117
|
+
Rpush::Daemon::AppRunner.sync
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'logs an error if the runner could not be started' do
|
121
|
+
Rpush::App.stub(:all => [app, new_app])
|
122
|
+
new_runner = double
|
123
|
+
Rpush::Daemon::AppRunner.should_receive(:new).with(new_app).and_return(new_runner)
|
124
|
+
new_runner.stub(:start).and_raise(StandardError)
|
125
|
+
Rpush.logger.should_receive(:error)
|
126
|
+
Rpush::Daemon::AppRunner.sync
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'reflects errors if the runner could not be started' do
|
130
|
+
Rpush::App.stub(:all => [app, new_app])
|
131
|
+
new_runner = double
|
132
|
+
Rpush::Daemon::AppRunner.should_receive(:new).with(new_app).and_return(new_runner)
|
133
|
+
e = StandardError.new
|
134
|
+
new_runner.stub(:start).and_raise(e)
|
135
|
+
Rpush::Daemon::AppRunner.should_receive(:reflect).with(:error, e)
|
136
|
+
Rpush::Daemon::AppRunner.sync
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe Rpush::Daemon::AppRunner, 'debug' do
|
141
|
+
let(:app) { double(Rpush::AppRunnerSpecService::App, :id => 1, :name => 'test', :connections => 1,
|
142
|
+
:environment => 'development', :certificate => TEST_CERT, :service_name => 'app_runner_spec_service') }
|
143
|
+
let(:logger) { double(Rpush::Logger, :info => nil) }
|
144
|
+
|
145
|
+
before do
|
146
|
+
Rpush::App.stub(:all => [app])
|
147
|
+
Rpush::Daemon.stub(:config => {})
|
148
|
+
Rpush.stub(:logger => logger)
|
149
|
+
Rpush::Daemon::AppRunner.sync
|
150
|
+
end
|
151
|
+
|
152
|
+
after { Rpush::Daemon::AppRunner.runners.clear }
|
153
|
+
|
154
|
+
it 'prints debug app states to the log' do
|
155
|
+
Rpush.logger.should_receive(:info).with("\ntest:\n dispatchers: 1\n queued: 0\n batch size: 0\n batch processed: 0\n idle: true\n")
|
156
|
+
Rpush::Daemon::AppRunner.debug
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
describe Rpush::Daemon::AppRunner, 'idle' do
|
161
|
+
let(:app) { double(Rpush::AppRunnerSpecService::App, :name => 'test', :connections => 1,
|
162
|
+
:environment => 'development', :certificate => TEST_CERT, :id => 1,
|
163
|
+
:service_name => 'app_runner_spec_service') }
|
164
|
+
let(:logger) { double(Rpush::Logger, :info => nil) }
|
165
|
+
|
166
|
+
before do
|
167
|
+
Rpush::App.stub(:all => [app])
|
168
|
+
Rpush.stub(:logger => logger)
|
169
|
+
Rpush::Daemon::AppRunner.sync
|
170
|
+
end
|
171
|
+
|
172
|
+
after { Rpush::Daemon::AppRunner.runners.clear }
|
173
|
+
|
174
|
+
it 'returns idle runners' do
|
175
|
+
runner = Rpush::Daemon::AppRunner.runners[app.id]
|
176
|
+
Rpush::Daemon::AppRunner.idle.should eq [runner]
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
describe Rpush::Daemon::AppRunner, 'wait' do
|
181
|
+
let(:app) { double(Rpush::AppRunnerSpecService::App, :id => 1, :name => 'test',
|
182
|
+
:connections => 1, :environment => 'development', :certificate => TEST_CERT,
|
183
|
+
:service_name => 'app_runner_spec_service') }
|
184
|
+
let(:logger) { double(Rpush::Logger, :info => nil) }
|
185
|
+
|
186
|
+
before do
|
187
|
+
Rpush::App.stub(:all => [app])
|
188
|
+
Rpush.stub(:logger => logger)
|
189
|
+
Rpush::Daemon::AppRunner.sync
|
190
|
+
end
|
191
|
+
|
192
|
+
after { Rpush::Daemon::AppRunner.runners.clear }
|
193
|
+
|
194
|
+
it 'waits until all runners are idle' do
|
195
|
+
Rpush::Daemon::AppRunner.runners.count.should eq 1
|
196
|
+
Timeout.timeout(5) { Rpush::Daemon::AppRunner.wait }
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
describe Rpush::Daemon::AppRunner do
|
201
|
+
let(:app) { double(Rpush::AppRunnerSpecService::App, :environment => :sandbox,
|
202
|
+
:connections => 1, :service_name => 'app_runner_spec_service',
|
203
|
+
:name => 'test') }
|
204
|
+
let(:runner) { Rpush::Daemon::AppRunner.new(app) }
|
205
|
+
let(:logger) { double(Rpush::Logger, :info => nil) }
|
206
|
+
let(:queue) { Queue.new }
|
207
|
+
let(:dispatcher_loop_collection) { Rpush::Daemon::DispatcherLoopCollection.new }
|
208
|
+
let(:service_loop) { double(Rpush::Daemon::AppRunnerSpecService::ServiceLoop,
|
209
|
+
:start => nil, :stop => nil) }
|
210
|
+
let(:store) { double(Rpush::Daemon::Store::ActiveRecord, release_connection: nil) }
|
211
|
+
|
212
|
+
before do
|
213
|
+
Rpush::Daemon.stub(store: store)
|
214
|
+
Rpush::Daemon::AppRunnerSpecService::ServiceLoop.stub(:new => service_loop)
|
215
|
+
Queue.stub(:new => queue)
|
216
|
+
Rpush.stub(:logger => logger)
|
217
|
+
Rpush::Daemon::DispatcherLoopCollection.stub(:new => dispatcher_loop_collection)
|
218
|
+
end
|
219
|
+
|
220
|
+
describe 'start' do
|
221
|
+
it 'starts a delivery dispatcher for each connection' do
|
222
|
+
app.stub(:connections => 2)
|
223
|
+
runner.start
|
224
|
+
runner.num_dispatchers.should eq 2
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'starts the loops' do
|
228
|
+
service_loop.should_receive(:start)
|
229
|
+
runner.start
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
describe 'enqueue' do
|
234
|
+
let(:notification) { double }
|
235
|
+
let(:batch) { double(:notifications => [notification]) }
|
236
|
+
|
237
|
+
it 'enqueues the batch' do
|
238
|
+
queue.should_receive(:push).with([notification, batch])
|
239
|
+
runner.enqueue(batch)
|
240
|
+
end
|
241
|
+
|
242
|
+
it 'reflects the notification has been enqueued' do
|
243
|
+
runner.should_receive(:reflect).with(:notification_enqueued, notification)
|
244
|
+
runner.enqueue(batch)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
describe 'stop' do
|
249
|
+
before { runner.start }
|
250
|
+
|
251
|
+
it 'stops the delivery dispatchers' do
|
252
|
+
dispatcher_loop_collection.should_receive(:stop)
|
253
|
+
runner.stop
|
254
|
+
end
|
255
|
+
|
256
|
+
it 'stop the loops' do
|
257
|
+
service_loop.should_receive(:stop)
|
258
|
+
runner.stop
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
describe 'idle?' do
|
263
|
+
it 'is idle if all notifications have been processed' do
|
264
|
+
runner.batch = double(:complete? => true)
|
265
|
+
runner.idle?.should be_true
|
266
|
+
end
|
267
|
+
|
268
|
+
it 'is idle if the runner has no associated batch' do
|
269
|
+
runner.batch = nil
|
270
|
+
runner.idle?.should be_true
|
271
|
+
end
|
272
|
+
|
273
|
+
it 'is not idle if not all notifications have been processed' do
|
274
|
+
runner.batch = double(:complete? => false)
|
275
|
+
runner.idle?.should be_false
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
describe 'sync' do
|
280
|
+
before { runner.start }
|
281
|
+
|
282
|
+
it 'reduces the number of dispatchers if needed' do
|
283
|
+
app.stub(:connections => 0)
|
284
|
+
expect { runner.sync(app) }.to change(runner, :num_dispatchers).to(0)
|
285
|
+
end
|
286
|
+
|
287
|
+
it 'increases the number of dispatchers if needed' do
|
288
|
+
app.stub(:connections => 2)
|
289
|
+
expect { runner.sync(app) }.to change(runner, :num_dispatchers).to(2)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
@@ -0,0 +1,232 @@
|
|
1
|
+
require 'unit_spec_helper'
|
2
|
+
|
3
|
+
describe Rpush::Daemon::Batch do
|
4
|
+
let(:notification1) { double(:notification1, :id => 1) }
|
5
|
+
let(:notification2) { double(:notification2, :id => 2) }
|
6
|
+
let(:batch) { Rpush::Daemon::Batch.new([notification1, notification2]) }
|
7
|
+
let(:store) { double.as_null_object }
|
8
|
+
let(:time) { Time.now }
|
9
|
+
|
10
|
+
before do
|
11
|
+
Time.stub(:now => time)
|
12
|
+
Rpush::Daemon.stub(:store => store)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'exposes the notifications' do
|
16
|
+
batch.notifications.should eq [notification1, notification2]
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'exposes the number notifications' do
|
20
|
+
batch.num_notifications.should eq 2
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'exposes the number notifications processed' do
|
24
|
+
batch.num_processed.should eq 0
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'increments the processed notifications count' do
|
28
|
+
expect { batch.notification_dispatched }.to change(batch, :num_processed).to(1)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'completes the batch when all notifications have been processed' do
|
32
|
+
batch.should_receive(:complete)
|
33
|
+
2.times { batch.notification_dispatched }
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'can be described' do
|
37
|
+
batch.describe.should eq '1, 2'
|
38
|
+
end
|
39
|
+
|
40
|
+
describe 'mark_delivered' do
|
41
|
+
describe 'batching is disabled' do
|
42
|
+
before { Rpush.config.batch_storage_updates = false }
|
43
|
+
|
44
|
+
it 'marks the notification as delivered immediately' do
|
45
|
+
store.should_receive(:mark_delivered).with(notification1, time)
|
46
|
+
batch.mark_delivered(notification1)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'reflects the notification was delivered' do
|
50
|
+
batch.should_receive(:reflect).with(:notification_delivered, notification1)
|
51
|
+
batch.mark_delivered(notification1)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe 'batching is enabled' do
|
56
|
+
before { Rpush.config.batch_storage_updates = true }
|
57
|
+
|
58
|
+
it 'marks the notification as delivered immediately without persisting' do
|
59
|
+
store.should_receive(:mark_delivered).with(notification1, time, :persist => false)
|
60
|
+
batch.mark_delivered(notification1)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'defers persisting' do
|
64
|
+
batch.mark_delivered(notification1)
|
65
|
+
batch.delivered.should eq [notification1]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe 'mark_failed' do
|
71
|
+
describe 'batching is disabled' do
|
72
|
+
before { Rpush.config.batch_storage_updates = false }
|
73
|
+
|
74
|
+
it 'marks the notification as failed' do
|
75
|
+
store.should_receive(:mark_failed).with(notification1, 1, 'an error', time)
|
76
|
+
batch.mark_failed(notification1, 1, 'an error')
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'reflects the notification failed' do
|
80
|
+
batch.should_receive(:reflect).with(:notification_delivered, notification1)
|
81
|
+
batch.mark_delivered(notification1)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe 'batching is enabled' do
|
86
|
+
before { Rpush.config.batch_storage_updates = true }
|
87
|
+
|
88
|
+
it 'marks the notification as failed without persisting' do
|
89
|
+
store.should_receive(:mark_failed).with(notification1, 1, 'an error', time, :persist => false)
|
90
|
+
batch.mark_failed(notification1, 1, 'an error')
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'defers persisting' do
|
94
|
+
Rpush.config.batch_storage_updates = true
|
95
|
+
batch.mark_failed(notification1, 1, 'an error')
|
96
|
+
batch.failed.should eq({[1, 'an error'] => [notification1]})
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe 'mark_retryable' do
|
102
|
+
describe 'batching is disabled' do
|
103
|
+
before { Rpush.config.batch_storage_updates = false }
|
104
|
+
|
105
|
+
it 'marks the notification as retryable' do
|
106
|
+
store.should_receive(:mark_retryable).with(notification1, time)
|
107
|
+
batch.mark_retryable(notification1, time)
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'reflects the notification will be retried' do
|
111
|
+
batch.should_receive(:reflect).with(:notification_will_retry, notification1)
|
112
|
+
batch.mark_retryable(notification1, time)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe 'batching is enabled' do
|
117
|
+
before { Rpush.config.batch_storage_updates = true }
|
118
|
+
|
119
|
+
it 'marks the notification as retryable without persisting' do
|
120
|
+
store.should_receive(:mark_retryable).with(notification1, time, :persist => false)
|
121
|
+
batch.mark_retryable(notification1, time)
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'defers persisting' do
|
125
|
+
batch.mark_retryable(notification1, time)
|
126
|
+
batch.retryable.should eq({time => [notification1]})
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe 'complete' do
|
132
|
+
before do
|
133
|
+
Rpush.config.batch_storage_updates = true
|
134
|
+
Rpush.stub(:logger => double.as_null_object)
|
135
|
+
batch.stub(:reflect)
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'clears the notifications' do
|
139
|
+
expect do
|
140
|
+
2.times { batch.notification_dispatched }
|
141
|
+
end.to change(batch, :notifications).to([])
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'identifies as complete' do
|
145
|
+
expect do
|
146
|
+
2.times { batch.notification_dispatched }
|
147
|
+
end.to change(batch, :complete?).to(be_true)
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'reflects errors raised during completion' do
|
151
|
+
e = StandardError.new
|
152
|
+
batch.stub(:complete_delivered).and_raise(e)
|
153
|
+
batch.should_receive(:reflect).with(:error, e)
|
154
|
+
2.times { batch.notification_dispatched }
|
155
|
+
end
|
156
|
+
|
157
|
+
describe 'delivered' do
|
158
|
+
def complete
|
159
|
+
[notification1, notification2].each do |n|
|
160
|
+
batch.mark_delivered(n)
|
161
|
+
batch.notification_dispatched
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'marks the batch as delivered' do
|
166
|
+
store.should_receive(:mark_batch_delivered).with([notification1, notification2])
|
167
|
+
complete
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'reflects the notifications were delivered' do
|
171
|
+
batch.should_receive(:reflect).with(:notification_delivered, notification1)
|
172
|
+
batch.should_receive(:reflect).with(:notification_delivered, notification2)
|
173
|
+
complete
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'clears the delivered notifications' do
|
177
|
+
complete
|
178
|
+
batch.delivered.should eq([])
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
describe 'failed' do
|
183
|
+
def complete
|
184
|
+
[notification1, notification2].each do |n|
|
185
|
+
batch.mark_failed(n, 1, 'an error')
|
186
|
+
batch.notification_dispatched
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'marks the batch as failed' do
|
191
|
+
store.should_receive(:mark_batch_failed).with([notification1, notification2], 1, 'an error')
|
192
|
+
complete
|
193
|
+
end
|
194
|
+
|
195
|
+
it 'reflects the notifications failed' do
|
196
|
+
batch.should_receive(:reflect).with(:notification_failed, notification1)
|
197
|
+
batch.should_receive(:reflect).with(:notification_failed, notification2)
|
198
|
+
complete
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'clears the failed notifications' do
|
202
|
+
complete
|
203
|
+
batch.failed.should eq({})
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
describe 'retryable' do
|
208
|
+
def complete
|
209
|
+
[notification1, notification2].each do |n|
|
210
|
+
batch.mark_retryable(n, time)
|
211
|
+
batch.notification_dispatched
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
it 'marks the batch as retryable' do
|
216
|
+
store.should_receive(:mark_batch_retryable).with([notification1, notification2], time)
|
217
|
+
complete
|
218
|
+
end
|
219
|
+
|
220
|
+
it 'reflects the notifications will be retried' do
|
221
|
+
batch.should_receive(:reflect).with(:notification_will_retry, notification1)
|
222
|
+
batch.should_receive(:reflect).with(:notification_will_retry, notification2)
|
223
|
+
complete
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'clears the retryable notifications' do
|
227
|
+
complete
|
228
|
+
batch.retryable.should eq({})
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require "unit_spec_helper"
|
2
|
+
|
3
|
+
describe Rpush::DeliveryError do
|
4
|
+
let(:error) { Rpush::DeliveryError.new(4, 12, "Missing payload") }
|
5
|
+
|
6
|
+
it "returns an informative message" do
|
7
|
+
error.to_s.should eq "Unable to deliver notification 12, received error 4 (Missing payload)"
|
8
|
+
end
|
9
|
+
|
10
|
+
it "returns the error code" do
|
11
|
+
error.code.should eq 4
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'unit_spec_helper'
|
2
|
+
|
3
|
+
describe Rpush::Daemon::Delivery do
|
4
|
+
|
5
|
+
class DeliverySpecDelivery < Rpush::Daemon::Delivery
|
6
|
+
def initialize(batch)
|
7
|
+
@batch = batch
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:now) { Time.parse("2014-10-14 00:00:00") }
|
12
|
+
let(:batch) { double(Rpush::Daemon::Batch) }
|
13
|
+
let(:delivery) { DeliverySpecDelivery.new(batch) }
|
14
|
+
let(:notification) { Rpush::Apns::Notification.new }
|
15
|
+
|
16
|
+
before { Time.stub(now: now) }
|
17
|
+
|
18
|
+
describe 'mark_retryable' do
|
19
|
+
|
20
|
+
it 'does not retry a notification with an expired fail_after' do
|
21
|
+
batch.should_receive(:mark_failed).with(notification, nil, "Notification failed to be delivered before 2014-10-13 23:00:00.")
|
22
|
+
notification.fail_after = Time.now - 1.hour
|
23
|
+
delivery.mark_retryable(notification, Time.now + 1.hour)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'retries the notification if does not have a fail_after time' do
|
27
|
+
batch.should_receive(:mark_retryable)
|
28
|
+
notification.fail_after = nil
|
29
|
+
delivery.mark_retryable(notification, Time.now + 1.hour)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'retries the notification if the fail_after time has not been reached' do
|
33
|
+
batch.should_receive(:mark_retryable)
|
34
|
+
notification.fail_after = Time.now + 1.hour
|
35
|
+
delivery.mark_retryable(notification, Time.now + 1.hour)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'unit_spec_helper'
|
2
|
+
|
3
|
+
describe Rpush::Daemon::Dispatcher::Http do
|
4
|
+
let(:app) { double }
|
5
|
+
let(:delivery_class) { double }
|
6
|
+
let(:notification) { double }
|
7
|
+
let(:batch) { double }
|
8
|
+
let(:http) { double }
|
9
|
+
let(:dispatcher) { Rpush::Daemon::Dispatcher::Http.new(app, delivery_class) }
|
10
|
+
|
11
|
+
before { Net::HTTP::Persistent.stub(:new => http) }
|
12
|
+
|
13
|
+
it 'constructs a new persistent connection' do
|
14
|
+
Net::HTTP::Persistent.should_receive(:new)
|
15
|
+
Rpush::Daemon::Dispatcher::Http.new(app, delivery_class)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'dispatch' do
|
19
|
+
it 'delivers the notification' do
|
20
|
+
delivery = double
|
21
|
+
delivery_class.should_receive(:new).with(app, http, notification, batch).and_return(delivery)
|
22
|
+
delivery.should_receive(:perform)
|
23
|
+
dispatcher.dispatch(notification, batch)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe 'cleanup' do
|
28
|
+
it 'closes the connection' do
|
29
|
+
http.should_receive(:shutdown)
|
30
|
+
dispatcher.cleanup
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'unit_spec_helper'
|
2
|
+
|
3
|
+
describe Rpush::Daemon::Dispatcher::Tcp do
|
4
|
+
let(:app) { double }
|
5
|
+
let(:delivery) { double(:perform => nil) }
|
6
|
+
let(:delivery_class) { double(:new => delivery) }
|
7
|
+
let(:notification) { double }
|
8
|
+
let(:batch) { double }
|
9
|
+
let(:connection) { double(Rpush::Daemon::TcpConnection, :connect => nil) }
|
10
|
+
let(:host) { 'localhost' }
|
11
|
+
let(:port) { 1234 }
|
12
|
+
let(:host_proc) { Proc.new { |app| [host, port] } }
|
13
|
+
let(:dispatcher) { Rpush::Daemon::Dispatcher::Tcp.new(app, delivery_class, :host => host_proc) }
|
14
|
+
|
15
|
+
before { Rpush::Daemon::TcpConnection.stub(:new => connection) }
|
16
|
+
|
17
|
+
describe 'dispatch' do
|
18
|
+
it 'lazily connects the socket' do
|
19
|
+
Rpush::Daemon::TcpConnection.should_receive(:new).with(app, host, port).and_return(connection)
|
20
|
+
connection.should_receive(:connect)
|
21
|
+
dispatcher.dispatch(notification, batch)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'delivers the notification' do
|
25
|
+
delivery_class.should_receive(:new).with(app, connection, notification, batch).and_return(delivery)
|
26
|
+
delivery.should_receive(:perform)
|
27
|
+
dispatcher.dispatch(notification, batch)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe 'cleanup' do
|
32
|
+
it 'closes the connection' do
|
33
|
+
dispatcher.dispatch(notification, batch) # lazily initialize connection
|
34
|
+
connection.should_receive(:close)
|
35
|
+
dispatcher.cleanup
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'unit_spec_helper'
|
2
|
+
|
3
|
+
describe Rpush::Daemon::DispatcherLoopCollection do
|
4
|
+
let(:dispatcher_loop) { double.as_null_object }
|
5
|
+
let(:collection) { Rpush::Daemon::DispatcherLoopCollection.new }
|
6
|
+
|
7
|
+
it 'returns the size of the collection' do
|
8
|
+
collection.push(dispatcher_loop)
|
9
|
+
collection.size.should eq 1
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'pops a dispatcher loop from the collection' do
|
13
|
+
collection.push(dispatcher_loop)
|
14
|
+
dispatcher_loop.should_receive(:stop)
|
15
|
+
dispatcher_loop.should_receive(:wakeup)
|
16
|
+
dispatcher_loop.should_receive(:wait)
|
17
|
+
collection.pop
|
18
|
+
collection.size.should eq 0
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'wakes up all dispatcher loops when popping a single dispatcher_loop' do
|
22
|
+
collection.push(dispatcher_loop)
|
23
|
+
dispatcher_loop2 = double.as_null_object
|
24
|
+
collection.push(dispatcher_loop2)
|
25
|
+
dispatcher_loop.should_receive(:wakeup)
|
26
|
+
dispatcher_loop2.should_receive(:wakeup)
|
27
|
+
collection.pop
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'stops all dispatcher detetcloops' do
|
31
|
+
collection.push(dispatcher_loop)
|
32
|
+
dispatcher_loop.should_receive(:stop)
|
33
|
+
dispatcher_loop.should_receive(:wakeup)
|
34
|
+
dispatcher_loop.should_receive(:wait)
|
35
|
+
collection.stop
|
36
|
+
end
|
37
|
+
end
|