rpush_extended 3.2.5

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 (248) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +365 -0
  3. data/LICENSE +7 -0
  4. data/README.md +393 -0
  5. data/bin/rpush +4 -0
  6. data/lib/generators/rpush_config_generator.rb +7 -0
  7. data/lib/generators/rpush_migration_generator.rb +66 -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 +117 -0
  13. data/lib/generators/templates/add_rpush.rb +402 -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 +25 -0
  17. data/lib/generators/templates/create_rapns_notifications.rb +36 -0
  18. data/lib/generators/templates/rename_rapns_to_rpush.rb +87 -0
  19. data/lib/generators/templates/rpush.rb +135 -0
  20. data/lib/generators/templates/rpush_2_0_0_updates.rb +79 -0
  21. data/lib/generators/templates/rpush_2_1_0_updates.rb +11 -0
  22. data/lib/generators/templates/rpush_2_6_0_updates.rb +10 -0
  23. data/lib/generators/templates/rpush_2_7_0_updates.rb +12 -0
  24. data/lib/generators/templates/rpush_3_0_0_updates.rb +11 -0
  25. data/lib/generators/templates/rpush_3_0_1_updates.rb +13 -0
  26. data/lib/generators/templates/rpush_3_1_0_add_pushy.rb +9 -0
  27. data/lib/generators/templates/rpush_3_1_1_updates.rb +15 -0
  28. data/lib/generators/templates/rpush_3_2_0_add_apns_p8.rb +15 -0
  29. data/lib/generators/templates/rpush_3_2_4_updates.rb +9 -0
  30. data/lib/generators/templates/rpush_3_3_0_updates.rb +9 -0
  31. data/lib/generators/templates/rpush_3_3_1_updates.rb +11 -0
  32. data/lib/rpush/apns_feedback.rb +17 -0
  33. data/lib/rpush/cli.rb +213 -0
  34. data/lib/rpush/client/active_model/adm/app.rb +23 -0
  35. data/lib/rpush/client/active_model/adm/data_validator.rb +14 -0
  36. data/lib/rpush/client/active_model/adm/notification.rb +28 -0
  37. data/lib/rpush/client/active_model/apns/app.rb +37 -0
  38. data/lib/rpush/client/active_model/apns/binary_notification_validator.rb +16 -0
  39. data/lib/rpush/client/active_model/apns/device_token_format_validator.rb +14 -0
  40. data/lib/rpush/client/active_model/apns/notification.rb +104 -0
  41. data/lib/rpush/client/active_model/apns2/app.rb +15 -0
  42. data/lib/rpush/client/active_model/apns2/notification.rb +9 -0
  43. data/lib/rpush/client/active_model/apnsp8/app.rb +23 -0
  44. data/lib/rpush/client/active_model/apnsp8/notification.rb +9 -0
  45. data/lib/rpush/client/active_model/gcm/app.rb +19 -0
  46. data/lib/rpush/client/active_model/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +14 -0
  47. data/lib/rpush/client/active_model/gcm/notification.rb +59 -0
  48. data/lib/rpush/client/active_model/notification.rb +22 -0
  49. data/lib/rpush/client/active_model/payload_data_size_validator.rb +13 -0
  50. data/lib/rpush/client/active_model/pushy/app.rb +20 -0
  51. data/lib/rpush/client/active_model/pushy/notification.rb +31 -0
  52. data/lib/rpush/client/active_model/pushy/time_to_live_validator.rb +14 -0
  53. data/lib/rpush/client/active_model/registration_ids_count_validator.rb +13 -0
  54. data/lib/rpush/client/active_model/wns/app.rb +23 -0
  55. data/lib/rpush/client/active_model/wns/notification.rb +32 -0
  56. data/lib/rpush/client/active_model/wpns/app.rb +13 -0
  57. data/lib/rpush/client/active_model/wpns/notification.rb +28 -0
  58. data/lib/rpush/client/active_model.rb +34 -0
  59. data/lib/rpush/client/active_record/adm/app.rb +11 -0
  60. data/lib/rpush/client/active_record/adm/notification.rb +11 -0
  61. data/lib/rpush/client/active_record/apns/app.rb +11 -0
  62. data/lib/rpush/client/active_record/apns/feedback.rb +18 -0
  63. data/lib/rpush/client/active_record/apns/notification.rb +40 -0
  64. data/lib/rpush/client/active_record/apns2/app.rb +11 -0
  65. data/lib/rpush/client/active_record/apns2/notification.rb +10 -0
  66. data/lib/rpush/client/active_record/apnsp8/app.rb +11 -0
  67. data/lib/rpush/client/active_record/apnsp8/notification.rb +10 -0
  68. data/lib/rpush/client/active_record/app.rb +13 -0
  69. data/lib/rpush/client/active_record/gcm/app.rb +11 -0
  70. data/lib/rpush/client/active_record/gcm/notification.rb +11 -0
  71. data/lib/rpush/client/active_record/notification.rb +42 -0
  72. data/lib/rpush/client/active_record/pushy/app.rb +11 -0
  73. data/lib/rpush/client/active_record/pushy/notification.rb +11 -0
  74. data/lib/rpush/client/active_record/wns/app.rb +11 -0
  75. data/lib/rpush/client/active_record/wns/badge_notification.rb +15 -0
  76. data/lib/rpush/client/active_record/wns/notification.rb +11 -0
  77. data/lib/rpush/client/active_record/wns/raw_notification.rb +13 -0
  78. data/lib/rpush/client/active_record/wpns/app.rb +11 -0
  79. data/lib/rpush/client/active_record/wpns/notification.rb +11 -0
  80. data/lib/rpush/client/active_record.rb +33 -0
  81. data/lib/rpush/client/redis/adm/app.rb +14 -0
  82. data/lib/rpush/client/redis/adm/notification.rb +11 -0
  83. data/lib/rpush/client/redis/apns/app.rb +11 -0
  84. data/lib/rpush/client/redis/apns/feedback.rb +20 -0
  85. data/lib/rpush/client/redis/apns/notification.rb +11 -0
  86. data/lib/rpush/client/redis/apns2/app.rb +11 -0
  87. data/lib/rpush/client/redis/apns2/notification.rb +11 -0
  88. data/lib/rpush/client/redis/apnsp8/app.rb +11 -0
  89. data/lib/rpush/client/redis/apnsp8/notification.rb +11 -0
  90. data/lib/rpush/client/redis/app.rb +29 -0
  91. data/lib/rpush/client/redis/gcm/app.rb +11 -0
  92. data/lib/rpush/client/redis/gcm/notification.rb +11 -0
  93. data/lib/rpush/client/redis/notification.rb +74 -0
  94. data/lib/rpush/client/redis/pushy/app.rb +16 -0
  95. data/lib/rpush/client/redis/pushy/notification.rb +18 -0
  96. data/lib/rpush/client/redis/wns/app.rb +14 -0
  97. data/lib/rpush/client/redis/wns/badge_notification.rb +15 -0
  98. data/lib/rpush/client/redis/wns/notification.rb +11 -0
  99. data/lib/rpush/client/redis/wns/raw_notification.rb +11 -0
  100. data/lib/rpush/client/redis/wpns/app.rb +11 -0
  101. data/lib/rpush/client/redis/wpns/notification.rb +11 -0
  102. data/lib/rpush/client/redis.rb +56 -0
  103. data/lib/rpush/configuration.rb +115 -0
  104. data/lib/rpush/daemon/adm/delivery.rb +226 -0
  105. data/lib/rpush/daemon/adm.rb +9 -0
  106. data/lib/rpush/daemon/apns/delivery.rb +43 -0
  107. data/lib/rpush/daemon/apns/feedback_receiver.rb +90 -0
  108. data/lib/rpush/daemon/apns.rb +17 -0
  109. data/lib/rpush/daemon/apns2/delivery.rb +127 -0
  110. data/lib/rpush/daemon/apns2.rb +10 -0
  111. data/lib/rpush/daemon/apnsp8/delivery.rb +166 -0
  112. data/lib/rpush/daemon/apnsp8/token.rb +43 -0
  113. data/lib/rpush/daemon/apnsp8.rb +10 -0
  114. data/lib/rpush/daemon/app_runner.rb +190 -0
  115. data/lib/rpush/daemon/batch.rb +138 -0
  116. data/lib/rpush/daemon/constants.rb +59 -0
  117. data/lib/rpush/daemon/delivery.rb +46 -0
  118. data/lib/rpush/daemon/delivery_error.rb +27 -0
  119. data/lib/rpush/daemon/dispatcher/apns_http2.rb +51 -0
  120. data/lib/rpush/daemon/dispatcher/apns_tcp.rb +152 -0
  121. data/lib/rpush/daemon/dispatcher/apnsp8_http2.rb +33 -0
  122. data/lib/rpush/daemon/dispatcher/http.rb +21 -0
  123. data/lib/rpush/daemon/dispatcher/tcp.rb +22 -0
  124. data/lib/rpush/daemon/dispatcher_loop.rb +73 -0
  125. data/lib/rpush/daemon/errors.rb +18 -0
  126. data/lib/rpush/daemon/feeder.rb +69 -0
  127. data/lib/rpush/daemon/gcm/delivery.rb +241 -0
  128. data/lib/rpush/daemon/gcm.rb +9 -0
  129. data/lib/rpush/daemon/interruptible_sleep.rb +24 -0
  130. data/lib/rpush/daemon/loggable.rb +33 -0
  131. data/lib/rpush/daemon/proc_title.rb +17 -0
  132. data/lib/rpush/daemon/pushy/delivery.rb +90 -0
  133. data/lib/rpush/daemon/pushy.rb +9 -0
  134. data/lib/rpush/daemon/queue_payload.rb +12 -0
  135. data/lib/rpush/daemon/retry_header_parser.rb +23 -0
  136. data/lib/rpush/daemon/retryable_error.rb +22 -0
  137. data/lib/rpush/daemon/ring_buffer.rb +16 -0
  138. data/lib/rpush/daemon/rpc/client.rb +27 -0
  139. data/lib/rpush/daemon/rpc/server.rb +82 -0
  140. data/lib/rpush/daemon/rpc.rb +9 -0
  141. data/lib/rpush/daemon/service_config_methods.rb +51 -0
  142. data/lib/rpush/daemon/signal_handler.rb +75 -0
  143. data/lib/rpush/daemon/store/active_record/reconnectable.rb +80 -0
  144. data/lib/rpush/daemon/store/active_record.rb +214 -0
  145. data/lib/rpush/daemon/store/interface.rb +20 -0
  146. data/lib/rpush/daemon/store/redis.rb +166 -0
  147. data/lib/rpush/daemon/string_helpers.rb +15 -0
  148. data/lib/rpush/daemon/synchronizer.rb +62 -0
  149. data/lib/rpush/daemon/tcp_connection.rb +190 -0
  150. data/lib/rpush/daemon/wns/badge_request.rb +32 -0
  151. data/lib/rpush/daemon/wns/delivery.rb +178 -0
  152. data/lib/rpush/daemon/wns/post_request.rb +33 -0
  153. data/lib/rpush/daemon/wns/raw_request.rb +22 -0
  154. data/lib/rpush/daemon/wns/toast_request.rb +54 -0
  155. data/lib/rpush/daemon/wns.rb +9 -0
  156. data/lib/rpush/daemon/wpns/delivery.rb +132 -0
  157. data/lib/rpush/daemon/wpns.rb +9 -0
  158. data/lib/rpush/daemon.rb +179 -0
  159. data/lib/rpush/deprecatable.rb +24 -0
  160. data/lib/rpush/deprecation.rb +26 -0
  161. data/lib/rpush/embed.rb +41 -0
  162. data/lib/rpush/logger.rb +92 -0
  163. data/lib/rpush/multi_json_helper.rb +16 -0
  164. data/lib/rpush/plugin.rb +44 -0
  165. data/lib/rpush/push.rb +11 -0
  166. data/lib/rpush/reflectable.rb +13 -0
  167. data/lib/rpush/reflection_collection.rb +44 -0
  168. data/lib/rpush/reflection_public_methods.rb +11 -0
  169. data/lib/rpush/version.rb +14 -0
  170. data/lib/rpush.rb +43 -0
  171. data/lib/tasks/quality.rake +35 -0
  172. data/lib/tasks/test.rake +69 -0
  173. data/spec/.rubocop.yml +4 -0
  174. data/spec/functional/adm_spec.rb +50 -0
  175. data/spec/functional/apns2_spec.rb +232 -0
  176. data/spec/functional/apns_spec.rb +162 -0
  177. data/spec/functional/cli_spec.rb +36 -0
  178. data/spec/functional/embed_spec.rb +49 -0
  179. data/spec/functional/gcm_spec.rb +46 -0
  180. data/spec/functional/new_app_spec.rb +44 -0
  181. data/spec/functional/pushy_spec.rb +22 -0
  182. data/spec/functional/retry_spec.rb +42 -0
  183. data/spec/functional/synchronization_spec.rb +97 -0
  184. data/spec/functional/wpns_spec.rb +71 -0
  185. data/spec/functional_spec_helper.rb +32 -0
  186. data/spec/spec_helper.rb +69 -0
  187. data/spec/support/active_record_setup.rb +73 -0
  188. data/spec/support/cert_with_password.pem +90 -0
  189. data/spec/support/cert_without_password.pem +59 -0
  190. data/spec/support/config/database.yml +44 -0
  191. data/spec/support/simplecov_helper.rb +24 -0
  192. data/spec/support/simplecov_quality_formatter.rb +12 -0
  193. data/spec/tmp/.gitkeep +0 -0
  194. data/spec/unit/apns_feedback_spec.rb +28 -0
  195. data/spec/unit/client/active_record/adm/app_spec.rb +58 -0
  196. data/spec/unit/client/active_record/adm/notification_spec.rb +43 -0
  197. data/spec/unit/client/active_record/apns/app_spec.rb +29 -0
  198. data/spec/unit/client/active_record/apns/feedback_spec.rb +9 -0
  199. data/spec/unit/client/active_record/apns/notification_spec.rb +324 -0
  200. data/spec/unit/client/active_record/app_spec.rb +30 -0
  201. data/spec/unit/client/active_record/gcm/app_spec.rb +4 -0
  202. data/spec/unit/client/active_record/gcm/notification_spec.rb +67 -0
  203. data/spec/unit/client/active_record/notification_spec.rb +21 -0
  204. data/spec/unit/client/active_record/pushy/app_spec.rb +17 -0
  205. data/spec/unit/client/active_record/pushy/notification_spec.rb +65 -0
  206. data/spec/unit/client/active_record/wns/badge_notification_spec.rb +15 -0
  207. data/spec/unit/client/active_record/wns/raw_notification_spec.rb +26 -0
  208. data/spec/unit/client/active_record/wpns/app_spec.rb +4 -0
  209. data/spec/unit/client/active_record/wpns/notification_spec.rb +21 -0
  210. data/spec/unit/configuration_spec.rb +46 -0
  211. data/spec/unit/daemon/adm/delivery_spec.rb +253 -0
  212. data/spec/unit/daemon/apns/certificate_expired_error_spec.rb +11 -0
  213. data/spec/unit/daemon/apns/delivery_spec.rb +108 -0
  214. data/spec/unit/daemon/apns/feedback_receiver_spec.rb +119 -0
  215. data/spec/unit/daemon/app_runner_spec.rb +188 -0
  216. data/spec/unit/daemon/batch_spec.rb +169 -0
  217. data/spec/unit/daemon/delivery_error_spec.rb +13 -0
  218. data/spec/unit/daemon/delivery_spec.rb +51 -0
  219. data/spec/unit/daemon/dispatcher/http_spec.rb +34 -0
  220. data/spec/unit/daemon/dispatcher/tcp_spec.rb +32 -0
  221. data/spec/unit/daemon/dispatcher_loop_spec.rb +53 -0
  222. data/spec/unit/daemon/feeder_spec.rb +96 -0
  223. data/spec/unit/daemon/gcm/delivery_spec.rb +387 -0
  224. data/spec/unit/daemon/proc_title_spec.rb +11 -0
  225. data/spec/unit/daemon/pushy/delivery_spec.rb +159 -0
  226. data/spec/unit/daemon/retryable_error_spec.rb +14 -0
  227. data/spec/unit/daemon/service_config_methods_spec.rb +36 -0
  228. data/spec/unit/daemon/signal_handler_spec.rb +99 -0
  229. data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +165 -0
  230. data/spec/unit/daemon/store/active_record_spec.rb +357 -0
  231. data/spec/unit/daemon/store/redis_spec.rb +365 -0
  232. data/spec/unit/daemon/tcp_connection_spec.rb +292 -0
  233. data/spec/unit/daemon/wns/delivery_spec.rb +176 -0
  234. data/spec/unit/daemon/wns/post_request_spec.rb +117 -0
  235. data/spec/unit/daemon/wpns/delivery_spec.rb +167 -0
  236. data/spec/unit/daemon_spec.rb +138 -0
  237. data/spec/unit/deprecatable_spec.rb +32 -0
  238. data/spec/unit/deprecation_spec.rb +15 -0
  239. data/spec/unit/embed_spec.rb +47 -0
  240. data/spec/unit/logger_spec.rb +127 -0
  241. data/spec/unit/notification_shared.rb +53 -0
  242. data/spec/unit/plugin_spec.rb +36 -0
  243. data/spec/unit/push_spec.rb +34 -0
  244. data/spec/unit/reflectable_spec.rb +27 -0
  245. data/spec/unit/reflection_collection_spec.rb +26 -0
  246. data/spec/unit/rpush_spec.rb +8 -0
  247. data/spec/unit_spec_helper.rb +26 -0
  248. metadata +709 -0
