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.
Files changed (145) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +99 -0
  3. data/LICENSE +7 -0
  4. data/README.md +189 -0
  5. data/bin/rpush +36 -0
  6. data/config/database.yml +44 -0
  7. data/lib/generators/rpush_generator.rb +44 -0
  8. data/lib/generators/templates/add_adm.rb +23 -0
  9. data/lib/generators/templates/add_alert_is_json_to_rapns_notifications.rb +9 -0
  10. data/lib/generators/templates/add_app_to_rapns.rb +11 -0
  11. data/lib/generators/templates/add_fail_after_to_rpush_notifications.rb +9 -0
  12. data/lib/generators/templates/add_gcm.rb +102 -0
  13. data/lib/generators/templates/add_rpush.rb +349 -0
  14. data/lib/generators/templates/add_wpns.rb +16 -0
  15. data/lib/generators/templates/create_rapns_apps.rb +16 -0
  16. data/lib/generators/templates/create_rapns_feedback.rb +18 -0
  17. data/lib/generators/templates/create_rapns_notifications.rb +29 -0
  18. data/lib/generators/templates/rename_rapns_to_rpush.rb +63 -0
  19. data/lib/generators/templates/rpush.rb +104 -0
  20. data/lib/rpush/TODO +3 -0
  21. data/lib/rpush/adm/app.rb +15 -0
  22. data/lib/rpush/adm/data_validator.rb +11 -0
  23. data/lib/rpush/adm/notification.rb +29 -0
  24. data/lib/rpush/apns/app.rb +29 -0
  25. data/lib/rpush/apns/binary_notification_validator.rb +12 -0
  26. data/lib/rpush/apns/device_token_format_validator.rb +12 -0
  27. data/lib/rpush/apns/feedback.rb +16 -0
  28. data/lib/rpush/apns/notification.rb +84 -0
  29. data/lib/rpush/apns_feedback.rb +13 -0
  30. data/lib/rpush/app.rb +18 -0
  31. data/lib/rpush/configuration.rb +75 -0
  32. data/lib/rpush/daemon/adm/delivery.rb +222 -0
  33. data/lib/rpush/daemon/adm.rb +9 -0
  34. data/lib/rpush/daemon/apns/certificate_expired_error.rb +20 -0
  35. data/lib/rpush/daemon/apns/delivery.rb +64 -0
  36. data/lib/rpush/daemon/apns/disconnection_error.rb +20 -0
  37. data/lib/rpush/daemon/apns/feedback_receiver.rb +79 -0
  38. data/lib/rpush/daemon/apns.rb +16 -0
  39. data/lib/rpush/daemon/app_runner.rb +187 -0
  40. data/lib/rpush/daemon/batch.rb +115 -0
  41. data/lib/rpush/daemon/constants.rb +59 -0
  42. data/lib/rpush/daemon/delivery.rb +28 -0
  43. data/lib/rpush/daemon/delivery_error.rb +19 -0
  44. data/lib/rpush/daemon/dispatcher/http.rb +21 -0
  45. data/lib/rpush/daemon/dispatcher/tcp.rb +30 -0
  46. data/lib/rpush/daemon/dispatcher_loop.rb +54 -0
  47. data/lib/rpush/daemon/dispatcher_loop_collection.rb +33 -0
  48. data/lib/rpush/daemon/feeder.rb +68 -0
  49. data/lib/rpush/daemon/gcm/delivery.rb +222 -0
  50. data/lib/rpush/daemon/gcm.rb +9 -0
  51. data/lib/rpush/daemon/interruptible_sleep.rb +61 -0
  52. data/lib/rpush/daemon/loggable.rb +31 -0
  53. data/lib/rpush/daemon/reflectable.rb +13 -0
  54. data/lib/rpush/daemon/retry_header_parser.rb +23 -0
  55. data/lib/rpush/daemon/retryable_error.rb +20 -0
  56. data/lib/rpush/daemon/service_config_methods.rb +33 -0
  57. data/lib/rpush/daemon/store/active_record/reconnectable.rb +68 -0
  58. data/lib/rpush/daemon/store/active_record.rb +154 -0
  59. data/lib/rpush/daemon/tcp_connection.rb +143 -0
  60. data/lib/rpush/daemon/too_many_requests_error.rb +20 -0
  61. data/lib/rpush/daemon/wpns/delivery.rb +132 -0
  62. data/lib/rpush/daemon/wpns.rb +9 -0
  63. data/lib/rpush/daemon.rb +140 -0
  64. data/lib/rpush/deprecatable.rb +23 -0
  65. data/lib/rpush/deprecation.rb +23 -0
  66. data/lib/rpush/embed.rb +28 -0
  67. data/lib/rpush/gcm/app.rb +11 -0
  68. data/lib/rpush/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +11 -0
  69. data/lib/rpush/gcm/notification.rb +30 -0
  70. data/lib/rpush/logger.rb +63 -0
  71. data/lib/rpush/multi_json_helper.rb +16 -0
  72. data/lib/rpush/notification.rb +69 -0
  73. data/lib/rpush/notifier.rb +52 -0
  74. data/lib/rpush/payload_data_size_validator.rb +10 -0
  75. data/lib/rpush/push.rb +16 -0
  76. data/lib/rpush/railtie.rb +11 -0
  77. data/lib/rpush/reflection.rb +58 -0
  78. data/lib/rpush/registration_ids_count_validator.rb +10 -0
  79. data/lib/rpush/version.rb +3 -0
  80. data/lib/rpush/wpns/app.rb +9 -0
  81. data/lib/rpush/wpns/notification.rb +26 -0
  82. data/lib/rpush.rb +62 -0
  83. data/lib/tasks/cane.rake +18 -0
  84. data/lib/tasks/rpush.rake +16 -0
  85. data/lib/tasks/test.rake +38 -0
  86. data/spec/functional/adm_spec.rb +43 -0
  87. data/spec/functional/apns_spec.rb +58 -0
  88. data/spec/functional/embed_spec.rb +49 -0
  89. data/spec/functional/gcm_spec.rb +42 -0
  90. data/spec/functional/wpns_spec.rb +41 -0
  91. data/spec/support/cert_with_password.pem +90 -0
  92. data/spec/support/cert_without_password.pem +59 -0
  93. data/spec/support/install.sh +32 -0
  94. data/spec/support/simplecov_helper.rb +20 -0
  95. data/spec/support/simplecov_quality_formatter.rb +8 -0
  96. data/spec/tmp/.gitkeep +0 -0
  97. data/spec/unit/adm/app_spec.rb +58 -0
  98. data/spec/unit/adm/notification_spec.rb +45 -0
  99. data/spec/unit/apns/app_spec.rb +29 -0
  100. data/spec/unit/apns/feedback_spec.rb +9 -0
  101. data/spec/unit/apns/notification_spec.rb +208 -0
  102. data/spec/unit/apns_feedback_spec.rb +21 -0
  103. data/spec/unit/app_spec.rb +30 -0
  104. data/spec/unit/configuration_spec.rb +45 -0
  105. data/spec/unit/daemon/adm/delivery_spec.rb +243 -0
  106. data/spec/unit/daemon/apns/certificate_expired_error_spec.rb +11 -0
  107. data/spec/unit/daemon/apns/delivery_spec.rb +101 -0
  108. data/spec/unit/daemon/apns/disconnection_error_spec.rb +18 -0
  109. data/spec/unit/daemon/apns/feedback_receiver_spec.rb +117 -0
  110. data/spec/unit/daemon/app_runner_spec.rb +292 -0
  111. data/spec/unit/daemon/batch_spec.rb +232 -0
  112. data/spec/unit/daemon/delivery_error_spec.rb +13 -0
  113. data/spec/unit/daemon/delivery_spec.rb +38 -0
  114. data/spec/unit/daemon/dispatcher/http_spec.rb +33 -0
  115. data/spec/unit/daemon/dispatcher/tcp_spec.rb +38 -0
  116. data/spec/unit/daemon/dispatcher_loop_collection_spec.rb +37 -0
  117. data/spec/unit/daemon/dispatcher_loop_spec.rb +71 -0
  118. data/spec/unit/daemon/feeder_spec.rb +98 -0
  119. data/spec/unit/daemon/gcm/delivery_spec.rb +310 -0
  120. data/spec/unit/daemon/interruptible_sleep_spec.rb +68 -0
  121. data/spec/unit/daemon/reflectable_spec.rb +27 -0
  122. data/spec/unit/daemon/retryable_error_spec.rb +14 -0
  123. data/spec/unit/daemon/service_config_methods_spec.rb +33 -0
  124. data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +114 -0
  125. data/spec/unit/daemon/store/active_record_spec.rb +357 -0
  126. data/spec/unit/daemon/tcp_connection_spec.rb +287 -0
  127. data/spec/unit/daemon/too_many_requests_error_spec.rb +14 -0
  128. data/spec/unit/daemon/wpns/delivery_spec.rb +159 -0
  129. data/spec/unit/daemon_spec.rb +159 -0
  130. data/spec/unit/deprecatable_spec.rb +32 -0
  131. data/spec/unit/deprecation_spec.rb +15 -0
  132. data/spec/unit/embed_spec.rb +50 -0
  133. data/spec/unit/gcm/app_spec.rb +4 -0
  134. data/spec/unit/gcm/notification_spec.rb +36 -0
  135. data/spec/unit/logger_spec.rb +127 -0
  136. data/spec/unit/notification_shared.rb +105 -0
  137. data/spec/unit/notification_spec.rb +15 -0
  138. data/spec/unit/notifier_spec.rb +49 -0
  139. data/spec/unit/push_spec.rb +43 -0
  140. data/spec/unit/reflection_spec.rb +30 -0
  141. data/spec/unit/rpush_spec.rb +9 -0
  142. data/spec/unit/wpns/app_spec.rb +4 -0
  143. data/spec/unit/wpns/notification_spec.rb +30 -0
  144. data/spec/unit_spec_helper.rb +101 -0
  145. 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