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,387 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Daemon::Gcm::Delivery do
4
+ let(:app) { Rpush::Gcm::App.create!(name: 'MyApp', auth_key: 'abc123') }
5
+ let(:notification) { Rpush::Gcm::Notification.create!(app: app, registration_ids: ['xyz'], deliver_after: Time.now) }
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::Gcm::Delivery.new(app, http, notification, batch) }
12
+ let(:store) { double(create_gcm_notification: double(id: 2)) }
13
+
14
+ def perform
15
+ delivery.perform
16
+ end
17
+
18
+ def perform_with_rescue
19
+ expect { perform }.to raise_error(StandardError)
20
+ end
21
+
22
+ before do
23
+ allow(delivery).to receive_messages(reflect: nil)
24
+ allow(Rpush::Daemon).to receive_messages(store: store)
25
+ allow(Time).to receive_messages(now: now)
26
+ allow(Rpush).to receive_messages(logger: logger)
27
+ end
28
+
29
+ shared_examples_for 'a notification with some delivery failures' do
30
+ let(:new_notification) { Rpush::Gcm::Notification.where('id != ?', notification.id).first }
31
+
32
+ before { allow(response).to receive_messages(body: JSON.dump(body)) }
33
+
34
+ it 'marks the original notification as failed' do
35
+ # error = Rpush::DeliveryError.new(nil, notification.id, error_description)
36
+ expect(delivery).to receive(:mark_failed) do |error|
37
+ expect(error.to_s).to match(error_description)
38
+ end
39
+ perform_with_rescue
40
+ end
41
+
42
+ it 'creates a new notification for the unavailable devices' do
43
+ notification.update_attributes(registration_ids: %w(id_0 id_1 id_2), data: { 'one' => 1 }, collapse_key: 'thing', delay_while_idle: true)
44
+ allow(response).to receive_messages(header: { 'retry-after' => 10 })
45
+ attrs = { 'collapse_key' => 'thing', 'delay_while_idle' => true, 'app_id' => app.id }
46
+ expect(store).to receive(:create_gcm_notification).with(attrs, notification.data,
47
+ %w(id_0 id_2), now + 10.seconds, notification.app)
48
+ perform_with_rescue
49
+ end
50
+
51
+ it 'raises a DeliveryError' do
52
+ expect { perform }.to raise_error(Rpush::DeliveryError)
53
+ end
54
+ end
55
+
56
+ describe 'a 200 response' do
57
+ before do
58
+ allow(response).to receive_messages(code: 200)
59
+ end
60
+
61
+ it 'reflects on any IDs which successfully received the notification' do
62
+ body = {
63
+ 'failure' => 1,
64
+ 'success' => 1,
65
+ 'results' => [
66
+ { 'message_id' => '1:000' },
67
+ { 'error' => 'Err' }
68
+ ]
69
+ }
70
+
71
+ allow(response).to receive_messages(body: JSON.dump(body))
72
+ allow(notification).to receive_messages(registration_ids: %w(1 2))
73
+ expect(delivery).to receive(:reflect).with(:gcm_delivered_to_recipient, notification, '1')
74
+ expect(delivery).not_to receive(:reflect).with(:gcm_delivered_to_recipient, notification, '2')
75
+ perform_with_rescue
76
+ end
77
+
78
+ it 'reflects on any IDs which failed to receive the notification' do
79
+ body = {
80
+ 'failure' => 1,
81
+ 'success' => 1,
82
+ 'results' => [
83
+ { 'error' => 'Err' },
84
+ { 'message_id' => '1:000' }
85
+ ]
86
+ }
87
+
88
+ allow(response).to receive_messages(body: JSON.dump(body))
89
+ allow(notification).to receive_messages(registration_ids: %w(1 2))
90
+ expect(delivery).to receive(:reflect).with(:gcm_failed_to_recipient, notification, 'Err', '1')
91
+ expect(delivery).not_to receive(:reflect).with(:gcm_failed_to_recipient, notification, anything, '2')
92
+ perform_with_rescue
93
+ end
94
+
95
+ it 'reflects on canonical IDs' do
96
+ body = {
97
+ 'failure' => 0,
98
+ 'success' => 3,
99
+ 'canonical_ids' => 1,
100
+ 'results' => [
101
+ { 'message_id' => '1:000' },
102
+ { 'message_id' => '1:000', 'registration_id' => 'canonical123' },
103
+ { 'message_id' => '1:000' }
104
+ ] }
105
+
106
+ allow(response).to receive_messages(body: JSON.dump(body))
107
+ allow(notification).to receive_messages(registration_ids: %w(1 2 3))
108
+ expect(delivery).to receive(:reflect).with(:gcm_canonical_id, '2', 'canonical123')
109
+ perform
110
+ end
111
+
112
+ it 'reflects on invalid IDs' do
113
+ body = {
114
+ 'failure' => 1,
115
+ 'success' => 2,
116
+ 'canonical_ids' => 0,
117
+ 'results' => [
118
+ { 'message_id' => '1:000' },
119
+ { 'error' => 'NotRegistered' },
120
+ { 'message_id' => '1:000' }
121
+ ]
122
+ }
123
+
124
+ allow(response).to receive_messages(body: JSON.dump(body))
125
+ allow(notification).to receive_messages(registration_ids: %w(1 2 3))
126
+ expect(delivery).to receive(:reflect).with(:gcm_invalid_registration_id, app, 'NotRegistered', '2')
127
+ perform_with_rescue
128
+ end
129
+
130
+ describe 'when delivered successfully to all devices' do
131
+ let(:body) do
132
+ {
133
+ 'failure' => 0,
134
+ 'success' => 1,
135
+ 'results' => [{ 'message_id' => '1:000' }]
136
+ }
137
+ end
138
+
139
+ before { allow(response).to receive_messages(body: JSON.dump(body)) }
140
+
141
+ it 'marks the notification as delivered' do
142
+ expect(delivery).to receive(:mark_delivered)
143
+ perform
144
+ end
145
+
146
+ it 'logs that the notification was delivered' do
147
+ expect(logger).to receive(:info).with("[MyApp] #{notification.id} sent to xyz")
148
+ perform
149
+ end
150
+ end
151
+
152
+ it 'marks a notification as failed if any ids are invalid' do
153
+ body = {
154
+ 'failure' => 1,
155
+ 'success' => 2,
156
+ 'canonical_ids' => 0,
157
+ 'results' => [
158
+ { 'message_id' => '1:000' },
159
+ { 'error' => 'NotRegistered' },
160
+ { 'message_id' => '1:000' }
161
+ ]
162
+ }
163
+
164
+ allow(response).to receive_messages(body: JSON.dump(body))
165
+ expect(delivery).to receive(:mark_failed)
166
+ expect(delivery).not_to receive(:mark_retryable)
167
+ expect(store).not_to receive(:create_gcm_notification)
168
+ perform_with_rescue
169
+ end
170
+
171
+ it 'marks a notification as failed if any deliveries failed that cannot be retried' do
172
+ body = {
173
+ 'failure' => 1,
174
+ 'success' => 1,
175
+ 'results' => [
176
+ { 'message_id' => '1:000' },
177
+ { 'error' => 'InvalidDataKey' }
178
+ ] }
179
+ allow(response).to receive_messages(body: JSON.dump(body))
180
+ error = Rpush::DeliveryError.new(nil, notification.id, 'Failed to deliver to all recipients. Errors: InvalidDataKey.')
181
+ expect(delivery).to receive(:mark_failed).with(error)
182
+ perform_with_rescue
183
+ end
184
+
185
+ describe 'all deliveries failed with Unavailable or InternalServerError' do
186
+ let(:body) do
187
+ {
188
+ 'failure' => 2,
189
+ 'success' => 0,
190
+ 'results' => [
191
+ { 'error' => 'Unavailable' },
192
+ { 'error' => 'Unavailable' }
193
+ ]
194
+ }
195
+ end
196
+
197
+ before do
198
+ allow(response).to receive_messages(body: JSON.dump(body))
199
+ allow(notification).to receive_messages(registration_ids: %w(1 2))
200
+ end
201
+
202
+ it 'retries the notification respecting the Retry-After header' do
203
+ allow(response).to receive_messages(header: { 'retry-after' => 10 })
204
+ expect(delivery).to receive(:mark_retryable).with(notification, now + 10.seconds)
205
+ perform
206
+ end
207
+
208
+ it 'retries the notification using exponential back-off if the Retry-After header is not present' do
209
+ expect(delivery).to receive(:mark_retryable).with(notification, now + 2)
210
+ perform
211
+ end
212
+
213
+ it 'does not mark the notification as failed' do
214
+ expect(delivery).not_to receive(:mark_failed)
215
+ perform
216
+ end
217
+
218
+ it 'logs that the notification will be retried' do
219
+ notification.retries = 1
220
+ notification.deliver_after = now + 2
221
+ expect(Rpush.logger).to receive(:warn).with("[MyApp] All recipients unavailable. Notification #{notification.id} will be retried after 2012-10-14 00:00:02 (retry 1).")
222
+ perform
223
+ end
224
+ end
225
+
226
+ describe 'all deliveries failed with some as Unavailable or InternalServerError' do
227
+ let(:body) do
228
+ { 'failure' => 3,
229
+ 'success' => 0,
230
+ 'results' => [
231
+ { 'error' => 'Unavailable' },
232
+ { 'error' => 'InvalidDataKey' },
233
+ { 'error' => 'Unavailable' }
234
+ ]
235
+ }
236
+ end
237
+ let(:error_description) { /#{Regexp.escape("Failed to deliver to recipients 0, 1, 2. Errors: Unavailable, InvalidDataKey, Unavailable. 0, 2 will be retried as notification")} [\d]+\./ }
238
+ it_should_behave_like 'a notification with some delivery failures'
239
+ end
240
+
241
+ describe 'some deliveries failed with Unavailable or InternalServerError' do
242
+ let(:body) do
243
+ { 'failure' => 2,
244
+ 'success' => 1,
245
+ 'results' => [
246
+ { 'error' => 'Unavailable' },
247
+ { 'message_id' => '1:000' },
248
+ { 'error' => 'InternalServerError' }
249
+ ]
250
+ }
251
+ end
252
+ let(:error_description) { /#{Regexp.escape("Failed to deliver to recipients 0, 2. Errors: Unavailable, InternalServerError. 0, 2 will be retried as notification")} [\d]+\./ }
253
+ it_should_behave_like 'a notification with some delivery failures'
254
+ end
255
+ end
256
+
257
+ describe 'a 503 response' do
258
+ before { allow(response).to receive_messages(code: 503) }
259
+
260
+ it 'logs a warning that the notification will be retried.' do
261
+ notification.retries = 1
262
+ notification.deliver_after = now + 2
263
+ expect(logger).to receive(:warn).with("[MyApp] GCM responded with an Service Unavailable Error. Notification #{notification.id} will be retried after 2012-10-14 00:00:02 (retry 1).")
264
+ perform
265
+ end
266
+
267
+ it 'respects an integer Retry-After header' do
268
+ allow(response).to receive_messages(header: { 'retry-after' => 10 })
269
+ expect(delivery).to receive(:mark_retryable).with(notification, now + 10.seconds)
270
+ perform
271
+ end
272
+
273
+ it 'respects a HTTP-date Retry-After header' do
274
+ allow(response).to receive_messages(header: { 'retry-after' => 'Wed, 03 Oct 2012 20:55:11 GMT' })
275
+ expect(delivery).to receive(:mark_retryable).with(notification, Time.parse('Wed, 03 Oct 2012 20:55:11 GMT'))
276
+ perform
277
+ end
278
+
279
+ it 'defaults to exponential back-off if the Retry-After header is not present' do
280
+ expect(delivery).to receive(:mark_retryable).with(notification, now + 2**1)
281
+ perform
282
+ end
283
+ end
284
+
285
+ describe 'a 502 response' do
286
+ before { allow(response).to receive_messages(code: 502) }
287
+
288
+ it 'logs a warning that the notification will be retried.' do
289
+ notification.retries = 1
290
+ notification.deliver_after = now + 2
291
+ expect(logger).to receive(:warn).with("[MyApp] GCM responded with a Bad Gateway Error. Notification #{notification.id} will be retried after 2012-10-14 00:00:02 (retry 1).")
292
+ perform
293
+ end
294
+
295
+ it 'respects an integer Retry-After header' do
296
+ allow(response).to receive_messages(header: { 'retry-after' => 10 })
297
+ expect(delivery).to receive(:mark_retryable).with(notification, now + 10.seconds)
298
+ perform
299
+ end
300
+
301
+ it 'respects a HTTP-date Retry-After header' do
302
+ allow(response).to receive_messages(header: { 'retry-after' => 'Wed, 03 Oct 2012 20:55:11 GMT' })
303
+ expect(delivery).to receive(:mark_retryable).with(notification, Time.parse('Wed, 03 Oct 2012 20:55:11 GMT'))
304
+ perform
305
+ end
306
+
307
+ it 'defaults to exponential back-off if the Retry-After header is not present' do
308
+ expect(delivery).to receive(:mark_retryable).with(notification, now + 2**1)
309
+ perform
310
+ end
311
+ end
312
+
313
+ describe 'a 500 response' do
314
+ before do
315
+ notification.update_attribute(:retries, 2)
316
+ allow(response).to receive_messages(code: 500)
317
+ end
318
+
319
+ it 'logs a warning that the notification has been re-queued.' do
320
+ notification.retries = 3
321
+ notification.deliver_after = now + 2**3
322
+ expect(Rpush.logger).to receive(:warn).with("[MyApp] GCM responded with an Internal Error. Notification #{notification.id} will be retried after #{(now + 2**3).strftime('%Y-%m-%d %H:%M:%S')} (retry 3).")
323
+ perform
324
+ end
325
+
326
+ it 'retries the notification in accordance with the exponential back-off strategy.' do
327
+ expect(delivery).to receive(:mark_retryable).with(notification, now + 2**3)
328
+ perform
329
+ end
330
+ end
331
+
332
+ describe 'a 5xx response' do
333
+ before { allow(response).to receive_messages(code: 555) }
334
+
335
+ it 'logs a warning that the notification will be retried.' do
336
+ notification.retries = 1
337
+ notification.deliver_after = now + 2
338
+ expect(logger).to receive(:warn).with("[MyApp] GCM responded with a 5xx Error. Notification #{notification.id} will be retried after 2012-10-14 00:00:02 (retry 1).")
339
+ perform
340
+ end
341
+
342
+ it 'respects an integer Retry-After header' do
343
+ allow(response).to receive_messages(header: { 'retry-after' => 10 })
344
+ expect(delivery).to receive(:mark_retryable).with(notification, now + 10.seconds)
345
+ perform
346
+ end
347
+
348
+ it 'respects a HTTP-date Retry-After header' do
349
+ allow(response).to receive_messages(header: { 'retry-after' => 'Wed, 03 Oct 2012 20:55:11 GMT' })
350
+ expect(delivery).to receive(:mark_retryable).with(notification, Time.parse('Wed, 03 Oct 2012 20:55:11 GMT'))
351
+ perform
352
+ end
353
+
354
+ it 'defaults to exponential back-off if the Retry-After header is not present' do
355
+ expect(delivery).to receive(:mark_retryable).with(notification, now + 2**1)
356
+ perform
357
+ end
358
+ end
359
+
360
+ describe 'a 401 response' do
361
+ before { allow(response).to receive_messages(code: 401) }
362
+
363
+ it 'raises an error' do
364
+ expect { perform }.to raise_error(Rpush::DeliveryError)
365
+ end
366
+ end
367
+
368
+ describe 'a 400 response' do
369
+ before { allow(response).to receive_messages(code: 400) }
370
+
371
+ it 'marks the notification as failed' do
372
+ error = Rpush::DeliveryError.new(400, notification.id, 'GCM failed to parse the JSON request. Possibly an Rpush bug, please open an issue.')
373
+ expect(delivery).to receive(:mark_failed).with(error)
374
+ perform_with_rescue
375
+ end
376
+ end
377
+
378
+ describe 'an un-handled response' do
379
+ before { allow(response).to receive_messages(code: 418) }
380
+
381
+ it 'marks the notification as failed' do
382
+ error = Rpush::DeliveryError.new(418, notification.id, "I'm a Teapot")
383
+ expect(delivery).to receive(:mark_failed).with(error)
384
+ perform_with_rescue
385
+ end
386
+ end
387
+ end
@@ -0,0 +1,11 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Daemon::ProcTitle do
4
+ it 'sets the process title' do
5
+ Rpush.config.embedded = false
6
+ Rpush.config.push = false
7
+ allow(Rpush::Daemon::AppRunner).to receive_messages(total_dispatchers: 2, total_queued: 10)
8
+ expect(Process).to receive(:setproctitle).with('rpush | 10 queued | 2 dispatchers')
9
+ Rpush::Daemon::ProcTitle.update
10
+ end
11
+ end
@@ -0,0 +1,159 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Daemon::Pushy::Delivery do
4
+ let(:app) { Rpush::Pushy::App.create!(name: 'MyApp', api_key: 'my_api_key') }
5
+ let(:token) { 'device token' }
6
+ let(:data) { { message: 'some message' } }
7
+ let(:notification) { Rpush::Pushy::Notification.create!(app: app, registration_ids: [token], data: data) }
8
+ let(:batch) { instance_double('Rpush::Daemon::Batch', notification_processed: nil) }
9
+ let(:response) { instance_double('Net::HTTPResponse', code: response_code, header: response_header) }
10
+ let(:response_code) { 200 }
11
+ let(:response_header) { {} }
12
+ let(:http) { instance_double('Net::HTTP::Persistent', request: response) }
13
+ let(:logger) { instance_double('Rpush::Logger', error: nil, info: nil, warn: nil, internal_logger: nil) }
14
+ let(:now) { Time.parse('2018-01-19 00:00:00 UTC') }
15
+
16
+ before do
17
+ allow(Rpush).to receive_messages(logger: logger)
18
+ allow(Time).to receive_messages(now: now)
19
+ end
20
+
21
+ subject(:delivery) { described_class.new(app, http, notification, batch) }
22
+
23
+ describe '#pushy_uri' do
24
+ it { expect(subject.pushy_uri).to eq URI.parse('https://api.pushy.me/push?api_key=my_api_key') }
25
+ end
26
+
27
+ describe '#perform' do
28
+ shared_examples 'process notification' do
29
+ it 'invoke batch.notification_processed' do
30
+ subject.perform rescue nil
31
+ expect(batch).to have_received(:notification_processed)
32
+ end
33
+ end
34
+
35
+ context 'when response code is 200' do
36
+ let(:external_device_id) { '5a60ca7f6ad08477b5070dd3' }
37
+
38
+ before do
39
+ allow(batch).to receive(:mark_delivered)
40
+ response_body = {
41
+ success: true,
42
+ id: external_device_id
43
+ }
44
+ allow(response).to receive(:body) { response_body.to_json }
45
+ Rpush::Daemon.store = Rpush::Daemon::Store.const_get(Rpush.config.client.to_s.camelcase).new
46
+ end
47
+
48
+ it 'marks the notification as delivered' do
49
+ delivery.perform
50
+ expect(batch).to have_received(:mark_delivered).with(notification)
51
+ end
52
+
53
+ it { expect { delivery.perform }.to change { notification.external_device_id }.to(external_device_id) }
54
+
55
+ it 'logs than notification received an external id' do
56
+ delivery.perform
57
+ expected = "#{notification.id} received an external id=5a60ca7f6ad08477b5070dd3"
58
+ expect(logger).to have_received(:info).with(expected)
59
+ end
60
+
61
+ it_behaves_like 'process notification'
62
+ end
63
+
64
+ shared_examples 'retry delivery' do |response_code:|
65
+ let(:response_code) { response_code }
66
+
67
+ shared_examples 'logs' do |deliver_after:|
68
+ let(:expected_log_message) do
69
+ "Pushy responded with a #{response_code} error. Notification #{notification.id} " \
70
+ "will be retried after #{deliver_after} (retry 1)."
71
+ end
72
+
73
+ it 'logs that the notification will be retried' do
74
+ delivery.perform
75
+ expect(logger).to have_received(:warn).with(expected_log_message)
76
+ end
77
+ end
78
+
79
+ context 'when Retry-After header is present' do
80
+ let(:response_header) { { 'retry-after' => 10 } }
81
+
82
+ before do
83
+ allow(delivery).to receive(:mark_retryable) do
84
+ notification.deliver_after = now + 10.seconds
85
+ notification.retries += 1
86
+ end
87
+ end
88
+
89
+ it 'retry the notification' do
90
+ delivery.perform
91
+ expect(delivery).to have_received(:mark_retryable).with(notification, now + 10.seconds)
92
+ end
93
+
94
+ it_behaves_like 'logs', deliver_after: '2018-01-19 00:00:10'
95
+ it_behaves_like 'process notification'
96
+ end
97
+
98
+ context 'when Retry-After header is not present' do
99
+ before do
100
+ allow(delivery).to receive(:mark_retryable_exponential) do
101
+ notification.deliver_after = now + 2.seconds
102
+ notification.retries = 1
103
+ end
104
+ end
105
+
106
+ it 'retry the notification' do
107
+ delivery.perform
108
+ expect(delivery).to have_received(:mark_retryable_exponential).with(notification)
109
+ end
110
+
111
+ it_behaves_like 'logs', deliver_after: '2018-01-19 00:00:02'
112
+ it_behaves_like 'process notification'
113
+ end
114
+ end
115
+
116
+ it_behaves_like 'retry delivery', response_code: 429
117
+ it_behaves_like 'retry delivery', response_code: 500
118
+ it_behaves_like 'retry delivery', response_code: 502
119
+ it_behaves_like 'retry delivery', response_code: 503
120
+ it_behaves_like 'retry delivery', response_code: 504
121
+
122
+ context 'when delivery failed' do
123
+ let(:response_code) { 400 }
124
+ let(:fail_message) { 'No devices matched the specified condition.' }
125
+ before do
126
+ allow(response).to receive(:body) { { error: fail_message }.to_json }
127
+ allow(batch).to receive(:mark_failed)
128
+ end
129
+
130
+ it 'logs that the notifications failed' do
131
+ expect { delivery.perform }.to raise_error(Rpush::DeliveryError)
132
+ expect(logger).to have_received(:error).with("#{notification.id} failed: #{fail_message}")
133
+ end
134
+
135
+ it 'marks the notification as failed' do
136
+ expect { delivery.perform }.to raise_error(Rpush::DeliveryError)
137
+ expected_message = "Unable to deliver notification #{notification.id}, " \
138
+ "received error 400 (#{fail_message})"
139
+ expect(batch).to have_received(:mark_failed).with(notification, 400, expected_message)
140
+ end
141
+
142
+ it_behaves_like 'process notification'
143
+ end
144
+
145
+ context 'when SocketError raised' do
146
+ before do
147
+ allow(http).to receive(:request) { raise SocketError }
148
+ allow(delivery).to receive(:mark_retryable)
149
+ end
150
+
151
+ it 'retry delivery after 10 seconds' do
152
+ expect { delivery.perform }.to raise_error(SocketError)
153
+ expect(delivery).to have_received(:mark_retryable).with(notification, now + 10.seconds, SocketError)
154
+ end
155
+
156
+ it_behaves_like 'process notification'
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,14 @@
1
+ require "unit_spec_helper"
2
+
3
+ describe Rpush::RetryableError do
4
+ let(:response) { double(code: 401, header: { 'retry-after' => 3600 }) }
5
+ let(:error) { Rpush::RetryableError.new(401, 12, "Unauthorized", response) }
6
+
7
+ it "returns an informative message" do
8
+ expect(error.to_s).to eq "Retryable error for 12, received error 401 (Unauthorized) - retry after 3600"
9
+ end
10
+
11
+ it "returns the error code" do
12
+ expect(error.code).to eq 401
13
+ end
14
+ end
@@ -0,0 +1,36 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Daemon::ServiceConfigMethods do
4
+ module ServiceConfigMethodsSpec
5
+ extend Rpush::Daemon::ServiceConfigMethods
6
+ class Delivery; end
7
+ end
8
+
9
+ it 'returns the delivery class' do
10
+ expect(ServiceConfigMethodsSpec.delivery_class).to eq ServiceConfigMethodsSpec::Delivery
11
+ end
12
+
13
+ it 'instantiates loops' do
14
+ loop_class = Class.new
15
+ app = double
16
+ loop_instance = loop_class.new
17
+ expect(loop_class).to receive(:new).with(app).and_return(loop_instance)
18
+ ServiceConfigMethodsSpec.loops loop_class
19
+ expect(ServiceConfigMethodsSpec.loop_instances(app)).to eq [loop_instance]
20
+ end
21
+
22
+ it 'returns a new dispatcher' do
23
+ ServiceConfigMethodsSpec.dispatcher :http, an: :option
24
+ app = double
25
+ dispatcher = double
26
+ expect(Rpush::Daemon::Dispatcher::Http).to receive(:new).with(app, ServiceConfigMethodsSpec::Delivery, an: :option).and_return(dispatcher)
27
+ expect(ServiceConfigMethodsSpec.new_dispatcher(app)).to eq dispatcher
28
+ end
29
+
30
+ it 'raises a NotImplementedError for an unknown dispatcher type' do
31
+ expect do
32
+ ServiceConfigMethodsSpec.dispatcher :unknown
33
+ ServiceConfigMethodsSpec.dispatcher_class
34
+ end.to raise_error(NotImplementedError)
35
+ end
36
+ end