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,365 @@
1
+ require 'unit_spec_helper'
2
+ require 'rpush/daemon/store/redis'
3
+
4
+ describe Rpush::Daemon::Store::Redis do
5
+ let(:app) { Rpush::Client::Redis::Apns::App.create!(name: 'my_app', environment: 'development', certificate: TEST_CERT) }
6
+ let(:notification) { Rpush::Client::Redis::Apns::Notification.create!(device_token: "a" * 64, app: app) }
7
+ let(:store) { Rpush::Daemon::Store::Redis.new }
8
+ let(:time) { Time.now.utc }
9
+ let(:logger) { double(Rpush::Logger, error: nil, internal_logger: nil) }
10
+
11
+ before do
12
+ allow(Rpush).to receive_messages(logger: logger)
13
+ allow(Time).to receive_messages(now: time)
14
+ end
15
+
16
+ it 'updates an notification' do
17
+ expect(notification).to receive(:save!)
18
+ store.update_notification(notification)
19
+ end
20
+
21
+ it 'updates an app' do
22
+ expect(app).to receive(:save!)
23
+ store.update_app(app)
24
+ end
25
+
26
+ it 'finds an app by ID' do
27
+ app
28
+ expect(store.app(app.id)).to eq(app)
29
+ end
30
+
31
+ it 'finds all apps' do
32
+ app
33
+ expect(store.all_apps).to eq([app])
34
+ end
35
+
36
+ it 'translates an Integer notification ID' do
37
+ expect(store.translate_integer_notification_id(notification.id)).to eq(notification.id)
38
+ end
39
+
40
+ it 'returns the pending notification count' do
41
+ notification
42
+ expect(store.pending_delivery_count).to eq(1)
43
+ end
44
+
45
+ describe 'deliverable_notifications' do
46
+ it 'loads notifications in batches' do
47
+ Rpush.config.batch_size = 100
48
+ allow(store).to receive_messages(pending_notification_ids: [1, 2, 3, 4])
49
+ expect(Rpush::Client::Redis::Notification).to receive(:find).exactly(4).times
50
+ store.deliverable_notifications(Rpush.config.batch_size)
51
+ end
52
+
53
+ it 'loads an undelivered notification without deliver_after set' do
54
+ notification.update_attributes!(delivered: false, deliver_after: nil)
55
+ expect(store.deliverable_notifications(Rpush.config.batch_size)).to eq [notification]
56
+ end
57
+
58
+ it 'loads an notification with a deliver_after time in the past' do
59
+ notification.update_attributes!(delivered: false, deliver_after: 1.hour.ago)
60
+ expect(store.deliverable_notifications(Rpush.config.batch_size)).to eq [notification]
61
+ end
62
+
63
+ it 'does not load an notification with a deliver_after time in the future' do
64
+ notification
65
+ notification = store.deliverable_notifications(Rpush.config.batch_size).first
66
+ store.mark_retryable(notification, 1.hour.from_now)
67
+ expect(store.deliverable_notifications(Rpush.config.batch_size)).to be_empty
68
+ end
69
+
70
+ it 'does not load a previously delivered notification' do
71
+ notification
72
+ notification = store.deliverable_notifications(Rpush.config.batch_size).first
73
+ store.mark_delivered(notification, Time.now)
74
+ expect(store.deliverable_notifications(Rpush.config.batch_size)).to be_empty
75
+ end
76
+
77
+ it "does not enqueue a notification that has previously failed delivery" do
78
+ notification
79
+ notification = store.deliverable_notifications(Rpush.config.batch_size).first
80
+ store.mark_failed(notification, 0, "failed", Time.now)
81
+ expect(store.deliverable_notifications(Rpush.config.batch_size)).to be_empty
82
+ end
83
+ end
84
+
85
+ describe 'mark_retryable' do
86
+ it 'increments the retry count' do
87
+ expect do
88
+ store.mark_retryable(notification, time)
89
+ end.to change(notification, :retries).by(1)
90
+ end
91
+
92
+ it 'sets the deliver after timestamp' do
93
+ deliver_after = time + 10.seconds
94
+ expect do
95
+ store.mark_retryable(notification, deliver_after)
96
+ end.to change(notification, :deliver_after).to(deliver_after)
97
+ end
98
+
99
+ it 'saves the notification without validation' do
100
+ expect(notification).to receive(:save!).with(validate: false)
101
+ store.mark_retryable(notification, time)
102
+ end
103
+
104
+ it 'does not save the notification if persist: false' do
105
+ expect(notification).not_to receive(:save!)
106
+ store.mark_retryable(notification, time, persist: false)
107
+ end
108
+ end
109
+
110
+ describe 'mark_ids_retryable' do
111
+ let(:deliver_after) { time + 10.seconds }
112
+
113
+ it 'sets the deliver after timestamp' do
114
+ expect do
115
+ store.mark_ids_retryable([notification.id], deliver_after)
116
+ notification.reload
117
+ end.to change { notification.deliver_after.try(:utc).to_s }.to(deliver_after.utc.to_s)
118
+ end
119
+ end
120
+
121
+ describe 'mark_batch_retryable' do
122
+ let(:deliver_after) { time + 10.seconds }
123
+
124
+ it 'sets the attributes on the object for use in reflections' do
125
+ store.mark_batch_retryable([notification], deliver_after)
126
+ expect(notification.deliver_after).to eq deliver_after
127
+ expect(notification.retries).to eq 1
128
+ end
129
+
130
+ it 'increments the retired count' do
131
+ expect do
132
+ store.mark_batch_retryable([notification], deliver_after)
133
+ notification.reload
134
+ end.to change(notification, :retries).by(1)
135
+ end
136
+
137
+ it 'sets the deliver after timestamp' do
138
+ expect do
139
+ store.mark_batch_retryable([notification], deliver_after)
140
+ notification.reload
141
+ end.to change { notification.deliver_after.try(:utc).to_s }.to(deliver_after.utc.to_s)
142
+ end
143
+ end
144
+
145
+ describe 'mark_delivered' do
146
+ it 'marks the notification as delivered' do
147
+ expect do
148
+ store.mark_delivered(notification, time)
149
+ end.to change(notification, :delivered).to(true)
150
+ end
151
+
152
+ it 'sets the time the notification was delivered' do
153
+ expect do
154
+ store.mark_delivered(notification, time)
155
+ notification.reload
156
+ end.to change { notification.delivered_at.try(:utc).to_s }.to(time.to_s)
157
+ end
158
+
159
+ it 'saves the notification without validation' do
160
+ expect(notification).to receive(:save!).with(validate: false)
161
+ store.mark_delivered(notification, time)
162
+ end
163
+
164
+ it 'does not save the notification if persist: false' do
165
+ expect(notification).not_to receive(:save!)
166
+ store.mark_delivered(notification, time, persist: false)
167
+ end
168
+ end
169
+
170
+ describe 'mark_batch_delivered' do
171
+ it 'sets the attributes on the object for use in reflections' do
172
+ store.mark_batch_delivered([notification])
173
+ expect(notification.delivered_at).to eq time
174
+ expect(notification.delivered).to be_truthy
175
+ end
176
+
177
+ it 'marks the notifications as delivered' do
178
+ expect do
179
+ store.mark_batch_delivered([notification])
180
+ notification.reload
181
+ end.to change(notification, :delivered).to(true)
182
+ end
183
+
184
+ it 'sets the time the notifications were delivered' do
185
+ expect do
186
+ store.mark_batch_delivered([notification])
187
+ notification.reload
188
+ end.to change { notification.delivered_at.try(:utc).to_s }.to(time.to_s)
189
+ end
190
+ end
191
+
192
+ describe 'mark_failed' do
193
+ it 'marks the notification as not delivered' do
194
+ store.mark_failed(notification, nil, '', time)
195
+ expect(notification.delivered).to eq(false)
196
+ end
197
+
198
+ it 'marks the notification as failed' do
199
+ expect do
200
+ store.mark_failed(notification, nil, '', time)
201
+ notification.reload
202
+ end.to change(notification, :failed).to(true)
203
+ end
204
+
205
+ it 'sets the time the notification delivery failed' do
206
+ expect do
207
+ store.mark_failed(notification, nil, '', time)
208
+ notification.reload
209
+ end.to change { notification.failed_at.try(:utc).to_s }.to(time.to_s)
210
+ end
211
+
212
+ it 'sets the error code' do
213
+ expect do
214
+ store.mark_failed(notification, 42, '', time)
215
+ end.to change(notification, :error_code).to(42)
216
+ end
217
+
218
+ it 'sets the error description' do
219
+ expect do
220
+ store.mark_failed(notification, 42, 'Weeee', time)
221
+ end.to change(notification, :error_description).to('Weeee')
222
+ end
223
+
224
+ it 'saves the notification without validation' do
225
+ expect(notification).to receive(:save!).with(validate: false)
226
+ store.mark_failed(notification, nil, '', time)
227
+ end
228
+
229
+ it 'does not save the notification if persist: false' do
230
+ expect(notification).not_to receive(:save!)
231
+ store.mark_failed(notification, nil, '', time, persist: false)
232
+ end
233
+ end
234
+
235
+ describe 'mark_ids_failed' do
236
+ it 'marks the notification as failed' do
237
+ expect do
238
+ store.mark_ids_failed([notification.id], nil, '', Time.now)
239
+ notification.reload
240
+ end.to change(notification, :failed).to(true)
241
+ end
242
+ end
243
+
244
+ describe 'mark_batch_failed' do
245
+ it 'sets the attributes on the object for use in reflections' do
246
+ store.mark_batch_failed([notification], 123, 'an error')
247
+ expect(notification.failed_at).to eq time
248
+ expect(notification.delivered_at).to be_nil
249
+ expect(notification.delivered).to eq(false)
250
+ expect(notification.failed).to be_truthy
251
+ expect(notification.error_code).to eq 123
252
+ expect(notification.error_description).to eq 'an error'
253
+ end
254
+
255
+ it 'marks the notification as not delivered' do
256
+ store.mark_batch_failed([notification], nil, '')
257
+ notification.reload
258
+ expect(notification.delivered).to be_falsey
259
+ end
260
+
261
+ it 'marks the notification as failed' do
262
+ expect do
263
+ store.mark_batch_failed([notification], nil, '')
264
+ notification.reload
265
+ end.to change(notification, :failed).to(true)
266
+ end
267
+
268
+ it 'sets the time the notification delivery failed' do
269
+ expect do
270
+ store.mark_batch_failed([notification], nil, '')
271
+ notification.reload
272
+ end.to change { notification.failed_at.try(:utc).to_s }.to(time.to_s)
273
+ end
274
+
275
+ it 'sets the error code' do
276
+ expect do
277
+ store.mark_batch_failed([notification], 42, '')
278
+ notification.reload
279
+ end.to change(notification, :error_code).to(42)
280
+ end
281
+
282
+ it 'sets the error description' do
283
+ expect do
284
+ store.mark_batch_failed([notification], 42, 'Weeee')
285
+ notification.reload
286
+ end.to change(notification, :error_description).to('Weeee')
287
+ end
288
+ end
289
+
290
+ describe 'create_apns_feedback' do
291
+ it 'creates the Feedback record' do
292
+ expect(Rpush::Client::Redis::Apns::Feedback).to receive(:create!).with(
293
+ failed_at: time, device_token: 'ab' * 32, app_id: app.id)
294
+ store.create_apns_feedback(time, 'ab' * 32, app)
295
+ end
296
+ end
297
+
298
+ describe 'create_gcm_notification' do
299
+ let(:data) { { data: true } }
300
+ let(:attributes) { { device_token: 'ab' * 32 } }
301
+ let(:registration_ids) { %w(123 456) }
302
+ let(:deliver_after) { time + 10.seconds }
303
+ let(:args) { [attributes, data, registration_ids, deliver_after, app] }
304
+
305
+ it 'sets the given attributes' do
306
+ new_notification = store.create_gcm_notification(*args)
307
+ expect(new_notification.device_token).to eq 'ab' * 32
308
+ end
309
+
310
+ it 'sets the given data' do
311
+ new_notification = store.create_gcm_notification(*args)
312
+ expect(new_notification.data).to eq(data: true)
313
+ end
314
+
315
+ it 'sets the given registration IDs' do
316
+ new_notification = store.create_gcm_notification(*args)
317
+ expect(new_notification.registration_ids).to eq registration_ids
318
+ end
319
+
320
+ it 'sets the deliver_after timestamp' do
321
+ new_notification = store.create_gcm_notification(*args)
322
+ expect(new_notification.deliver_after.utc.to_s).to eq deliver_after.to_s
323
+ end
324
+
325
+ it 'saves the new notification' do
326
+ new_notification = store.create_gcm_notification(*args)
327
+ expect(new_notification.new_record?).to be_falsey
328
+ end
329
+ end
330
+
331
+ describe 'create_adm_notification' do
332
+ let(:data) { { data: true } }
333
+ let(:attributes) { { app_id: app.id, collapse_key: 'ckey', delay_while_idle: true } }
334
+ let(:registration_ids) { %w(123 456) }
335
+ let(:deliver_after) { time + 10.seconds }
336
+ let(:args) { [attributes, data, registration_ids, deliver_after, app] }
337
+
338
+ it 'sets the given attributes' do
339
+ new_notification = store.create_adm_notification(*args)
340
+ expect(new_notification.app_id).to eq app.id
341
+ expect(new_notification.collapse_key).to eq 'ckey'
342
+ expect(new_notification.delay_while_idle).to be_truthy
343
+ end
344
+
345
+ it 'sets the given data' do
346
+ new_notification = store.create_adm_notification(*args)
347
+ expect(new_notification.data).to eq(data: true)
348
+ end
349
+
350
+ it 'sets the given registration IDs' do
351
+ new_notification = store.create_adm_notification(*args)
352
+ expect(new_notification.registration_ids).to eq registration_ids
353
+ end
354
+
355
+ it 'sets the deliver_after timestamp' do
356
+ new_notification = store.create_adm_notification(*args)
357
+ expect(new_notification.deliver_after.utc.to_s).to eq deliver_after.to_s
358
+ end
359
+
360
+ it 'saves the new notification' do
361
+ new_notification = store.create_adm_notification(*args)
362
+ expect(new_notification.new_record?).to be_falsey
363
+ end
364
+ end
365
+ end if redis?
@@ -0,0 +1,292 @@
1
+ require "unit_spec_helper"
2
+
3
+ describe Rpush::Daemon::TcpConnection do
4
+ let(:rsa_key) { double }
5
+ let(:certificate) { double }
6
+ let(:password) { double }
7
+ let(:x509_certificate) { OpenSSL::X509::Certificate.new(TEST_CERT) }
8
+ let(:ssl_context) { double(:key= => nil, :cert= => nil, cert: x509_certificate) }
9
+ let(:host) { 'gateway.push.apple.com' }
10
+ let(:port) { '2195' }
11
+ let(:tcp_socket) { double(setsockopt: nil, close: nil) }
12
+ let(:ssl_socket) { double(:sync= => nil, connect: nil, close: nil, write: nil, flush: nil) }
13
+ let(:logger) { double(info: nil, error: nil, warn: nil) }
14
+ let(:app) { double(name: 'Connection 0', certificate: certificate, password: password) }
15
+ let(:connection) { Rpush::Daemon::TcpConnection.new(app, host, port) }
16
+
17
+ before do
18
+ allow(OpenSSL::SSL::SSLContext).to receive_messages(new: ssl_context)
19
+ allow(OpenSSL::PKey::RSA).to receive_messages(new: rsa_key)
20
+ allow(OpenSSL::X509::Certificate).to receive_messages(new: x509_certificate)
21
+ allow(TCPSocket).to receive_messages(new: tcp_socket)
22
+ allow(OpenSSL::SSL::SSLSocket).to receive_messages(new: ssl_socket)
23
+ allow(Rpush).to receive_messages(logger: logger)
24
+ allow(connection).to receive(:reflect)
25
+ end
26
+
27
+ it "reads the number of bytes from the SSL socket" do
28
+ expect(ssl_socket).to receive(:read).with(123)
29
+ connection.connect
30
+ connection.read(123)
31
+ end
32
+
33
+ it "selects on the SSL socket until the given timeout" do
34
+ expect(IO).to receive(:select).with([ssl_socket], nil, nil, 10)
35
+ connection.connect
36
+ connection.select(10)
37
+ end
38
+
39
+ describe "when setting up the SSL context" do
40
+ it "sets the key on the context" do
41
+ expect(OpenSSL::PKey::RSA).to receive(:new).with(certificate, password).and_return(rsa_key)
42
+ expect(ssl_context).to receive(:key=).with(rsa_key)
43
+ connection.connect
44
+ end
45
+
46
+ it "sets the cert on the context" do
47
+ expect(OpenSSL::X509::Certificate).to receive(:new).with(certificate).and_return(x509_certificate)
48
+ expect(ssl_context).to receive(:cert=).with(x509_certificate)
49
+ connection.connect
50
+ end
51
+ end
52
+
53
+ describe "when connecting the socket" do
54
+ it "creates a TCP socket using the configured host and port" do
55
+ expect(TCPSocket).to receive(:new).with(host, port).and_return(tcp_socket)
56
+ connection.connect
57
+ end
58
+
59
+ it "creates a new SSL socket using the TCP socket and SSL context" do
60
+ expect(OpenSSL::SSL::SSLSocket).to receive(:new).with(tcp_socket, ssl_context).and_return(ssl_socket)
61
+ connection.connect
62
+ end
63
+
64
+ it "sets the sync option on the SSL socket" do
65
+ expect(ssl_socket).to receive(:sync=).with(true)
66
+ connection.connect
67
+ end
68
+
69
+ it "connects the SSL socket" do
70
+ expect(ssl_socket).to receive(:connect)
71
+ connection.connect
72
+ end
73
+
74
+ it "sets the socket option TCP_NODELAY" do
75
+ expect(tcp_socket).to receive(:setsockopt).with(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true)
76
+ connection.connect
77
+ end
78
+
79
+ it "sets the socket option SO_KEEPALIVE" do
80
+ expect(tcp_socket).to receive(:setsockopt).with(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)
81
+ connection.connect
82
+ end
83
+
84
+ describe 'certificate expiry' do
85
+ it 'reflects if the certificate will expire soon' do
86
+ cert = OpenSSL::X509::Certificate.new(app.certificate)
87
+ expect(connection).to receive(:reflect).with(:ssl_certificate_will_expire, app, cert.not_after)
88
+ Timecop.freeze(cert.not_after - 3.days) { connection.connect }
89
+ end
90
+
91
+ it 'logs that the certificate will expire soon' do
92
+ cert = OpenSSL::X509::Certificate.new(app.certificate)
93
+ expect(logger).to receive(:warn).with("[#{app.name}] Certificate will expire at 2022-09-07 03:18:32 UTC.")
94
+ Timecop.freeze(cert.not_after - 3.days) { connection.connect }
95
+ end
96
+
97
+ it 'does not reflect if the certificate will not expire soon' do
98
+ cert = OpenSSL::X509::Certificate.new(app.certificate)
99
+ expect(connection).not_to receive(:reflect).with(:ssl_certificate_will_expire, app, kind_of(Time))
100
+ Timecop.freeze(cert.not_after - 2.months) { connection.connect }
101
+ end
102
+
103
+ it 'logs that the certificate has expired' do
104
+ cert = OpenSSL::X509::Certificate.new(app.certificate)
105
+ expect(logger).to receive(:error).with("[#{app.name}] Certificate expired at 2022-09-07 03:18:32 UTC.")
106
+ Timecop.freeze(cert.not_after + 1.day) { connection.connect rescue Rpush::CertificateExpiredError }
107
+ end
108
+
109
+ it 'raises an error if the certificate has expired' do
110
+ cert = OpenSSL::X509::Certificate.new(app.certificate)
111
+ Timecop.freeze(cert.not_after + 1.day) do
112
+ expect { connection.connect }.to raise_error(Rpush::CertificateExpiredError)
113
+ end
114
+ end
115
+ end
116
+
117
+ describe 'certificate revocation' do
118
+ let(:cert_revoked_error) { OpenSSL::SSL::SSLError.new('certificate revoked') }
119
+ before do
120
+ allow(ssl_socket).to receive(:connect).and_raise(cert_revoked_error)
121
+ end
122
+
123
+ it 'reflects that the certificate has been revoked' do
124
+ expect(connection).to receive(:reflect).with(:ssl_certificate_revoked, app, cert_revoked_error)
125
+ expect { connection.connect }.to raise_error(Rpush::Daemon::TcpConnectionError, 'OpenSSL::SSL::SSLError, certificate revoked')
126
+ end
127
+
128
+ it 'logs that the certificate has been revoked' do
129
+ expect(logger).to receive(:error).with('[Connection 0] Certificate has been revoked.')
130
+ expect { connection.connect }.to raise_error(Rpush::Daemon::TcpConnectionError, 'OpenSSL::SSL::SSLError, certificate revoked')
131
+ end
132
+ end
133
+ end
134
+
135
+ describe "when shuting down the connection" do
136
+ it "closes the TCP socket" do
137
+ connection.connect
138
+ expect(tcp_socket).to receive(:close)
139
+ connection.close
140
+ end
141
+
142
+ it "does not attempt to close the TCP socket if it is not connected" do
143
+ connection.connect
144
+ expect(tcp_socket).not_to receive(:close)
145
+ connection.instance_variable_set("@tcp_socket", nil)
146
+ connection.close
147
+ end
148
+
149
+ it "closes the SSL socket" do
150
+ connection.connect
151
+ expect(ssl_socket).to receive(:close)
152
+ connection.close
153
+ end
154
+
155
+ it "does not attempt to close the SSL socket if it is not connected" do
156
+ connection.connect
157
+ expect(ssl_socket).not_to receive(:close)
158
+ connection.instance_variable_set("@ssl_socket", nil)
159
+ connection.close
160
+ end
161
+
162
+ it "ignores IOError when the socket is already closed" do
163
+ allow(tcp_socket).to receive(:close).and_raise(IOError)
164
+ connection.connect
165
+ connection.close
166
+ end
167
+ end
168
+
169
+ shared_examples_for "when the write fails" do
170
+ before do
171
+ allow(connection).to receive(:sleep)
172
+ connection.connect
173
+ allow(ssl_socket).to receive(:write).and_raise(error)
174
+ end
175
+
176
+ it 'reflects the connection has been lost' do
177
+ expect(connection).to receive(:reflect).with(:tcp_connection_lost, app, kind_of(error.class))
178
+ expect { connection.write(nil) }.to raise_error(Rpush::Daemon::TcpConnectionError)
179
+ end
180
+
181
+ it "logs that the connection has been lost once only" do
182
+ expect(logger).to receive(:error).with("[Connection 0] Lost connection to gateway.push.apple.com:2195 (#{error.class.name}, #{error.message}), reconnecting...").once
183
+ expect { connection.write(nil) }.to raise_error(Rpush::Daemon::TcpConnectionError)
184
+ end
185
+
186
+ it "retries to make a connection 3 times" do
187
+ expect(connection).to receive(:reconnect).exactly(3).times
188
+ expect { connection.write(nil) }.to raise_error(Rpush::Daemon::TcpConnectionError)
189
+ end
190
+
191
+ it "raises a TcpConnectionError after 3 attempts at reconnecting" do
192
+ expect do
193
+ connection.write(nil)
194
+ end.to raise_error(Rpush::Daemon::TcpConnectionError, "Connection 0 tried 3 times to reconnect but failed (#{error.class.name}, #{error.message}).")
195
+ end
196
+
197
+ it "sleeps 1 second before retrying the connection" do
198
+ expect(connection).to receive(:sleep).with(1)
199
+ expect { connection.write(nil) }.to raise_error(Rpush::Daemon::TcpConnectionError)
200
+ end
201
+ end
202
+
203
+ describe "when write raises an Errno::EPIPE" do
204
+ it_should_behave_like "when the write fails"
205
+
206
+ def error
207
+ Errno::EPIPE.new('an message')
208
+ end
209
+ end
210
+
211
+ describe "when write raises an Errno::ETIMEDOUT" do
212
+ it_should_behave_like "when the write fails"
213
+
214
+ def error
215
+ Errno::ETIMEDOUT.new('an message')
216
+ end
217
+ end
218
+
219
+ describe "when write raises an OpenSSL::SSL::SSLError" do
220
+ it_should_behave_like "when the write fails"
221
+
222
+ def error
223
+ OpenSSL::SSL::SSLError.new('an message')
224
+ end
225
+ end
226
+
227
+ describe "when write raises an IOError" do
228
+ it_should_behave_like "when the write fails"
229
+
230
+ def error
231
+ IOError.new('an message')
232
+ end
233
+ end
234
+
235
+ describe "when reconnecting" do
236
+ before { connection.connect }
237
+
238
+ it 'closes the socket' do
239
+ expect(connection).to receive(:close)
240
+ connection.send(:reconnect)
241
+ end
242
+
243
+ it 'connects the socket' do
244
+ expect(connection).to receive(:connect_socket)
245
+ connection.send(:reconnect)
246
+ end
247
+ end
248
+
249
+ describe "when sending a notification" do
250
+ before { connection.connect }
251
+
252
+ it "writes the data to the SSL socket" do
253
+ expect(ssl_socket).to receive(:write).with("blah")
254
+ connection.write("blah")
255
+ end
256
+
257
+ it "flushes the SSL socket" do
258
+ expect(ssl_socket).to receive(:flush)
259
+ connection.write("blah")
260
+ end
261
+ end
262
+
263
+ describe 'idle period' do
264
+ before { connection.connect }
265
+
266
+ it 'reconnects if the connection has been idle for more than the defined period' do
267
+ allow(Rpush::Daemon::TcpConnection).to receive_messages(idle_period: 60)
268
+ allow(Time).to receive_messages(now: Time.now + 61)
269
+ expect(connection).to receive(:reconnect)
270
+ connection.write('blah')
271
+ end
272
+
273
+ it 'resets the last touch time' do
274
+ now = Time.now
275
+ allow(Time).to receive_messages(now: now)
276
+ connection.write('blah')
277
+ expect(connection.last_touch).to eq now
278
+ end
279
+
280
+ it 'does not reconnect if the connection has not been idle for more than the defined period' do
281
+ expect(connection).not_to receive(:reconnect)
282
+ connection.write('blah')
283
+ end
284
+
285
+ it 'logs the the connection is idle' do
286
+ allow(Rpush::Daemon::TcpConnection).to receive_messages(idle_period: 60)
287
+ allow(Time).to receive_messages(now: Time.now + 61)
288
+ expect(Rpush.logger).to receive(:info).with('[Connection 0] Idle period exceeded, reconnecting...')
289
+ connection.write('blah')
290
+ end
291
+ end
292
+ end