fcm-ruby-push-notifications 1.2.0

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 (73) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +37 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +19 -0
  5. data/CHANGELOG.md +32 -0
  6. data/Gemfile +6 -0
  7. data/LICENSE +22 -0
  8. data/README.md +82 -0
  9. data/Rakefile +12 -0
  10. data/examples/apns.rb +27 -0
  11. data/examples/gcm.rb +25 -0
  12. data/examples/mpns.rb +22 -0
  13. data/examples/wns.rb +25 -0
  14. data/gemfiles/Gemfile-legacy +8 -0
  15. data/lib/ruby-push-notifications.rb +7 -0
  16. data/lib/ruby-push-notifications/apns.rb +44 -0
  17. data/lib/ruby-push-notifications/apns/apns_connection.rb +70 -0
  18. data/lib/ruby-push-notifications/apns/apns_notification.rb +91 -0
  19. data/lib/ruby-push-notifications/apns/apns_pusher.rb +84 -0
  20. data/lib/ruby-push-notifications/apns/apns_results.rb +30 -0
  21. data/lib/ruby-push-notifications/fcm.rb +8 -0
  22. data/lib/ruby-push-notifications/fcm/fcm_connection.rb +60 -0
  23. data/lib/ruby-push-notifications/fcm/fcm_error.rb +17 -0
  24. data/lib/ruby-push-notifications/fcm/fcm_notification.rb +31 -0
  25. data/lib/ruby-push-notifications/fcm/fcm_pusher.rb +29 -0
  26. data/lib/ruby-push-notifications/fcm/fcm_request.rb +28 -0
  27. data/lib/ruby-push-notifications/fcm/fcm_response.rb +89 -0
  28. data/lib/ruby-push-notifications/fcm/fcm_result.rb +52 -0
  29. data/lib/ruby-push-notifications/gcm.rb +6 -0
  30. data/lib/ruby-push-notifications/gcm/gcm_connection.rb +54 -0
  31. data/lib/ruby-push-notifications/gcm/gcm_error.rb +17 -0
  32. data/lib/ruby-push-notifications/gcm/gcm_notification.rb +31 -0
  33. data/lib/ruby-push-notifications/gcm/gcm_pusher.rb +35 -0
  34. data/lib/ruby-push-notifications/gcm/gcm_response.rb +89 -0
  35. data/lib/ruby-push-notifications/gcm/gcm_result.rb +52 -0
  36. data/lib/ruby-push-notifications/mpns.rb +5 -0
  37. data/lib/ruby-push-notifications/mpns/mpns_connection.rb +87 -0
  38. data/lib/ruby-push-notifications/mpns/mpns_notification.rb +94 -0
  39. data/lib/ruby-push-notifications/mpns/mpns_pusher.rb +33 -0
  40. data/lib/ruby-push-notifications/mpns/mpns_response.rb +82 -0
  41. data/lib/ruby-push-notifications/mpns/mpns_result.rb +182 -0
  42. data/lib/ruby-push-notifications/notification_results_manager.rb +14 -0
  43. data/lib/ruby-push-notifications/wns.rb +6 -0
  44. data/lib/ruby-push-notifications/wns/wns_access.rb +82 -0
  45. data/lib/ruby-push-notifications/wns/wns_connection.rb +97 -0
  46. data/lib/ruby-push-notifications/wns/wns_notification.rb +101 -0
  47. data/lib/ruby-push-notifications/wns/wns_pusher.rb +32 -0
  48. data/lib/ruby-push-notifications/wns/wns_response.rb +83 -0
  49. data/lib/ruby-push-notifications/wns/wns_result.rb +208 -0
  50. data/ruby-push-notifications.gemspec +28 -0
  51. data/spec/factories.rb +17 -0
  52. data/spec/factories/notifications.rb +30 -0
  53. data/spec/ruby-push-notifications/apns/apns_connection_spec.rb +92 -0
  54. data/spec/ruby-push-notifications/apns/apns_notification_spec.rb +42 -0
  55. data/spec/ruby-push-notifications/apns/apns_pusher_spec.rb +295 -0
  56. data/spec/ruby-push-notifications/gcm/gcm_connection_spec.rb +46 -0
  57. data/spec/ruby-push-notifications/gcm/gcm_notification_spec.rb +37 -0
  58. data/spec/ruby-push-notifications/gcm/gcm_pusher_spec.rb +45 -0
  59. data/spec/ruby-push-notifications/gcm/gcm_response_spec.rb +82 -0
  60. data/spec/ruby-push-notifications/mpns/mpns_connection_spec.rb +46 -0
  61. data/spec/ruby-push-notifications/mpns/mpns_notification_spec.rb +53 -0
  62. data/spec/ruby-push-notifications/mpns/mpns_pusher_spec.rb +59 -0
  63. data/spec/ruby-push-notifications/mpns/mpns_response_spec.rb +64 -0
  64. data/spec/ruby-push-notifications/wns/wns_access_spec.rb +76 -0
  65. data/spec/ruby-push-notifications/wns/wns_connection_spec.rb +53 -0
  66. data/spec/ruby-push-notifications/wns/wns_notification_spec.rb +177 -0
  67. data/spec/ruby-push-notifications/wns/wns_pusher_spec.rb +58 -0
  68. data/spec/ruby-push-notifications/wns/wns_response_spec.rb +65 -0
  69. data/spec/spec_helper.rb +23 -0
  70. data/spec/support/dummy.pem +44 -0
  71. data/spec/support/factory_girl.rb +5 -0
  72. data/spec/support/results_shared_examples.rb +31 -0
  73. metadata +249 -0