@@ -0,0 +1,21 @@
1
+ require 'unit_spec_helper'
2
+ require 'unit/notification_shared.rb'
3
+
4
+ describe Rpush::Client::ActiveRecord::Wpns::Notification do
5
+ it_should_behave_like 'an Notification subclass'
6
+ let(:app) { Rpush::Client::ActiveRecord::Wpns::App.create!(name: 'test', auth_key: 'abc') }
7
+ let(:notification_class) { Rpush::Client::ActiveRecord::Wpns::Notification }
8
+ let(:notification) { notification_class.new }
9
+
10
+ it "should have an url in the uri parameter" do
11
+ notification = Rpush::Client::ActiveRecord::Wpns::Notification.new(uri: "somthing")
12
+ notification.valid?
13
+ expect(notification.errors[:uri]).to include('is invalid')
14
+ end
15
+
16
+ it "should be invalid if there's no data" do
17
+ notification = Rpush::Client::ActiveRecord::Wpns::Notification.new(data: {})
18
+ notification.valid?
19
+ expect(notification.errors[:data]).to include("can't be blank")
20
+ end
21
+ end if active_record?
@@ -0,0 +1,46 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush do
4
+ let(:config) { Rpush.config }
5
+
6
+ before do
7
+ allow(Rpush).to receive_messages(require: nil)
8
+ allow(Rpush).to receive_messages(config: config)
9
+ end
10
+
11
+ it 'yields a configure block' do
12
+ expect { |b| Rpush.configure(&b) }.to yield_with_args(config)
13
+ end
14
+ end
15
+
16
+ describe Rpush::Configuration do
17
+ let(:config) do
18
+ Rpush::Deprecation.muted do
19
+ Rpush::Configuration.new
20
+ end
21
+ end
22
+
23
+ it 'can be updated' do
24
+ Rpush::Deprecation.muted do
25
+ new_config = Rpush::Configuration.new
26
+ new_config.batch_size = 200
27
+ expect { config.update(new_config) }.to change(config, :batch_size).to(200)
28
+ end
29
+ end
30
+
31
+ it 'sets the pid_file relative if not absolute' do
32
+ config.pid_file = 'tmp/rpush.pid'
33
+ expect(config.pid_file).to eq '/tmp/rails_root/tmp/rpush.pid'
34
+ end
35
+
36
+ it 'does not alter an absolute pid_file path' do
37
+ config.pid_file = '/tmp/rpush.pid'
38
+ expect(config.pid_file).to eq '/tmp/rpush.pid'
39
+ end
40
+
41
+ it 'delegate redis_options to Modis' do
42
+ Rpush.config.client = :redis
43
+ Rpush.config.redis_options = { hi: :mom }
44
+ expect(Modis.redis_options).to eq(hi: :mom)
45
+ end
46
+ end
@@ -0,0 +1,253 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Daemon::Adm::Delivery do
4
+ let(:app) { Rpush::Adm::App.create!(name: 'MyApp', client_id: 'CLIENT_ID', client_secret: 'CLIENT_SECRET') }
5
+ let(:notification) { Rpush::Adm::Notification.create!(app: app, registration_ids: ['xyz'], deliver_after: Time.now, data: { 'message' => 'test' }) }
6
+ let(:logger) { double(error: nil, info: nil, warn: nil) }
7
+ let(:response) { double(code: 200, header: {}) }
8
+ let(:http) { double(shutdown: nil, request: response) }
9
+ let(:now) { Time.parse('2012-10-14 00:00:00') }
10
+ let(:batch) { double(mark_failed: nil, mark_delivered: nil, mark_retryable: nil, notification_processed: nil) }
11
+ let(:delivery) { Rpush::Daemon::Adm::Delivery.new(app, http, notification, batch) }
12
+ let(:store) { double(create_adm_notification: double(id: 2)) }
13
+
14
+ def perform
15
+ delivery.perform
16
+ end
17
+
18
+ before do
19
+ app.access_token = 'ACCESS_TOKEN'
20
+ app.access_token_expiration = Time.now + 1.month
21
+
22
+ allow(delivery).to receive_messages(reflect: nil)
23
+ allow(Rpush::Daemon).to receive_messages(store: store)
24
+ allow(Time).to receive_messages(now: now)
25
+ allow(Rpush).to receive_messages(logger: logger)
26
+ end
27
+
28
+ describe 'unknown error response' do
29
+ before do
30
+ allow(response).to receive_messages(code: 408)
31
+ end
32
+
33
+ it 'marks the notification as failed because no successful delivery was made' do
34
+ allow(response).to receive_messages(body: JSON.dump('reason' => 'InvalidData'))
35
+ error = Rpush::DeliveryError.new(408, notification.id, 'Request Timeout')
36
+ expect(delivery).to receive(:mark_failed).with(error)
37
+ expect { perform }.to raise_error(Rpush::DeliveryError)
38
+ end
39
+ end
40
+
41
+ describe 'a 200 (Ok) response' do
42
+ before do
43
+ allow(response).to receive_messages(code: 200)
44
+ end
45
+
46
+ it 'marks the notification as delivered if delivered successfully to all devices' do
47
+ allow(response).to receive_messages(body: JSON.dump('registrationID' => 'xyz'))
48
+ expect(delivery).to receive(:mark_delivered)
49
+ perform
50
+ end
51
+
52
+ it 'logs that the notification was delivered' do
53
+ allow(response).to receive_messages(body: JSON.dump('registrationID' => 'xyz'))
54
+ expect(logger).to receive(:info).with("[MyApp] #{notification.id} sent to xyz")
55
+ perform
56
+ end
57
+
58
+ it 'reflects on canonical IDs' do
59
+ allow(response).to receive_messages(body: JSON.dump('registrationID' => 'canonical123'))
60
+ allow(notification).to receive_messages(registration_ids: ['1'])
61
+ expect(delivery).to receive(:reflect).with(:adm_canonical_id, '1', 'canonical123')
62
+ perform
63
+ end
64
+ end
65
+
66
+ describe 'a 400 (Bad Request) response' do
67
+ before do
68
+ allow(response).to receive_messages(code: 400)
69
+ end
70
+
71
+ it 'marks the notification as failed because no successful delivery was made' do
72
+ allow(response).to receive_messages(body: JSON.dump('reason' => 'InvalidData'))
73
+ error = Rpush::DeliveryError.new(nil, notification.id, 'Failed to deliver to all recipients.')
74
+ expect(delivery).to receive(:mark_failed).with(error)
75
+ expect { perform }.to raise_error(error)
76
+ end
77
+
78
+ it 'logs that the notification was not delivered' do
79
+ allow(response).to receive_messages(body: JSON.dump('reason' => 'InvalidRegistrationId'))
80
+ expect(logger).to receive(:warn).with("[MyApp] bad_request: xyz (InvalidRegistrationId)")
81
+ expect { perform }.to raise_error(Rpush::DeliveryError)
82
+ end
83
+
84
+ it 'reflects' do
85
+ allow(response).to receive_messages(body: JSON.dump('registrationID' => 'canonical123', 'reason' => 'Unregistered'))
86
+ allow(notification).to receive_messages(registration_ids: ['1'])
87
+ expect(delivery).to receive(:reflect).with(:adm_failed_to_recipient, notification, '1', 'Unregistered')
88
+ expect { perform }.to raise_error(Rpush::DeliveryError)
89
+ end
90
+ end
91
+
92
+ describe 'a 401 (Unauthorized) response' do
93
+ let(:http) { double(shutdown: nil) }
94
+ let(:token_response) { double(code: 200, header: {}, body: JSON.dump('access_token' => 'ACCESS_TOKEN', 'expires_in' => 60)) }
95
+
96
+ before do
97
+ allow(response).to receive_messages(code: 401, header: { 'retry-after' => 10 })
98
+
99
+ # first request to deliver message that returns unauthorized response
100
+ adm_uri = URI.parse(format(Rpush::Daemon::Adm::Delivery::AMAZON_ADM_URL, notification.registration_ids.first))
101
+ expect(http).to receive(:request).with(adm_uri, instance_of(Net::HTTP::Post)).and_return(response)
102
+ end
103
+
104
+ it 'should retrieve a new access token and mark the notification for retry' do
105
+ # request for access token
106
+ expect(http).to receive(:request).with(Rpush::Daemon::Adm::Delivery::AMAZON_TOKEN_URI, instance_of(Net::HTTP::Post)).and_return(token_response)
107
+
108
+ expect(store).to receive(:update_app).with(notification.app)
109
+ expect(delivery).to receive(:mark_retryable).with(notification, now)
110
+
111
+ perform
112
+ end
113
+
114
+ it 'should update the app with the new access token' do
115
+ # request for access token
116
+ expect(http).to receive(:request).with(Rpush::Daemon::Adm::Delivery::AMAZON_TOKEN_URI, instance_of(Net::HTTP::Post)).and_return(token_response)
117
+
118
+ expect(store).to receive(:update_app) do |app|
119
+ expect(app.access_token).to eq 'ACCESS_TOKEN'
120
+ expect(app.access_token_expiration).to eq now + 60.seconds
121
+ end
122
+ expect(delivery).to receive(:mark_retryable).with(notification, now)
123
+
124
+ perform
125
+ end
126
+
127
+ it 'should log the error and stop retrying if new access token can\'t be retrieved' do
128
+ allow(token_response).to receive_messages(code: 404, body: "test")
129
+ # request for access token
130
+ expect(http).to receive(:request).with(Rpush::Daemon::Adm::Delivery::AMAZON_TOKEN_URI, instance_of(Net::HTTP::Post)).and_return(token_response)
131
+
132
+ expect(store).not_to receive(:update_app).with(notification.app)
133
+ expect(delivery).not_to receive(:mark_retryable)
134
+
135
+ expect(logger).to receive(:warn).with("[MyApp] Could not retrieve access token from ADM: test")
136
+
137
+ perform
138
+ end
139
+ end
140
+
141
+ describe 'a 429 (Too Many Request) response' do
142
+ let(:http) { double(shutdown: nil) }
143
+ let(:notification) { Rpush::Adm::Notification.create!(app: app, registration_ids: %w(abc xyz), deliver_after: Time.now, collapse_key: 'sync', data: { 'message' => 'test' }) }
144
+ let(:rate_limited_response) { double(code: 429, header: { 'retry-after' => 3600 }) }
145
+
146
+ it 'should retry the entire notification respecting the Retry-After header if none sent out yet' do
147
+ allow(response).to receive_messages(code: 429, header: { 'retry-after' => 3600 })
148
+
149
+ # first request to deliver message that returns too many request response
150
+ adm_uri = URI.parse(format(Rpush::Daemon::Adm::Delivery::AMAZON_ADM_URL, notification.registration_ids.first))
151
+ expect(http).to receive(:request).with(adm_uri, instance_of(Net::HTTP::Post)).and_return(response)
152
+
153
+ expect(delivery).to receive(:mark_retryable).with(notification, now + 1.hour)
154
+ perform
155
+ end
156
+
157
+ it 'should retry the entire notification using exponential backoff' do
158
+ allow(response).to receive_messages(code: 429, header: {})
159
+
160
+ # first request to deliver message that returns too many request response
161
+ adm_uri = URI.parse(format(Rpush::Daemon::Adm::Delivery::AMAZON_ADM_URL, notification.registration_ids.first))
162
+ expect(http).to receive(:request).with(adm_uri, instance_of(Net::HTTP::Post)).and_return(response)
163
+
164
+ expect(delivery).to receive(:mark_retryable).with(notification, Time.now + 2**(notification.retries + 1))
165
+ perform
166
+ end
167
+
168
+ it 'should keep sent reg ids in original notification and create new notification with remaining reg ids for retry' do
169
+ allow(response).to receive_messages(code: 200, body: JSON.dump('registrationID' => 'abc'))
170
+
171
+ # first request to deliver message succeeds
172
+ adm_uri = URI.parse(format(Rpush::Daemon::Adm::Delivery::AMAZON_ADM_URL, 'abc'))
173
+ expect(http).to receive(:request).with(adm_uri, instance_of(Net::HTTP::Post)).and_return(response)
174
+
175
+ # first request to deliver message that returns too many request response
176
+ adm_uri = URI.parse(format(Rpush::Daemon::Adm::Delivery::AMAZON_ADM_URL, 'xyz'))
177
+ expect(http).to receive(:request).with(adm_uri, instance_of(Net::HTTP::Post)).and_return(rate_limited_response)
178
+
179
+ expect(store).to receive(:update_notification) do |notif|
180
+ expect(notif.registration_ids).to include('abc')
181
+ expect(notif.registration_ids).to_not include('xyz')
182
+ end
183
+
184
+ expect(store).to receive(:create_adm_notification) do |attrs, _notification_data, reg_ids, deliver_after, notification_app|
185
+ expect(attrs.keys).to include('collapse_key')
186
+ expect(attrs.keys).to include('delay_while_idle')
187
+ expect(attrs.keys).to include('app_id')
188
+
189
+ expect(reg_ids).to eq ['xyz']
190
+ expect(deliver_after).to eq now + 1.hour
191
+ expect(notification_app).to eq notification.app
192
+ end
193
+
194
+ expect(delivery).to receive(:mark_delivered)
195
+
196
+ perform
197
+ end
198
+ end
199
+
200
+ describe 'a 500 (Internal Server Error) response' do
201
+ before do
202
+ allow(response).to receive_messages(code: 500)
203
+ end
204
+
205
+ it 'marks the notification as failed because no successful delivery was made' do
206
+ error = Rpush::DeliveryError.new(nil, notification.id, 'Failed to deliver to all recipients.')
207
+ expect(delivery).to receive(:mark_failed).with(error)
208
+ expect { perform }.to raise_error(Rpush::DeliveryError)
209
+ end
210
+
211
+ it 'logs that the notification was not delivered' do
212
+ expect(logger).to receive(:warn).with("[MyApp] internal_server_error: xyz (Internal Server Error)")
213
+ expect { perform }.to raise_error(Rpush::DeliveryError)
214
+ end
215
+ end
216
+
217
+ describe 'a 503 (Service Unavailable) response' do
218
+ before do
219
+ allow(response).to receive_messages(code: 503, header: { 'retry-after' => 10 })
220
+ end
221
+
222
+ it 'should retry the notification respecting the Retry-After header' do
223
+ expect(delivery).to receive(:mark_retryable).with(notification, now + 10.seconds)
224
+ perform
225
+ end
226
+ end
227
+
228
+ describe 'some registration ids succeeding and some failing' do
229
+ let(:http) { double(shutdown: nil) }
230
+ let(:notification) { Rpush::Adm::Notification.create!(app: app, registration_ids: %w(abc xyz), deliver_after: Time.now, collapse_key: 'sync', data: { 'message' => 'test' }) }
231
+ let(:bad_request_response) { double(code: 400, body: JSON.dump('reason' => 'InvalidData')) }
232
+
233
+ it 'should keep sent reg ids in original notification and create new notification with remaining reg ids for retry' do
234
+ allow(response).to receive_messages(code: 200, body: JSON.dump('registrationID' => 'abc'))
235
+
236
+ # first request to deliver message succeeds
237
+ adm_uri = URI.parse(format(Rpush::Daemon::Adm::Delivery::AMAZON_ADM_URL, 'abc'))
238
+ expect(http).to receive(:request).with(adm_uri, instance_of(Net::HTTP::Post)).and_return(response)
239
+
240
+ # first request to deliver message that returns too many request response
241
+ adm_uri = URI.parse(format(Rpush::Daemon::Adm::Delivery::AMAZON_ADM_URL, 'xyz'))
242
+ expect(http).to receive(:request).with(adm_uri, instance_of(Net::HTTP::Post)).and_return(bad_request_response)
243
+
244
+ expect(store).to receive(:update_notification) do |notif|
245
+ expect(notif.error_description).to eq "Failed to deliver to recipients: \nxyz: InvalidData"
246
+ end
247
+
248
+ expect(delivery).to receive(:mark_delivered)
249
+
250
+ perform
251
+ end
252
+ end
253
+ end
@@ -0,0 +1,11 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::CertificateExpiredError do
4
+ let(:app) { double(name: 'test') }
5
+ let(:error) { Rpush::CertificateExpiredError.new(app, Time.now) }
6
+
7
+ it 'returns a message' do
8
+ error.message
9
+ error.to_s
10
+ end
11
+ end
@@ -0,0 +1,108 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Daemon::Apns::Delivery do
4
+ let(:app) { double(name: 'MyApp') }
5
+ let(:notification1) { double.as_null_object }
6
+ let(:notification2) { double.as_null_object }
7
+ let(:batch) { double(mark_all_failed: nil, mark_all_delivered: nil, all_processed: nil) }
8
+ let(:logger) { double(error: nil, info: nil) }
9
+ let(:connection) { double(select: false, write: nil, reconnect: nil, close: nil, connect: nil) }
10
+ let(:delivery) { Rpush::Daemon::Apns::Delivery.new(app, connection, batch) }
11
+
12
+ before do
13
+ allow(batch).to receive(:each_notification) do |&blk|
14
+ [notification1, notification2].each(&blk)
15
+ end
16
+ allow(Rpush).to receive_messages(logger: logger)
17
+ end
18
+
19
+ it 'writes the binary batch' do
20
+ allow(notification1).to receive_messages(to_binary: 'binary1')
21
+ allow(notification2).to receive_messages(to_binary: 'binary2')
22
+ expect(connection).to receive(:write).with('binary1binary2')
23
+ delivery.perform
24
+ end
25
+
26
+ it 'logs the notification deliveries' do
27
+ allow(notification1).to receive_messages(id: 666, device_token: 'abc123')
28
+ allow(notification2).to receive_messages(id: 42, device_token: 'abc456')
29
+ expect(logger).to receive(:info).with('[MyApp] 666 sent to abc123')
30
+ expect(logger).to receive(:info).with('[MyApp] 42 sent to abc456')
31
+ delivery.perform
32
+ end
33
+
34
+ it 'marks all notifications as delivered' do
35
+ expect(delivery).to receive(:mark_batch_delivered)
36
+ delivery.perform
37
+ end
38
+
39
+ it 'notifies the batch all notifications have been processed' do
40
+ expect(batch).to receive(:all_processed)
41
+ delivery.perform
42
+ end
43
+
44
+ describe 'when an error is raised' do
45
+ it 'marks all notifications as failed' do
46
+ error = StandardError.new
47
+ allow(connection).to receive(:write).and_raise(error)
48
+ expect(delivery).to receive(:mark_batch_failed).with(error)
49
+ expect { delivery.perform }.to raise_error(error)
50
+ end
51
+ end
52
+
53
+ # describe "when delivery fails" do
54
+ # before { connection.stub(select: true, read: [8, 4, 69].pack("ccN")) }
55
+ #
56
+ # it "marks the notification as failed" do
57
+ # delivery.should_receive(:mark_failed).with(4, "Unable to deliver notification 69, received error 4 (Missing payload)")
58
+ # perform
59
+ # end
60
+ #
61
+ # it "logs the delivery error" do
62
+ # # checking for the doublebed error doesn't work in jruby, but checking
63
+ # # for the exception by class does.
64
+ #
65
+ # # error = Rpush::DeliveryError.new(4, 12, "Missing payload")
66
+ # # Rpush::DeliveryError.stub(new: error)
67
+ # # expect { delivery.perform }.to raise_error(error)
68
+ #
69
+ # expect { delivery.perform }.to raise_error(Rpush::DeliveryError)
70
+ # end
71
+ #
72
+ # it "reads 6 bytes from the socket" do
73
+ # connection.should_receive(:read).with(6).and_return(nil)
74
+ # perform
75
+ # end
76
+ #
77
+ # it "does not attempt to read from the socket if the socket was not selected for reading after the timeout" do
78
+ # connection.stub(select: nil)
79
+ # connection.should_not_receive(:read)
80
+ # perform
81
+ # end
82
+ #
83
+ # it "reconnects the socket" do
84
+ # connection.should_receive(:reconnect)
85
+ # perform
86
+ # end
87
+ #
88
+ # it "logs that the connection is being reconnected" do
89
+ # Rpush.logger.should_receive(:error).with("[MyApp] Error received, reconnecting...")
90
+ # perform
91
+ # end
92
+ #
93
+ # context "when the APNs disconnects without returning an error" do
94
+ # before do
95
+ # connection.stub(read: nil)
96
+ # end
97
+ #
98
+ # it 'raises a DisconnectError error if the connection is closed without an error being returned' do
99
+ # expect { delivery.perform }.to raise_error(Rpush::DisconnectionError)
100
+ # end
101
+ #
102
+ # it 'marks the notification as failed' do
103
+ # delivery.should_receive(:mark_failed).with(nil, "The APNs disconnected without returning an error. This may indicate you are using an invalid certificate for the host.")
104
+ # perform
105
+ # end
106
+ # end
107
+ # end
108
+ end
@@ -0,0 +1,119 @@
1
+ require 'unit_spec_helper'
2
+ require 'rpush/daemon/store/active_record'
3
+
4
+ describe Rpush::Daemon::Apns::FeedbackReceiver, 'check_for_feedback' do
5
+ let(:host) { 'feedback.push.apple.com' }
6
+ let(:port) { 2196 }
7
+ let(:frequency) { 60 }
8
+ let(:certificate) { double }
9
+ let(:password) { double }
10
+ let(:app) { double(name: 'my_app', password: password, certificate: certificate, environment: 'production') }
11
+ let(:connection) { double(connect: nil, read: nil, close: nil) }
12
+ let(:logger) { double(error: nil, info: nil) }
13
+ let(:receiver) { Rpush::Daemon::Apns::FeedbackReceiver.new(app) }
14
+ let(:feedback) { double }
15
+ let(:sleeper) { double(Rpush::Daemon::InterruptibleSleep, sleep: nil, stop: nil) }
16
+ let(:store) { double(Rpush::Daemon::Store::ActiveRecord, create_apns_feedback: feedback, release_connection: nil) }
17
+
18
+ before do
19
+ Rpush.config.apns.feedback_receiver.frequency = frequency
20
+ allow(Rpush::Daemon::InterruptibleSleep).to receive_messages(new: sleeper)
21
+ allow(Rpush).to receive_messages(logger: logger)
22
+ allow(Rpush::Daemon::TcpConnection).to receive_messages(new: connection)
23
+ receiver.instance_variable_set("@stop", false)
24
+ allow(Rpush::Daemon).to receive_messages(store: store)
25
+ end
26
+
27
+ def double_connection_read_with_tuple
28
+ def connection.read(*)
29
+ unless @called
30
+ @called = true
31
+ "N\xE3\x84\r\x00 \x83OxfU\xEB\x9F\x84aJ\x05\xAD}\x00\xAF1\xE5\xCF\xE9:\xC3\xEA\a\x8F\x1D\xA4M*N\xB0\xCE\x17"
32
+ end
33
+ end
34
+ end
35
+
36
+ it 'initializes the sleeper with the feedback polling frequency' do
37
+ expect(Rpush::Daemon::InterruptibleSleep).to receive_messages(new: sleeper)
38
+ Rpush::Daemon::Apns::FeedbackReceiver.new(app)
39
+ end
40
+
41
+ it 'instantiates a new connection' do
42
+ expect(Rpush::Daemon::TcpConnection).to receive(:new).with(app, host, port)
43
+ receiver.check_for_feedback
44
+ end
45
+
46
+ it 'connects to the feeback service' do
47
+ expect(connection).to receive(:connect)
48
+ receiver.check_for_feedback
49
+ end
50
+
51
+ it 'closes the connection' do
52
+ expect(connection).to receive(:close)
53
+ receiver.check_for_feedback
54
+ end
55
+
56
+ it 'reads from the connection' do
57
+ expect(connection).to receive(:read).with(38)
58
+ receiver.check_for_feedback
59
+ end
60
+
61
+ it 'logs the feedback' do
62
+ double_connection_read_with_tuple
63
+ expect(Rpush.logger).to receive(:info).with("[my_app] [FeedbackReceiver] Delivery failed at 2011-12-10 16:08:45 UTC for 834f786655eb9f84614a05ad7d00af31e5cfe93ac3ea078f1da44d2a4eb0ce17.")
64
+ receiver.check_for_feedback
65
+ end
66
+
67
+ it 'creates the feedback' do
68
+ expect(Rpush::Daemon.store).to receive(:create_apns_feedback).with(Time.at(1_323_533_325), '834f786655eb9f84614a05ad7d00af31e5cfe93ac3ea078f1da44d2a4eb0ce17', app)
69
+ double_connection_read_with_tuple
70
+ receiver.check_for_feedback
71
+ end
72
+
73
+ it 'logs errors' do
74
+ error = StandardError.new('bork!')
75
+ allow(connection).to receive(:read).and_raise(error)
76
+ expect(Rpush.logger).to receive(:error).with(error)
77
+ receiver.check_for_feedback
78
+ end
79
+
80
+ describe 'start' do
81
+ before do
82
+ allow(Thread).to receive(:new).and_yield
83
+ allow(receiver).to receive(:loop).and_yield
84
+ end
85
+
86
+ it 'sleeps' do
87
+ allow(receiver).to receive(:check_for_feedback)
88
+ expect(sleeper).to receive(:sleep).at_least(:once)
89
+ receiver.start
90
+ end
91
+
92
+ it 'checks for feedback when started' do
93
+ expect(receiver).to receive(:check_for_feedback).at_least(:once)
94
+ receiver.start
95
+ end
96
+ end
97
+
98
+ describe 'stop' do
99
+ it 'interrupts sleep when stopped' do
100
+ allow(receiver).to receive(:check_for_feedback)
101
+ expect(sleeper).to receive(:stop)
102
+ receiver.stop
103
+ end
104
+
105
+ it 'releases the store connection' do
106
+ allow(Thread).to receive(:new).and_yield
107
+ allow(receiver).to receive(:loop).and_yield
108
+ expect(Rpush::Daemon.store).to receive(:release_connection)
109
+ receiver.start
110
+ receiver.stop
111
+ end
112
+ end
113
+
114
+ it 'reflects feedback was received' do
115
+ double_connection_read_with_tuple
116
+ expect(receiver).to receive(:reflect).with(:apns_feedback, feedback)
117
+ receiver.check_for_feedback
118
+ end
119
+ end