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,176 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Daemon::Wns::Delivery do
4
+ let(:app) { Rpush::Wns::App.create!(name: "MyApp", client_id: "someclient", client_secret: "somesecret", access_token: "access_token", access_token_expiration: Time.now + (60 * 10)) }
5
+ let(:notification) { Rpush::Wns::Notification.create!(app: app, data: { title: "MyApp", body: "Example notification", param: "/param1" }, uri: "http://some.example/", deliver_after: Time.now) }
6
+ let(:logger) { double(error: nil, info: nil, warn: nil) }
7
+ let(:response) { double(code: 200, header: {}, body: '') }
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::Wns::Delivery.new(app, http, notification, batch) }
12
+ let(:store) { double(create_wpns_notification: double(id: 2), update_app: nil) }
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 "an notification with some delivery faliures" do
30
+ let(:new_notification) { Rpush::Wns::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 falied" do
35
+ expect(delivery).to receive(:mark_failed) do |error|
36
+ expect(error.message).to match(error_description)
37
+ end
38
+ perform_with_rescue
39
+ end
40
+
41
+ it "raises a DeliveryError" do
42
+ expect { perform }.to raise_error(Rpush::DeliveryError)
43
+ end
44
+ end
45
+
46
+ describe "an 200 response without an access token" do
47
+ before do
48
+ allow(app).to receive_messages(access_token_expired?: true)
49
+ allow(response).to receive_messages(to_hash: {}, code: 200, body: JSON.dump(access_token: "dummy_access_token", expires_in: 60))
50
+ end
51
+
52
+ it 'set the access token for the app' do
53
+ expect(delivery).to receive(:update_access_token).with("access_token" => "dummy_access_token", "expires_in" => 60)
54
+ expect(store).to receive(:update_app).with app
55
+ perform
56
+ end
57
+
58
+ it 'uses the PostRequest factory for creating the request' do
59
+ expect(Rpush::Daemon::Wns::PostRequest).to receive(:create).with(notification, "dummy_access_token")
60
+ perform
61
+ end
62
+ end
63
+
64
+ describe "an 200 response with a valid access token" do
65
+ before do
66
+ allow(response).to receive_messages(code: 200)
67
+ end
68
+
69
+ it "marks the notification as delivered if delivered successfully to all devices" do
70
+ allow(response).to receive_messages(body: JSON.dump("failure" => 0))
71
+ allow(response).to receive_messages(to_hash: { "X-WNS-Status" => ["received"] })
72
+ expect(batch).to receive(:mark_delivered).with(notification)
73
+ perform
74
+ end
75
+
76
+ it "retries the notification when the queue is full" do
77
+ allow(response).to receive_messages(body: JSON.dump("failure" => 0))
78
+ allow(response).to receive_messages(to_hash: { "X-WNS-Status" => ["channelthrottled"] })
79
+ expect(batch).to receive(:mark_retryable).with(notification, Time.now + (60 * 10))
80
+ perform
81
+ end
82
+
83
+ it "marks the notification as failed if the notification is suppressed" do
84
+ allow(response).to receive_messages(body: JSON.dump("faliure" => 0))
85
+ allow(response).to receive_messages(to_hash: { "X-WNS-Status" => ["dropped"], "X-WNS-Error-Description" => "" })
86
+ error = Rpush::DeliveryError.new(200, notification.id, 'Notification was received but suppressed by the service ().')
87
+ expect(delivery).to receive(:mark_failed).with(error)
88
+ perform_with_rescue
89
+ end
90
+ end
91
+
92
+ describe "an 400 response" do
93
+ before { allow(response).to receive_messages(code: 400) }
94
+ it "marks notifications as failed" do
95
+ error = Rpush::DeliveryError.new(400, notification.id, 'One or more headers were specified incorrectly or conflict with another header.')
96
+ expect(delivery).to receive(:mark_failed).with(error)
97
+ perform_with_rescue
98
+ end
99
+ end
100
+
101
+ describe "an 404 response" do
102
+ before { allow(response).to receive_messages(code: 404) }
103
+ it "marks notifications as failed" do
104
+ error = Rpush::DeliveryError.new(404, notification.id, 'The channel URI is not valid or is not recognized by WNS.')
105
+ expect(delivery).to receive(:mark_failed).with(error)
106
+ perform_with_rescue
107
+ end
108
+ end
109
+
110
+ describe "an 405 response" do
111
+ before { allow(response).to receive_messages(code: 405) }
112
+ it "marks notifications as failed" do
113
+ error = Rpush::DeliveryError.new(405, notification.id, 'Invalid method (GET, CREATE); only POST (Windows or Windows Phone) or DELETE (Windows Phone only) is allowed.')
114
+ expect(delivery).to receive(:mark_failed).with(error)
115
+ perform_with_rescue
116
+ end
117
+ end
118
+
119
+ describe "an 406 response" do
120
+ before { allow(response).to receive_messages(code: 406) }
121
+
122
+ it "retries the notification" do
123
+ expect(batch).to receive(:mark_retryable).with(notification, Time.now + (60 * 60))
124
+ perform
125
+ end
126
+
127
+ it "logs a warning that the notification will be retried" do
128
+ notification.retries = 1
129
+ notification.deliver_after = now + 2
130
+ expect(logger).to receive(:warn).with("[MyApp] Per-day throttling limit reached. Notification #{notification.id} will be retried after 2012-10-14 00:00:02 (retry 1).")
131
+ perform
132
+ end
133
+ end
134
+
135
+ describe "an 412 response" do
136
+ before { allow(response).to receive_messages(code: 412) }
137
+
138
+ it "retries the notification" do
139
+ expect(batch).to receive(:mark_retryable).with(notification, Time.now + (60 * 60))
140
+ perform
141
+ end
142
+
143
+ it "logs a warning that the notification will be retried" do
144
+ notification.retries = 1
145
+ notification.deliver_after = now + 2
146
+ expect(logger).to receive(:warn).with("[MyApp] Device unreachable. Notification #{notification.id} will be retried after 2012-10-14 00:00:02 (retry 1).")
147
+ perform
148
+ end
149
+ end
150
+
151
+ describe "an 503 response" do
152
+ before { allow(response).to receive_messages(code: 503) }
153
+
154
+ it "retries the notification exponentially" do
155
+ expect(delivery).to receive(:mark_retryable_exponential).with(notification)
156
+ perform
157
+ end
158
+
159
+ it 'logs a warning that the notification will be retried.' do
160
+ notification.retries = 1
161
+ notification.deliver_after = now + 2
162
+ expect(logger).to receive(:warn).with("[MyApp] Service Unavailable. Notification #{notification.id} will be retried after 2012-10-14 00:00:02 (retry 1).")
163
+ perform
164
+ end
165
+ end
166
+
167
+ describe 'an un-handled response' do
168
+ before { allow(response).to receive_messages(code: 418) }
169
+
170
+ it 'marks the notification as failed' do
171
+ error = Rpush::DeliveryError.new(418, notification.id, "I'm a Teapot")
172
+ expect(delivery).to receive(:mark_failed).with(error)
173
+ perform_with_rescue
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,117 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Daemon::Wns::PostRequest do
4
+ let(:app) do
5
+ Rpush::Wns::App.create!(
6
+ name: "MyApp",
7
+ client_id: "someclient",
8
+ client_secret: "somesecret",
9
+ access_token: "access_token",
10
+ access_token_expiration: Time.now + (60 * 10)
11
+ )
12
+ end
13
+
14
+ context 'Notification' do
15
+ let(:notification) do
16
+ Rpush::Wns::Notification.create!(
17
+ app: app,
18
+ data: {
19
+ title: "MyApp",
20
+ body: "Example notification"
21
+ },
22
+ uri: "http://some.example/"
23
+ )
24
+ end
25
+
26
+ it 'creates a request characteristic for toast notification' do
27
+ request = Rpush::Daemon::Wns::PostRequest.create(notification, 'token')
28
+ expect(request['X-WNS-Type']).to eq('wns/toast')
29
+ expect(request['Content-Type']).to eq('text/xml')
30
+ expect(request.body).to include('<toast>')
31
+ expect(request.body).to include('MyApp')
32
+ expect(request.body).to include('Example notification')
33
+ end
34
+
35
+ context 'with launch' do
36
+ let(:notification) do
37
+ Rpush::Wns::Notification.create!(
38
+ app: app,
39
+ data: {
40
+ title: "MyApp",
41
+ body: "Example notification",
42
+ launch: "MyLaunchArgument"
43
+ },
44
+ uri: "http://some.example/"
45
+ )
46
+ end
47
+
48
+ it 'creates a request characteristic for toast notification with launch' do
49
+ request = Rpush::Daemon::Wns::PostRequest.create(notification, 'token')
50
+ expect(request['X-WNS-Type']).to eq('wns/toast')
51
+ expect(request['Content-Type']).to eq('text/xml')
52
+ expect(request.body).to include("<toast launch='MyLaunchArgument'>")
53
+ expect(request.body).to include('MyApp')
54
+ expect(request.body).to include('Example notification')
55
+ end
56
+ end
57
+
58
+ context 'with sound' do
59
+ let(:notification) do
60
+ Rpush::Wns::Notification.create!(
61
+ app: app,
62
+ data: {
63
+ title: "MyApp",
64
+ body: "Example notification"
65
+ },
66
+ uri: "http://some.example/",
67
+ sound: "ms-appx:///examplesound.wav"
68
+ )
69
+ end
70
+
71
+ it 'creates a request characteristic for toast notification' do
72
+ request = Rpush::Daemon::Wns::PostRequest.create(notification, 'token')
73
+ expect(request['X-WNS-Type']).to eq('wns/toast')
74
+ expect(request['Content-Type']).to eq('text/xml')
75
+ expect(request.body).to include('<toast>')
76
+ expect(request.body).to include('MyApp')
77
+ expect(request.body).to include('Example notification')
78
+ expect(request.body).to include("<audio src='ms-appx:///examplesound.wav'/>")
79
+ end
80
+ end
81
+ end
82
+
83
+ context 'RawNotification' do
84
+ let(:notification) do
85
+ Rpush::Wns::RawNotification.create!(
86
+ app: app,
87
+ data: { foo: 'foo', bar: 'bar' },
88
+ uri: "http://some.example/"
89
+ )
90
+ end
91
+
92
+ it 'creates a request characteristic for raw notification' do
93
+ request = Rpush::Daemon::Wns::PostRequest.create(notification, 'token')
94
+ expect(request['X-WNS-Type']).to eq('wns/raw')
95
+ expect(request['Content-Type']).to eq('application/octet-stream')
96
+ expect(request.body).to eq("{\"foo\":\"foo\",\"bar\":\"bar\"}")
97
+ end
98
+ end
99
+
100
+ context 'BadgeNotification' do
101
+ let(:notification) do
102
+ Rpush::Wns::BadgeNotification.create!(
103
+ app: app,
104
+ uri: "http://some.example/",
105
+ badge: 42
106
+ )
107
+ end
108
+
109
+ it 'creates a request characteristic for badge notification' do
110
+ request = Rpush::Daemon::Wns::PostRequest.create(notification, 'token')
111
+ expect(request['X-WNS-Type']).to eq('wns/badge')
112
+ expect(request['Content-Type']).to eq('text/xml')
113
+ expect(request.body).to include('<badge')
114
+ expect(request.body).to include('42')
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,167 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Daemon::Wpns::Delivery do
4
+ let(:app) { Rpush::Wpns::App.create!(name: "MyApp") }
5
+ let(:notification) { Rpush::Wpns::Notification.create!(app: app, data: { title: "MyApp", body: "Example notification", param: "/param1" }, uri: "http://some.example/", 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::Wpns::Delivery.new(app, http, notification, batch) }
12
+ let(:store) { double(create_wpns_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 "an notification with some delivery faliures" do
30
+ let(:new_notification) { Rpush::Wpns::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 falied" do
35
+ expect(delivery).to receive(:mark_failed) do |error|
36
+ expect(error.message).to match(error_description)
37
+ end
38
+ perform_with_rescue
39
+ end
40
+
41
+ it "raises a DeliveryError" do
42
+ expect { perform }.to raise_error(Rpush::DeliveryError)
43
+ end
44
+ end
45
+
46
+ describe "an 200 response" do
47
+ before do
48
+ allow(response).to receive_messages(code: 200)
49
+ end
50
+
51
+ it "marks the notification as delivered if delivered successfully to all devices" do
52
+ allow(response).to receive_messages(body: JSON.dump("failure" => 0))
53
+ allow(response).to receive_messages(to_hash: { "x-notificationstatus" => ["Received"] })
54
+ expect(batch).to receive(:mark_delivered).with(notification)
55
+ perform
56
+ end
57
+
58
+ it "retries the notification when the queue is full" do
59
+ allow(response).to receive_messages(body: JSON.dump("failure" => 0))
60
+ allow(response).to receive_messages(to_hash: { "x-notificationstatus" => ["QueueFull"] })
61
+ expect(batch).to receive(:mark_retryable).with(notification, Time.now + (60 * 10))
62
+ perform
63
+ end
64
+
65
+ it "marks the notification as failed if the notification is suppressed" do
66
+ allow(response).to receive_messages(body: JSON.dump("faliure" => 0))
67
+ allow(response).to receive_messages(to_hash: { "x-notificationstatus" => ["Suppressed"] })
68
+ error = Rpush::DeliveryError.new(200, notification.id, 'Notification was received but suppressed by the service.')
69
+ expect(delivery).to receive(:mark_failed).with(error)
70
+ perform_with_rescue
71
+ end
72
+ end
73
+
74
+ describe "an 400 response" do
75
+ before { allow(response).to receive_messages(code: 400) }
76
+ it "marks notifications as failed" do
77
+ error = Rpush::DeliveryError.new(400, notification.id, 'Bad XML or malformed notification URI.')
78
+ expect(delivery).to receive(:mark_failed).with(error)
79
+ perform_with_rescue
80
+ end
81
+ end
82
+
83
+ describe "an 401 response" do
84
+ before { allow(response).to receive_messages(code: 401) }
85
+ it "marks notifications as failed" do
86
+ error = Rpush::DeliveryError.new(401, notification.id, 'Unauthorized to send a notification to this app.')
87
+ expect(delivery).to receive(:mark_failed).with(error)
88
+ perform_with_rescue
89
+ end
90
+ end
91
+
92
+ describe "an 404 response" do
93
+ before { allow(response).to receive_messages(code: 404) }
94
+ it "marks notifications as failed" do
95
+ error = Rpush::DeliveryError.new(404, notification.id, 'Not Found')
96
+ expect(delivery).to receive(:mark_failed).with(error)
97
+ perform_with_rescue
98
+ end
99
+ end
100
+
101
+ describe "an 405 response" do
102
+ before { allow(response).to receive_messages(code: 405) }
103
+ it "marks notifications as failed" do
104
+ error = Rpush::DeliveryError.new(405, notification.id, 'Method Not Allowed')
105
+ expect(delivery).to receive(:mark_failed).with(error)
106
+ perform_with_rescue
107
+ end
108
+ end
109
+
110
+ describe "an 406 response" do
111
+ before { allow(response).to receive_messages(code: 406) }
112
+
113
+ it "retries the notification" do
114
+ expect(batch).to receive(:mark_retryable).with(notification, Time.now + (60 * 60))
115
+ perform
116
+ end
117
+
118
+ it "logs a warning that the notification will be retried" do
119
+ notification.retries = 1
120
+ notification.deliver_after = now + 2
121
+ expect(logger).to receive(:warn).with("[MyApp] Per-day throttling limit reached. Notification #{notification.id} will be retried after 2012-10-14 00:00:02 (retry 1).")
122
+ perform
123
+ end
124
+ end
125
+
126
+ describe "an 412 response" do
127
+ before { allow(response).to receive_messages(code: 412) }
128
+
129
+ it "retries the notification" do
130
+ expect(batch).to receive(:mark_retryable).with(notification, Time.now + (60 * 60))
131
+ perform
132
+ end
133
+
134
+ it "logs a warning that the notification will be retried" do
135
+ notification.retries = 1
136
+ notification.deliver_after = now + 2
137
+ expect(logger).to receive(:warn).with("[MyApp] Device unreachable. Notification #{notification.id} will be retried after 2012-10-14 00:00:02 (retry 1).")
138
+ perform
139
+ end
140
+ end
141
+
142
+ describe "an 503 response" do
143
+ before { allow(response).to receive_messages(code: 503) }
144
+
145
+ it "retries the notification exponentially" do
146
+ expect(delivery).to receive(:mark_retryable_exponential).with(notification)
147
+ perform
148
+ end
149
+
150
+ it 'logs a warning that the notification will be retried.' do
151
+ notification.retries = 1
152
+ notification.deliver_after = now + 2
153
+ expect(logger).to receive(:warn).with("[MyApp] Service Unavailable. Notification #{notification.id} will be retried after 2012-10-14 00:00:02 (retry 1).")
154
+ perform
155
+ end
156
+ end
157
+
158
+ describe 'an un-handled response' do
159
+ before { allow(response).to receive_messages(code: 418) }
160
+
161
+ it 'marks the notification as failed' do
162
+ error = Rpush::DeliveryError.new(418, notification.id, "I'm a Teapot")
163
+ expect(delivery).to receive(:mark_failed).with(error)
164
+ perform_with_rescue
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,138 @@
1
+ require 'unit_spec_helper'
2
+ require 'rpush/daemon/store/active_record'
3
+
4
+ describe Rpush::Daemon, "when starting" do
5
+ module Rails; end
6
+
7
+ let(:certificate) { double }
8
+ let(:password) { double }
9
+ let(:logger) { double(:logger, info: nil, error: nil, warn: nil, internal_logger: nil) }
10
+
11
+ before do
12
+ allow(Rpush).to receive(:logger) { logger }
13
+ allow(Rpush::Daemon::Feeder).to receive(:start)
14
+ allow(Rpush::Daemon::Synchronizer).to receive(:sync)
15
+ allow(Rpush::Daemon::AppRunner).to receive(:stop)
16
+ allow(Rpush::Daemon::Rpc::Server).to receive(:start)
17
+ allow(Rpush::Daemon::Rpc::Server).to receive(:stop)
18
+ allow(Rpush::Daemon).to receive(:exit)
19
+ allow(Rpush::Daemon).to receive(:puts)
20
+ allow(Rpush::Daemon::SignalHandler).to receive(:start)
21
+ allow(Rpush::Daemon::SignalHandler).to receive(:stop)
22
+ allow(Rpush::Daemon::SignalHandler).to receive(:handle_shutdown_signal)
23
+ allow(Process).to receive(:daemon)
24
+ allow(File).to receive(:open)
25
+ end
26
+
27
+ unless Rpush.jruby?
28
+ it "forks into a daemon if the foreground option is false" do
29
+ Rpush.config.foreground = false
30
+ Rpush::Daemon.common_init
31
+ expect(Process).to receive(:daemon)
32
+ Rpush::Daemon.start
33
+ end
34
+
35
+ it "does not fork into a daemon if the foreground option is true" do
36
+ Rpush.config.foreground = true
37
+ expect(Process).to_not receive(:daemon)
38
+ Rpush::Daemon.start
39
+ end
40
+
41
+ it "does not fork into a daemon if the push option is true" do
42
+ Rpush.config.push = true
43
+ expect(Process).to_not receive(:daemon)
44
+ Rpush::Daemon.start
45
+ end
46
+
47
+ it "does not fork into a daemon if the embedded option is true" do
48
+ Rpush.config.embedded = true
49
+ expect(Process).to_not receive(:daemon)
50
+ Rpush::Daemon.start
51
+ end
52
+ end
53
+
54
+ it 'releases the store connection' do
55
+ Rpush::Daemon.store = double
56
+ expect(Rpush::Daemon.store).to receive(:release_connection)
57
+ Rpush::Daemon.start
58
+ end
59
+
60
+ it 'sets up setup signal traps' do
61
+ expect(Rpush::Daemon::SignalHandler).to receive(:start)
62
+ Rpush::Daemon.start
63
+ end
64
+
65
+ it 'instantiates the store' do
66
+ Rpush.config.client = :active_record
67
+ Rpush::Daemon.start
68
+ expect(Rpush::Daemon.store).to be_kind_of(Rpush::Daemon::Store::ActiveRecord)
69
+ end
70
+
71
+ it 'initializes plugins' do
72
+ plugin = Rpush.plugin(:test)
73
+ did_init = false
74
+ plugin.init { did_init = true }
75
+ Rpush::Daemon.common_init
76
+ expect(did_init).to eq(true)
77
+ end
78
+
79
+ it 'logs an error if the store cannot be loaded' do
80
+ Rpush.config.client = :foo_bar
81
+ expect(Rpush.logger).to receive(:error).with(kind_of(LoadError))
82
+ allow(Rpush::Daemon).to receive(:exit) { Rpush::Daemon.store = double.as_null_object }
83
+ Rpush::Daemon.start
84
+ end
85
+
86
+ it "writes the process ID to the PID file" do
87
+ expect(Rpush::Daemon).to receive(:write_pid_file)
88
+ Rpush::Daemon.start
89
+ end
90
+
91
+ it "logs an error if the PID file could not be written" do
92
+ Rpush.config.pid_file = '/rails_root/rpush.pid'
93
+ allow(File).to receive(:open).and_raise(Errno::ENOENT)
94
+ expect(logger).to receive(:error).with(%r{Failed to write PID to '/rails_root/rpush\.pid'})
95
+ Rpush::Daemon.start
96
+ end
97
+
98
+ it "starts the feeder" do
99
+ expect(Rpush::Daemon::Feeder).to receive(:start)
100
+ Rpush::Daemon.start
101
+ end
102
+
103
+ it "syncs apps" do
104
+ expect(Rpush::Daemon::Synchronizer).to receive(:sync)
105
+ Rpush::Daemon.start
106
+ end
107
+
108
+ describe "shutdown" do
109
+ it "stops the feeder" do
110
+ expect(Rpush::Daemon::Feeder).to receive(:stop)
111
+ Rpush::Daemon.shutdown
112
+ end
113
+
114
+ it "stops the app runners" do
115
+ expect(Rpush::Daemon::AppRunner).to receive(:stop)
116
+ Rpush::Daemon.shutdown
117
+ end
118
+
119
+ it "removes the PID file if one was written" do
120
+ Rpush.config.pid_file = "/rails_root/rpush.pid"
121
+ allow(File).to receive(:exist?) { true }
122
+ expect(File).to receive(:delete).with("/rails_root/rpush.pid")
123
+ Rpush::Daemon.shutdown
124
+ end
125
+
126
+ it "does not attempt to remove the PID file if it does not exist" do
127
+ allow(File).to receive(:exist?) { false }
128
+ expect(File).to_not receive(:delete)
129
+ Rpush::Daemon.shutdown
130
+ end
131
+
132
+ it "does not attempt to remove the PID file if one was not written" do
133
+ Rpush.config.pid_file = nil
134
+ expect(File).to_not receive(:delete)
135
+ Rpush::Daemon.shutdown
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,32 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Deprecatable do
4
+ class HasDeprecatedMethod
5
+ include Rpush::Deprecatable
6
+
7
+ def original_called?
8
+ @called == true
9
+ end
10
+
11
+ def deprecated_method
12
+ @called = true
13
+ end
14
+ deprecated(:deprecated_method, '4.0')
15
+ end
16
+
17
+ let(:klass) { HasDeprecatedMethod.new }
18
+
19
+ before do
20
+ allow(Rpush::Deprecation).to receive(:warn)
21
+ end
22
+
23
+ it 'warns the method is deprecated when called' do
24
+ expect(Rpush::Deprecation).to receive(:warn).with(/deprecated_method is deprecated and will be removed from Rpush 4\.0\./)
25
+ klass.deprecated_method
26
+ end
27
+
28
+ it 'calls the original method' do
29
+ klass.deprecated_method
30
+ expect(klass.original_called?).to eq(true)
31
+ end
32
+ end
@@ -0,0 +1,15 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Deprecation do
4
+ it 'prints a warning' do
5
+ expect(STDERR).to receive(:puts).with("DEPRECATION WARNING: msg")
6
+ Rpush::Deprecation.warn("msg")
7
+ end
8
+
9
+ it 'does not print a warning when muted' do
10
+ expect(STDERR).not_to receive(:puts)
11
+ Rpush::Deprecation.muted do
12
+ Rpush::Deprecation.warn("msg")
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,47 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush, 'embed' do
4
+ before do
5
+ allow(Rpush::Daemon).to receive_messages(start: nil, shutdown: nil)
6
+ allow(Kernel).to receive(:at_exit)
7
+ end
8
+
9
+ after { Rpush.shutdown }
10
+
11
+ it 'sets the embedded config option to true' do
12
+ Rpush.embed
13
+ expect(Rpush.config.embedded).to eq(true)
14
+ end
15
+
16
+ it 'starts the daemon' do
17
+ expect(Rpush::Daemon).to receive(:start)
18
+ Rpush.embed
19
+ end
20
+ end
21
+
22
+ describe Rpush, 'shutdown' do
23
+ before { Rpush.config.embedded = true }
24
+
25
+ it 'shuts down the daemon' do
26
+ expect(Rpush::Daemon).to receive(:shutdown)
27
+ Rpush.shutdown
28
+ end
29
+ end
30
+
31
+ describe Rpush, 'sync' do
32
+ before { Rpush.config.embedded = true }
33
+
34
+ it 'syncs' do
35
+ expect(Rpush::Daemon::Synchronizer).to receive(:sync)
36
+ Rpush.sync
37
+ end
38
+ end
39
+
40
+ describe Rpush, 'status' do
41
+ before { Rpush.config.embedded = true }
42
+
43
+ it 'returns the AppRunner status' do
44
+ expect(Rpush::Daemon::AppRunner).to receive_messages(status: { status: true })
45
+ expect(Rpush.status).to eq(status: true)
46
+ end
47
+ end