data/spec/factories.rb ADDED
@@ -0,0 +1,17 @@
1
+ FactoryGirl.define do
2
+ sequence :apns_token do |i|
3
+ "ce8be627 2e43e855 16033e24 b4c28922 0eeda487 9c477160 b2545e95 b68b596#{i}"
4
+ end
5
+
6
+ sequence :gcm_registration_id do |i|
7
+ "APA91bHPRgkF3JUikC4ENAHEeMrd41Zxv3hVZjC9KtT8OvPVGJ-hQMRKRrZuJAEcl7B338qju59zJMjw2DELjzEvxwYv7hH5Ynpc1ODQ0aT4U4OFEeco8ohsN5PjL1iC2dNtk2BAokeMCg2ZXKqpc8FXKmhX94kIxQ#{i}"
8
+ end
9
+
10
+ sequence :mpns_device_url do |i|
11
+ "http://s.notify.live.net/u/1/bn1/HmQAAACP-0esPuxBSkzBNNXH4W0lV3lK-stEw6eRfpXX39uYbM7IwehXOTO9pRBjaaGECWOdD_7x5j5U4w4iXG4hGxer/d2luZG93c3Bob25lZGVmYXVsdA/EMDhx32Q5BG0DWnZpuVX1g/kRFAu0-jnhMQ-HG94rXzrbb0wQk#{i}"
12
+ end
13
+
14
+ sequence :wns_device_url do |i|
15
+ "https://hk2.notify.windows.com/?token=AwYAAABe%2bpjShu%2fzcVaaf1NPm%2b2dpiosm7RnmBJGMVkBDiYNXpAEp0mETldEu8axFoamgwb%2fdKCuNSfGGDHZ3RcaO2fcNGfxC6Y4Yp3xTFkDOhv5kNfgSXef7pSP0uwueIpqbWI%3d#{i}"
16
+ end
17
+ end
@@ -0,0 +1,30 @@
1
+ FactoryGirl.define do
2
+ factory :apns_notification, class: 'RubyPushNotifications::APNS::APNSNotification' do
3
+ tokens { [generate(:apns_token)] }
4
+ data a: 1
5
+
6
+ initialize_with { new tokens, data }
7
+ end
8
+
9
+ factory :gcm_notification, class: 'RubyPushNotifications::GCM::GCMNotification' do
10
+ registration_ids { [generate(:gcm_registration_id)] }
11
+ data a: 1
12
+
13
+ initialize_with { new registration_ids, data }
14
+ end
15
+
16
+ factory :mpns_notification, class: 'RubyPushNotifications::MPNS::MPNSNotification' do
17
+ device_urls { [generate(:mpns_device_url)] }
18
+ data message: { value1: 'hello' }
19
+
20
+ initialize_with { new device_urls, data }
21
+ end
22
+
23
+ factory :wns_notification, class: 'RubyPushNotifications::WNS::WNSNotification' do
24
+ device_urls { [generate(:wns_device_url)] }
25
+ data message: { value1: 'hello' }
26
+
27
+ initialize_with { new device_urls, data }
28
+ end
29
+
30
+ end
@@ -0,0 +1,92 @@
1
+
2
+ module RubyPushNotifications
3
+ module APNS
4
+ describe APNSConnection do
5
+
6
+ let(:cert) { File.read 'spec/support/dummy.pem' }
7
+ let(:tcp_socket) { instance_double(Socket).as_null_object }
8
+ let(:ssl_socket) { instance_double(OpenSSL::SSL::SSLSocket).as_null_object }
9
+
10
+ describe '::open' do
11
+ before do
12
+ allow(Socket).to receive(:tcp).with('gateway.sandbox.push.apple.com', 2195, nil, nil, { connect_timeout: 30 }).and_return tcp_socket
13
+ allow(OpenSSL::SSL::SSLSocket).to receive(:new).with(tcp_socket, an_instance_of(OpenSSL::SSL::SSLContext)).and_return ssl_socket
14
+ end
15
+
16
+ it 'creates the connection' do
17
+ expect(Socket).to receive(:tcp).with('gateway.sandbox.push.apple.com', 2195, nil, nil, { connect_timeout: 30 }).and_return tcp_socket
18
+ expect(OpenSSL::SSL::SSLSocket).to receive(:new).with(tcp_socket, an_instance_of(OpenSSL::SSL::SSLContext)).and_return ssl_socket
19
+ APNSConnection.open cert, true
20
+ end
21
+
22
+ it 'returns an instance of APNSConnection' do
23
+ expect(APNSConnection.open(cert, true)).to be_a APNSConnection
24
+ end
25
+
26
+ it 'sets the password for pem file' do
27
+ expect(OpenSSL::SSL::SSLSocket).to receive(:new).with(tcp_socket, an_instance_of(OpenSSL::SSL::SSLContext)).and_return ssl_socket
28
+ expect(OpenSSL::PKey::RSA).to receive(:new).with(cert, 'password')
29
+ expect(APNSConnection.open(cert, true, 'password')).to be_a APNSConnection
30
+ end
31
+
32
+ context 'when :host option is present' do
33
+ it 'opens a connection with a custom APNS' do
34
+ expect(Socket).to receive(:tcp).with('gateway.push.example.com', 2195, nil, nil, { connect_timeout: 30 }).and_return tcp_socket
35
+ APNSConnection.open cert, true, 'password', { host: "gateway.push.example.com" }
36
+ end
37
+ end
38
+ end
39
+
40
+ describe '#close' do
41
+ let(:connection) { APNSConnection.new tcp_socket, ssl_socket }
42
+
43
+ it 'closes the ssl socket' do
44
+ expect(ssl_socket).to receive(:close)
45
+ connection.close
46
+ end
47
+
48
+ it 'closes the tcp socket' do
49
+ expect(tcp_socket).to receive(:close)
50
+ connection.close
51
+ end
52
+ end
53
+
54
+ describe '#write' do
55
+ let(:connection) { APNSConnection.new tcp_socket, ssl_socket }
56
+ let(:contents_string) { 'the contents string' }
57
+
58
+ it 'writes the ssl socket' do
59
+ expect(ssl_socket).to receive(:write).with contents_string
60
+ connection.write contents_string
61
+ end
62
+ end
63
+
64
+ describe '#read' do
65
+ let(:connection) { APNSConnection.new tcp_socket, ssl_socket }
66
+
67
+ it 'writes the ssl socket' do
68
+ expect(ssl_socket).to receive(:read).with 6
69
+ connection.read 6
70
+ end
71
+ end
72
+
73
+ describe '#flush' do
74
+ let(:connection) { APNSConnection.new tcp_socket, ssl_socket }
75
+
76
+ it 'flushes the ssl socket' do
77
+ expect(ssl_socket).to receive :flush
78
+ connection.flush
79
+ end
80
+ end
81
+
82
+ describe 'IO behavior' do
83
+ let(:connection) { APNSConnection.new tcp_socket, ssl_socket }
84
+
85
+ it 'can be selected' do
86
+ allow(ssl_socket).to receive(:to_io).and_return IO.new(IO.sysopen('/dev/null'))
87
+ IO.select [connection]
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,42 @@
1
+
2
+ module RubyPushNotifications
3
+ module APNS
4
+ describe APNSNotification do
5
+
6
+ let(:device_tokens) { ['12', '34'] }
7
+ let(:data) { { a: 1 } }
8
+ let(:notification) { APNSNotification.new device_tokens, data }
9
+ let(:notif_id) { 1 }
10
+
11
+ it 'successfully creates the apns binary' do
12
+ json = JSON.dump data
13
+ expect do |b|
14
+ notification.each_message notif_id, &b
15
+ end.to yield_successive_args apns_binary(json, device_tokens[0], notif_id), apns_binary(json, device_tokens[1], notif_id + 1)
16
+ end
17
+
18
+ it 'caches the payload' do
19
+ expect(JSON).to receive(:dump).with(data).once.and_return 'dummy string'
20
+ allow(data).to receive(:respond_to?).with(:to_json).and_return(false)
21
+ notification.each_message(notif_id) {}
22
+ end
23
+
24
+ it 'validates the data'
25
+
26
+ it 'validates the tokens'
27
+
28
+ it_behaves_like 'a proper results manager' do
29
+ let(:success_count) { 2 }
30
+ let(:failures_count) { 1 }
31
+ let(:individual_results) do
32
+ [
33
+ NO_ERROR_STATUS_CODE,
34
+ PROCESSING_ERROR_STATUS_CODE,
35
+ NO_ERROR_STATUS_CODE
36
+ ]
37
+ end
38
+ let(:results) { APNSResults.new individual_results }
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,295 @@
1
+ module RubyPushNotifications
2
+ module APNS
3
+ describe APNSPusher do
4
+
5
+ let(:sandbox) { true }
6
+ let(:certificate) { 'abc' }
7
+ let(:pusher) { APNSPusher.new certificate, sandbox }
8
+ let(:connection) { instance_double(APNSConnection).as_null_object }
9
+ let(:data) { { a: 1 } }
10
+
11
+ before do
12
+ allow(APNSConnection).to receive(:open).with(certificate, sandbox, nil, {}).and_return connection
13
+ end
14
+
15
+ describe '#push' do
16
+
17
+ context 'a single notification' do
18
+
19
+ context 'containing a single destination' do
20
+
21
+ let(:token) { generate :apns_token }
22
+ let(:notification) { build :apns_notification, data: data, tokens: [token] }
23
+
24
+ describe 'success' do
25
+
26
+ before { allow(IO).to receive(:select).and_return [] }
27
+
28
+ it 'writes the notification to the socket' do
29
+ expect(connection).to receive(:write).with apns_binary(data, token, 0)
30
+ pusher.push [notification]
31
+ end
32
+
33
+ it 'flushes the socket contents' do
34
+ expect(connection).to receive(:flush)
35
+ pusher.push [notification]
36
+ end
37
+
38
+ describe 'results' do
39
+ before { pusher.push [notification] }
40
+
41
+ include_examples 'right results', 1, 0, [NO_ERROR_STATUS_CODE]
42
+ end
43
+ end
44
+
45
+ describe 'failure' do
46
+
47
+ before do
48
+ allow(IO).to receive(:select).and_return [[connection]]
49
+ allow(connection).to receive(:read).with(6).and_return [8, PROCESSING_ERROR_STATUS_CODE, 0].pack 'ccN'
50
+ end
51
+
52
+ it 'does not retry' do
53
+ expect(connection).to receive(:write).with(apns_binary(data, token, 0)).once
54
+ pusher.push [notification]
55
+ end
56
+
57
+ describe 'results' do
58
+ before { pusher.push [notification] }
59
+
60
+ include_examples 'right results', 0, 1, [PROCESSING_ERROR_STATUS_CODE]
61
+ end
62
+ end
63
+ end
64
+
65
+ context 'containing several destinations' do
66
+ let(:tokens) { [generate(:apns_token), generate(:apns_token)] }
67
+ let(:notification) { build :apns_notification, data: data, tokens: tokens }
68
+
69
+ describe 'success' do
70
+
71
+ before { allow(IO).to receive(:select).and_return [] }
72
+
73
+ it 'writes the messages to the socket' do
74
+ expect(connection).to receive(:write).with apns_binary(data, tokens[0], 0)
75
+ expect(connection).to receive(:write).with apns_binary(data, tokens[1], 1)
76
+ pusher.push [notification]
77
+ end
78
+
79
+ it 'flushes the socket contents' do
80
+ expect(connection).to receive(:flush).once
81
+ pusher.push [notification]
82
+ end
83
+
84
+ describe 'results' do
85
+ before { pusher.push [notification] }
86
+
87
+ include_examples 'right results', 2, 0, [NO_ERROR_STATUS_CODE] * 2
88
+ end
89
+ end
90
+
91
+ describe 'failure' do
92
+
93
+ let(:connection2) { instance_double(APNSConnection).as_null_object }
94
+
95
+ before do
96
+ allow(APNSConnection).to receive(:open).with(certificate, sandbox, nil, {}).and_return connection, connection2
97
+ end
98
+
99
+ context 'failing first' do
100
+ before do
101
+ allow(IO).to receive(:select).and_return [[connection]], []
102
+ allow(connection).to receive(:read).with(6).and_return [8, PROCESSING_ERROR_STATUS_CODE, 0].pack 'ccN'
103
+ end
104
+
105
+ it 'sends the each notification once and to the expected connection instance' do
106
+ expect(connection).to receive(:write).with(apns_binary(data, tokens[0], 0)).once
107
+ expect(connection2).to receive(:write).with(apns_binary(data, tokens[1], 1)).once
108
+ pusher.push [notification]
109
+ end
110
+
111
+ describe 'results' do
112
+ before { pusher.push [notification] }
113
+
114
+ include_examples 'right results', 1, 1, [PROCESSING_ERROR_STATUS_CODE, NO_ERROR_STATUS_CODE]
115
+ end
116
+ end
117
+
118
+ context 'failing first but delayed error' do
119
+ before do
120
+ allow(IO).to receive(:select).and_return [], [[connection]], []
121
+ allow(connection).to receive(:read).with(6).and_return [8, PROCESSING_ERROR_STATUS_CODE, 0].pack 'ccN'
122
+ end
123
+
124
+ it 'sends the second notification twice' do
125
+ expect(connection).to receive(:write).with(apns_binary(data, tokens[0], 0)).once
126
+ expect(connection).to receive(:write).with(apns_binary(data, tokens[1], 1)).once
127
+ expect(connection2).to receive(:write).with(apns_binary(data, tokens[1], 1)).once
128
+ pusher.push [notification]
129
+ end
130
+
131
+ describe 'results' do
132
+ before { pusher.push [notification] }
133
+
134
+ include_examples 'right results', 1, 1, [PROCESSING_ERROR_STATUS_CODE, NO_ERROR_STATUS_CODE]
135
+ end
136
+ end
137
+
138
+ context 'failing last' do
139
+ before do
140
+ allow(IO).to receive(:select).and_return [], [[connection]]
141
+ allow(connection).to receive(:read).with(6).and_return [8, PROCESSING_ERROR_STATUS_CODE, 1].pack 'ccN'
142
+ end
143
+
144
+ it 'sends the second notification just once' do
145
+ expect(connection).to receive(:write).with(apns_binary(data, tokens[0], 0)).once
146
+ expect(connection).to receive(:write).with(apns_binary(data, tokens[1], 1)).once
147
+ expect(connection2).to_not receive(:write)
148
+ pusher.push [notification]
149
+ end
150
+
151
+ describe 'results' do
152
+ before { pusher.push [notification] }
153
+
154
+ include_examples 'right results', 1, 1, [NO_ERROR_STATUS_CODE, PROCESSING_ERROR_STATUS_CODE]
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
160
+
161
+ context 'several notifications' do
162
+ let(:tokens) { (0...10).map { generate:apns_token } }
163
+ let(:notifications) { tokens.map { |token| build :apns_notification, data: data, tokens: [token] } }
164
+
165
+ describe 'success' do
166
+
167
+ before { allow(IO).to receive(:select).and_return [] }
168
+
169
+ it 'writes the notifications to the socket' do
170
+ notifications.each_with_index do |notification, i|
171
+ expect(connection).to receive(:write).with(apns_binary(data, tokens[i], i)).once
172
+ end
173
+ pusher.push notifications
174
+ end
175
+
176
+ it 'flushes the socket contents' do
177
+ expect(connection).to receive(:flush).once
178
+ pusher.push notifications
179
+ end
180
+
181
+ it 'saves results' do
182
+ pusher.push notifications
183
+ expect(notifications.map(&:individual_results)).to eq [[NO_ERROR_STATUS_CODE]] * 10
184
+ end
185
+ end
186
+
187
+ describe 'failure' do
188
+
189
+ context 'several async failures' do
190
+
191
+ let(:connection2) { instance_double(APNSConnection).as_null_object }
192
+ let(:connection3) { instance_double(APNSConnection).as_null_object }
193
+ let(:connection4) { instance_double(APNSConnection).as_null_object }
194
+
195
+ before do
196
+ allow(IO).to receive(:select).and_return [], [], [[connection]], [], [], [[connection2]], [], [], [], [], [], [], [[connection3]]
197
+ allow(connection).to receive(:read).with(6).and_return [8, PROCESSING_ERROR_STATUS_CODE, 0].pack('ccN')
198
+ allow(connection2).to receive(:read).with(6).and_return [8, MISSING_DEVICE_TOKEN_STATUS_CODE, 2].pack('ccN')
199
+ allow(connection3).to receive(:read).with(6).and_return [8, INVALID_TOPIC_SIZE_STATUS_CODE, 9].pack('ccN')
200
+ allow(APNSConnection).to receive(:open).with(certificate, sandbox, nil, {}).and_return connection, connection2, connection3, connection4
201
+ end
202
+
203
+ it 'reopens the connection' do
204
+ (0..2).each do |i|
205
+ expect(connection).to receive(:write).with(apns_binary(data, tokens[i], i)).once
206
+ end
207
+ expect(connection).to_not receive(:write).with apns_binary(data, tokens[3], 3)
208
+
209
+ expect(connection2).to_not receive(:write).with apns_binary(data, tokens[0], 0)
210
+ (1..3).each do |i|
211
+ expect(connection2).to receive(:write).with(apns_binary(data, tokens[i], i)).once
212
+ end
213
+ expect(connection2).to_not receive(:write).with apns_binary(data, tokens[4], 4)
214
+
215
+ expect(connection3).to_not receive(:write).with apns_binary(data, tokens[2], 2)
216
+ (3..9).each do |i|
217
+ expect(connection3).to receive(:write).with(apns_binary(data, tokens[i], i)).once
218
+ end
219
+
220
+ expect(connection4).to_not receive :write
221
+ pusher.push notifications
222
+ end
223
+
224
+ it 'saves the statuses' do
225
+ pusher.push notifications
226
+ expect(notifications.map(&:individual_results)).to eq [
227
+ [PROCESSING_ERROR_STATUS_CODE],
228
+ [NO_ERROR_STATUS_CODE],
229
+ [MISSING_DEVICE_TOKEN_STATUS_CODE],
230
+ [NO_ERROR_STATUS_CODE],
231
+ [NO_ERROR_STATUS_CODE],
232
+ [NO_ERROR_STATUS_CODE],
233
+ [NO_ERROR_STATUS_CODE],
234
+ [NO_ERROR_STATUS_CODE],
235
+ [NO_ERROR_STATUS_CODE],
236
+ [INVALID_TOPIC_SIZE_STATUS_CODE]
237
+ ]
238
+ end
239
+ end
240
+
241
+ context 'several sync failures' do
242
+
243
+ let(:connection2) { instance_double(APNSConnection).as_null_object }
244
+ let(:connection3) { instance_double(APNSConnection).as_null_object }
245
+ let(:connection4) { instance_double(APNSConnection).as_null_object }
246
+
247
+ before do
248
+ allow(IO).to receive(:select).and_return [[connection]], [], [[connection2]], [], [], [], [], [], [], [[connection3]]
249
+ allow(connection).to receive(:read).with(6).and_return [8, PROCESSING_ERROR_STATUS_CODE, 0].pack('ccN')
250
+ allow(connection2).to receive(:read).with(6).and_return [8, MISSING_DEVICE_TOKEN_STATUS_CODE, 2].pack('ccN')
251
+ allow(connection3).to receive(:read).with(6).and_return [8, INVALID_TOPIC_SIZE_STATUS_CODE, 9].pack('ccN')
252
+ allow(APNSConnection).to receive(:open).with(certificate, sandbox, nil, {}).and_return connection, connection2, connection3, connection4
253
+ end
254
+
255
+ it 'repones the connection' do
256
+ expect(connection).to receive(:write).with(apns_binary(data, tokens[0], 0)).once
257
+ expect(connection).to_not receive(:write).with apns_binary(data, tokens[1], 1)
258
+
259
+ expect(connection2).to_not receive(:write).with apns_binary(data, tokens[0], 0)
260
+ (1..2).each do |i|
261
+ expect(connection2).to receive(:write).with(apns_binary(data, tokens[i], i)).once
262
+ end
263
+ expect(connection2).to_not receive(:write).with apns_binary(data, tokens[3], 3)
264
+
265
+ expect(connection3).to_not receive(:write).with apns_binary(data, tokens[2], 2)
266
+ (3..9).each do |i|
267
+ expect(connection3).to receive(:write).with(apns_binary(data, tokens[i], i)).once
268
+ end
269
+
270
+ expect(connection4).to_not receive :write
271
+ pusher.push notifications
272
+ end
273
+
274
+ it 'saves the statuses' do
275
+ pusher.push notifications
276
+ expect(notifications.map(&:individual_results)).to eq [
277
+ [PROCESSING_ERROR_STATUS_CODE],
278
+ [NO_ERROR_STATUS_CODE],
279
+ [MISSING_DEVICE_TOKEN_STATUS_CODE],
280
+ [NO_ERROR_STATUS_CODE],
281
+ [NO_ERROR_STATUS_CODE],
282
+ [NO_ERROR_STATUS_CODE],
283
+ [NO_ERROR_STATUS_CODE],
284
+ [NO_ERROR_STATUS_CODE],
285
+ [NO_ERROR_STATUS_CODE],
286
+ [INVALID_TOPIC_SIZE_STATUS_CODE]
287
+ ]
288
+ end
289
+ end
290
+ end
291
+ end
292
+ end
293
+ end
294
+ end
295
+ end