rpush 8.0.0 → 9.0.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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -6
  3. data/README.md +8 -65
  4. data/lib/generators/templates/rpush.rb +9 -16
  5. data/lib/rpush/client/active_model.rb +0 -4
  6. data/lib/rpush/client/active_record.rb +0 -3
  7. data/lib/rpush/client/redis.rb +0 -3
  8. data/lib/rpush/configuration.rb +2 -19
  9. data/lib/rpush/daemon/service_config_methods.rb +0 -2
  10. data/lib/rpush/daemon/store/active_record.rb +2 -14
  11. data/lib/rpush/daemon/store/interface.rb +2 -2
  12. data/lib/rpush/daemon/store/redis.rb +2 -11
  13. data/lib/rpush/daemon.rb +0 -10
  14. data/lib/rpush/reflection_collection.rb +1 -2
  15. data/lib/rpush/version.rb +1 -1
  16. data/lib/rpush.rb +0 -1
  17. data/spec/functional/cli_spec.rb +41 -15
  18. data/spec/functional/embed_spec.rb +57 -26
  19. data/spec/functional/retry_spec.rb +21 -4
  20. data/spec/functional/synchronization_spec.rb +1 -1
  21. data/spec/functional_spec_helper.rb +0 -6
  22. data/spec/spec_helper.rb +2 -0
  23. data/spec/unit/client/active_record/shared/app.rb +1 -1
  24. data/spec/unit/daemon/shared/store.rb +0 -42
  25. metadata +49 -55
  26. data/lib/rpush/apns_feedback.rb +0 -18
  27. data/lib/rpush/client/active_model/gcm/app.rb +0 -19
  28. data/lib/rpush/client/active_model/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +0 -14
  29. data/lib/rpush/client/active_model/gcm/notification.rb +0 -62
  30. data/lib/rpush/client/active_record/gcm/app.rb +0 -11
  31. data/lib/rpush/client/active_record/gcm/notification.rb +0 -11
  32. data/lib/rpush/client/redis/gcm/app.rb +0 -11
  33. data/lib/rpush/client/redis/gcm/notification.rb +0 -11
  34. data/lib/rpush/daemon/apns/delivery.rb +0 -43
  35. data/lib/rpush/daemon/apns/feedback_receiver.rb +0 -91
  36. data/lib/rpush/daemon/apns.rb +0 -17
  37. data/lib/rpush/daemon/dispatcher/apns_tcp.rb +0 -152
  38. data/lib/rpush/daemon/dispatcher/tcp.rb +0 -22
  39. data/lib/rpush/daemon/gcm/delivery.rb +0 -241
  40. data/lib/rpush/daemon/gcm.rb +0 -9
  41. data/lib/rpush/daemon/tcp_connection.rb +0 -190
  42. data/spec/functional/apns_spec.rb +0 -162
  43. data/spec/functional/gcm_priority_spec.rb +0 -40
  44. data/spec/functional/gcm_spec.rb +0 -46
  45. data/spec/functional/new_app_spec.rb +0 -44
  46. data/spec/unit/apns_feedback_spec.rb +0 -39
  47. data/spec/unit/client/active_record/gcm/app_spec.rb +0 -6
  48. data/spec/unit/client/active_record/gcm/notification_spec.rb +0 -14
  49. data/spec/unit/client/redis/gcm/app_spec.rb +0 -5
  50. data/spec/unit/client/redis/gcm/notification_spec.rb +0 -5
  51. data/spec/unit/client/shared/gcm/app.rb +0 -4
  52. data/spec/unit/client/shared/gcm/notification.rb +0 -77
  53. data/spec/unit/daemon/apns/delivery_spec.rb +0 -108
  54. data/spec/unit/daemon/apns/feedback_receiver_spec.rb +0 -137
  55. data/spec/unit/daemon/dispatcher/tcp_spec.rb +0 -32
  56. data/spec/unit/daemon/gcm/delivery_spec.rb +0 -387
  57. data/spec/unit/daemon/tcp_connection_spec.rb +0 -293
@@ -1,108 +0,0 @@
1
- require 'unit_spec_helper'
2
-
3
- describe Rpush::Daemon::Apns::Delivery do
4
- let(:app) { double(name: 'MyApp') }
5
- let(:notification1) { double.as_null_object }
6
- let(:notification2) { double.as_null_object }
7
- let(:batch) { double(mark_all_failed: nil, mark_all_delivered: nil, all_processed: nil) }
8
- let(:logger) { double(error: nil, info: nil) }
9
- let(:connection) { double(select: false, write: nil, reconnect: nil, close: nil, connect: nil) }
10
- let(:delivery) { Rpush::Daemon::Apns::Delivery.new(app, connection, batch) }
11
-
12
- before do
13
- allow(batch).to receive(:each_notification) do |&blk|
14
- [notification1, notification2].each(&blk)
15
- end
16
- allow(Rpush).to receive_messages(logger: logger)
17
- end
18
-
19
- it 'writes the binary batch' do
20
- allow(notification1).to receive_messages(to_binary: 'binary1')
21
- allow(notification2).to receive_messages(to_binary: 'binary2')
22
- expect(connection).to receive(:write).with('binary1binary2')
23
- delivery.perform
24
- end
25
-
26
- it 'logs the notification deliveries' do
27
- allow(notification1).to receive_messages(id: 666, device_token: 'abc123')
28
- allow(notification2).to receive_messages(id: 42, device_token: 'abc456')
29
- expect(logger).to receive(:info).with('[MyApp] 666 sent to abc123')
30
- expect(logger).to receive(:info).with('[MyApp] 42 sent to abc456')
31
- delivery.perform
32
- end
33
-
34
- it 'marks all notifications as delivered' do
35
- expect(delivery).to receive(:mark_batch_delivered)
36
- delivery.perform
37
- end
38
-
39
- it 'notifies the batch all notifications have been processed' do
40
- expect(batch).to receive(:all_processed)
41
- delivery.perform
42
- end
43
-
44
- describe 'when an error is raised' do
45
- it 'marks all notifications as failed' do
46
- error = StandardError.new
47
- allow(connection).to receive(:write).and_raise(error)
48
- expect(delivery).to receive(:mark_batch_failed).with(error)
49
- expect { delivery.perform }.to raise_error(error)
50
- end
51
- end
52
-
53
- # describe "when delivery fails" do
54
- # before { connection.stub(select: true, read: [8, 4, 69].pack("ccN")) }
55
- #
56
- # it "marks the notification as failed" do
57
- # delivery.should_receive(:mark_failed).with(4, "Unable to deliver notification 69, received error 4 (Missing payload)")
58
- # perform
59
- # end
60
- #
61
- # it "logs the delivery error" do
62
- # # checking for the doublebed error doesn't work in jruby, but checking
63
- # # for the exception by class does.
64
- #
65
- # # error = Rpush::DeliveryError.new(4, 12, "Missing payload")
66
- # # Rpush::DeliveryError.stub(new: error)
67
- # # expect { delivery.perform }.to raise_error(error)
68
- #
69
- # expect { delivery.perform }.to raise_error(Rpush::DeliveryError)
70
- # end
71
- #
72
- # it "reads 6 bytes from the socket" do
73
- # connection.should_receive(:read).with(6).and_return(nil)
74
- # perform
75
- # end
76
- #
77
- # it "does not attempt to read from the socket if the socket was not selected for reading after the timeout" do
78
- # connection.stub(select: nil)
79
- # connection.should_not_receive(:read)
80
- # perform
81
- # end
82
- #
83
- # it "reconnects the socket" do
84
- # connection.should_receive(:reconnect)
85
- # perform
86
- # end
87
- #
88
- # it "logs that the connection is being reconnected" do
89
- # Rpush.logger.should_receive(:error).with("[MyApp] Error received, reconnecting...")
90
- # perform
91
- # end
92
- #
93
- # context "when the APNs disconnects without returning an error" do
94
- # before do
95
- # connection.stub(read: nil)
96
- # end
97
- #
98
- # it 'raises a DisconnectError error if the connection is closed without an error being returned' do
99
- # expect { delivery.perform }.to raise_error(Rpush::DisconnectionError)
100
- # end
101
- #
102
- # it 'marks the notification as failed' do
103
- # delivery.should_receive(:mark_failed).with(nil, "The APNs disconnected without returning an error. This may indicate you are using an invalid certificate for the host.")
104
- # perform
105
- # end
106
- # end
107
- # end
108
- end
@@ -1,137 +0,0 @@
1
- require 'unit_spec_helper'
2
- require 'rpush/daemon/store/active_record'
3
-
4
- describe Rpush::Daemon::Apns::FeedbackReceiver, 'check_for_feedback' do
5
- let(:host) { 'feedback.push.apple.com' }
6
- let(:port) { 2196 }
7
- let(:frequency) { 60 }
8
- let(:certificate) { double }
9
- let(:password) { double }
10
- let(:feedback_enabled) { true }
11
- let(:app) do
12
- double(
13
- name: 'my_app',
14
- password: password,
15
- certificate: certificate,
16
- feedback_enabled: feedback_enabled,
17
- environment: 'production'
18
- )
19
- end
20
- let(:connection) { double(connect: nil, read: nil, close: nil) }
21
- let(:logger) { double(error: nil, info: nil) }
22
- let(:receiver) { Rpush::Daemon::Apns::FeedbackReceiver.new(app) }
23
- let(:feedback) { double }
24
- let(:sleeper) { double(Rpush::Daemon::InterruptibleSleep, sleep: nil, stop: nil) }
25
- let(:store) { double(Rpush::Daemon::Store::ActiveRecord, create_apns_feedback: feedback, release_connection: nil) }
26
-
27
- before do
28
- Rpush.config.apns.feedback_receiver.frequency = frequency
29
- allow(Rpush::Daemon::InterruptibleSleep).to receive_messages(new: sleeper)
30
- allow(Rpush).to receive_messages(logger: logger)
31
- allow(Rpush::Daemon::TcpConnection).to receive_messages(new: connection)
32
- receiver.instance_variable_set("@stop", false)
33
- allow(Rpush::Daemon).to receive_messages(store: store)
34
- end
35
-
36
- def double_connection_read_with_tuple
37
- def connection.read(*)
38
- unless @called
39
- @called = true
40
- "N\xE3\x84\r\x00 \x83OxfU\xEB\x9F\x84aJ\x05\xAD}\x00\xAF1\xE5\xCF\xE9:\xC3\xEA\a\x8F\x1D\xA4M*N\xB0\xCE\x17"
41
- end
42
- end
43
- end
44
-
45
- it 'initializes the sleeper with the feedback polling frequency' do
46
- expect(Rpush::Daemon::InterruptibleSleep).to receive_messages(new: sleeper)
47
- Rpush::Daemon::Apns::FeedbackReceiver.new(app)
48
- end
49
-
50
- it 'instantiates a new connection' do
51
- expect(Rpush::Daemon::TcpConnection).to receive(:new).with(app, host, port)
52
- receiver.check_for_feedback
53
- end
54
-
55
- it 'connects to the feeback service' do
56
- expect(connection).to receive(:connect)
57
- receiver.check_for_feedback
58
- end
59
-
60
- it 'closes the connection' do
61
- expect(connection).to receive(:close)
62
- receiver.check_for_feedback
63
- end
64
-
65
- it 'reads from the connection' do
66
- expect(connection).to receive(:read).with(38)
67
- receiver.check_for_feedback
68
- end
69
-
70
- it 'logs the feedback' do
71
- double_connection_read_with_tuple
72
- expect(Rpush.logger).to receive(:info).with("[my_app] [FeedbackReceiver] Delivery failed at 2011-12-10 16:08:45 UTC for 834f786655eb9f84614a05ad7d00af31e5cfe93ac3ea078f1da44d2a4eb0ce17.")
73
- receiver.check_for_feedback
74
- end
75
-
76
- it 'creates the feedback' do
77
- expect(Rpush::Daemon.store).to receive(:create_apns_feedback).with(Time.at(1_323_533_325), '834f786655eb9f84614a05ad7d00af31e5cfe93ac3ea078f1da44d2a4eb0ce17', app)
78
- double_connection_read_with_tuple
79
- receiver.check_for_feedback
80
- end
81
-
82
- it 'logs errors' do
83
- error = StandardError.new('bork!')
84
- allow(connection).to receive(:read).and_raise(error)
85
- expect(Rpush.logger).to receive(:error).with(error)
86
- receiver.check_for_feedback
87
- end
88
-
89
- describe 'start' do
90
- before do
91
- allow(Thread).to receive(:new).and_yield
92
- allow(receiver).to receive(:loop).and_yield
93
- end
94
-
95
- it 'sleeps' do
96
- allow(receiver).to receive(:check_for_feedback)
97
- expect(sleeper).to receive(:sleep).at_least(:once)
98
- receiver.start
99
- end
100
-
101
- it 'checks for feedback when started' do
102
- expect(receiver).to receive(:check_for_feedback).at_least(:once)
103
- receiver.start
104
- end
105
-
106
- context 'with feedback_enabled false' do
107
- let(:feedback_enabled) { false }
108
-
109
- it 'does not check for feedback when started' do
110
- expect(receiver).not_to receive(:check_for_feedback)
111
- receiver.start
112
- end
113
- end
114
- end
115
-
116
- describe 'stop' do
117
- it 'interrupts sleep when stopped' do
118
- allow(receiver).to receive(:check_for_feedback)
119
- expect(sleeper).to receive(:stop)
120
- receiver.stop
121
- end
122
-
123
- it 'releases the store connection' do
124
- allow(Thread).to receive(:new).and_yield
125
- allow(receiver).to receive(:loop).and_yield
126
- expect(Rpush::Daemon.store).to receive(:release_connection)
127
- receiver.start
128
- receiver.stop
129
- end
130
- end
131
-
132
- it 'reflects feedback was received' do
133
- double_connection_read_with_tuple
134
- expect(receiver).to receive(:reflect).with(:apns_feedback, feedback)
135
- receiver.check_for_feedback
136
- end
137
- end
@@ -1,32 +0,0 @@
1
- require 'unit_spec_helper'
2
-
3
- describe Rpush::Daemon::Dispatcher::Tcp do
4
- let(:app) { double }
5
- let(:delivery) { double(perform: nil) }
6
- let(:delivery_class) { double(new: delivery) }
7
- let(:notification) { double }
8
- let(:batch) { double }
9
- let(:connection) { double(Rpush::Daemon::TcpConnection, connect: nil) }
10
- let(:host) { 'localhost' }
11
- let(:port) { 1234 }
12
- let(:host_proc) { proc { [host, port] } }
13
- let(:queue_payload) { Rpush::Daemon::QueuePayload.new(batch, notification) }
14
- let(:dispatcher) { Rpush::Daemon::Dispatcher::Tcp.new(app, delivery_class, host: host_proc) }
15
-
16
- before { allow(Rpush::Daemon::TcpConnection).to receive_messages(new: connection) }
17
-
18
- describe 'dispatch' do
19
- it 'delivers the notification' do
20
- expect(delivery_class).to receive(:new).with(app, connection, notification, batch).and_return(delivery)
21
- expect(delivery).to receive(:perform)
22
- dispatcher.dispatch(queue_payload)
23
- end
24
- end
25
-
26
- describe 'cleanup' do
27
- it 'closes the connection' do
28
- expect(connection).to receive(:close)
29
- dispatcher.cleanup
30
- end
31
- end
32
- end
@@ -1,387 +0,0 @@
1
- require 'unit_spec_helper'
2
-
3
- describe Rpush::Daemon::Gcm::Delivery do
4
- let(:app) { Rpush::Gcm::App.create!(name: 'MyApp', auth_key: 'abc123') }
5
- let(:notification) { Rpush::Gcm::Notification.create!(app: app, registration_ids: ['xyz'], deliver_after: Time.now) }
6
- let(:logger) { double(error: nil, info: nil, warn: nil) }
7
- let(:response) { double(code: 200, header: {}) }
8
- let(:http) { double(shutdown: nil, request: response) }
9
- let(:now) { Time.parse('2012-10-14 00:00:00') }
10
- let(:batch) { double(mark_failed: nil, mark_delivered: nil, mark_retryable: nil, notification_processed: nil) }
11
- let(:delivery) { Rpush::Daemon::Gcm::Delivery.new(app, http, notification, batch) }
12
- let(:store) { double(create_gcm_notification: double(id: 2)) }
13
-
14
- def perform
15
- delivery.perform
16
- end
17
-
18
- def perform_with_rescue
19
- expect { perform }.to raise_error(StandardError)
20
- end
21
-
22
- before do
23
- allow(delivery).to receive_messages(reflect: nil)
24
- allow(Rpush::Daemon).to receive_messages(store: store)
25
- allow(Time).to receive_messages(now: now)
26
- allow(Rpush).to receive_messages(logger: logger)
27
- end
28
-
29
- shared_examples_for 'a notification with some delivery failures' do
30
- let(:new_notification) { Rpush::Gcm::Notification.where('id != ?', notification.id).first }
31
-
32
- before { allow(response).to receive_messages(body: JSON.dump(body)) }
33
-
34
- it 'marks the original notification as failed' do
35
- # error = Rpush::DeliveryError.new(nil, notification.id, error_description)
36
- expect(delivery).to receive(:mark_failed) do |error|
37
- expect(error.to_s).to match(error_description)
38
- end
39
- perform_with_rescue
40
- end
41
-
42
- it 'creates a new notification for the unavailable devices' do
43
- notification.update(registration_ids: %w(id_0 id_1 id_2), data: { 'one' => 1 }, collapse_key: 'thing', delay_while_idle: true)
44
- allow(response).to receive_messages(header: { 'retry-after' => 10 })
45
- attrs = { 'collapse_key' => 'thing', 'delay_while_idle' => true, 'app_id' => app.id }
46
- expect(store).to receive(:create_gcm_notification).with(attrs, notification.data,
47
- %w(id_0 id_2), now + 10.seconds, notification.app)
48
- perform_with_rescue
49
- end
50
-
51
- it 'raises a DeliveryError' do
52
- expect { perform }.to raise_error(Rpush::DeliveryError)
53
- end
54
- end
55
-
56
- describe 'a 200 response' do
57
- before do
58
- allow(response).to receive_messages(code: 200)
59
- end
60
-
61
- it 'reflects on any IDs which successfully received the notification' do
62
- body = {
63
- 'failure' => 1,
64
- 'success' => 1,
65
- 'results' => [
66
- { 'message_id' => '1:000' },
67
- { 'error' => 'Err' }
68
- ]
69
- }
70
-
71
- allow(response).to receive_messages(body: JSON.dump(body))
72
- allow(notification).to receive_messages(registration_ids: %w(1 2))
73
- expect(delivery).to receive(:reflect).with(:gcm_delivered_to_recipient, notification, '1')
74
- expect(delivery).not_to receive(:reflect).with(:gcm_delivered_to_recipient, notification, '2')
75
- perform_with_rescue
76
- end
77
-
78
- it 'reflects on any IDs which failed to receive the notification' do
79
- body = {
80
- 'failure' => 1,
81
- 'success' => 1,
82
- 'results' => [
83
- { 'error' => 'Err' },
84
- { 'message_id' => '1:000' }
85
- ]
86
- }
87
-
88
- allow(response).to receive_messages(body: JSON.dump(body))
89
- allow(notification).to receive_messages(registration_ids: %w(1 2))
90
- expect(delivery).to receive(:reflect).with(:gcm_failed_to_recipient, notification, 'Err', '1')
91
- expect(delivery).not_to receive(:reflect).with(:gcm_failed_to_recipient, notification, anything, '2')
92
- perform_with_rescue
93
- end
94
-
95
- it 'reflects on canonical IDs' do
96
- body = {
97
- 'failure' => 0,
98
- 'success' => 3,
99
- 'canonical_ids' => 1,
100
- 'results' => [
101
- { 'message_id' => '1:000' },
102
- { 'message_id' => '1:000', 'registration_id' => 'canonical123' },
103
- { 'message_id' => '1:000' }
104
- ] }
105
-
106
- allow(response).to receive_messages(body: JSON.dump(body))
107
- allow(notification).to receive_messages(registration_ids: %w(1 2 3))
108
- expect(delivery).to receive(:reflect).with(:gcm_canonical_id, '2', 'canonical123')
109
- perform
110
- end
111
-
112
- it 'reflects on invalid IDs' do
113
- body = {
114
- 'failure' => 1,
115
- 'success' => 2,
116
- 'canonical_ids' => 0,
117
- 'results' => [
118
- { 'message_id' => '1:000' },
119
- { 'error' => 'NotRegistered' },
120
- { 'message_id' => '1:000' }
121
- ]
122
- }
123
-
124
- allow(response).to receive_messages(body: JSON.dump(body))
125
- allow(notification).to receive_messages(registration_ids: %w(1 2 3))
126
- expect(delivery).to receive(:reflect).with(:gcm_invalid_registration_id, app, 'NotRegistered', '2')
127
- perform_with_rescue
128
- end
129
-
130
- describe 'when delivered successfully to all devices' do
131
- let(:body) do
132
- {
133
- 'failure' => 0,
134
- 'success' => 1,
135
- 'results' => [{ 'message_id' => '1:000' }]
136
- }
137
- end
138
-
139
- before { allow(response).to receive_messages(body: JSON.dump(body)) }
140
-
141
- it 'marks the notification as delivered' do
142
- expect(delivery).to receive(:mark_delivered)
143
- perform
144
- end
145
-
146
- it 'logs that the notification was delivered' do
147
- expect(logger).to receive(:info).with("[MyApp] #{notification.id} sent to xyz")
148
- perform
149
- end
150
- end
151
-
152
- it 'marks a notification as failed if any ids are invalid' do
153
- body = {
154
- 'failure' => 1,
155
- 'success' => 2,
156
- 'canonical_ids' => 0,
157
- 'results' => [
158
- { 'message_id' => '1:000' },
159
- { 'error' => 'NotRegistered' },
160
- { 'message_id' => '1:000' }
161
- ]
162
- }
163
-
164
- allow(response).to receive_messages(body: JSON.dump(body))
165
- expect(delivery).to receive(:mark_failed)
166
- expect(delivery).not_to receive(:mark_retryable)
167
- expect(store).not_to receive(:create_gcm_notification)
168
- perform_with_rescue
169
- end
170
-
171
- it 'marks a notification as failed if any deliveries failed that cannot be retried' do
172
- body = {
173
- 'failure' => 1,
174
- 'success' => 1,
175
- 'results' => [
176
- { 'message_id' => '1:000' },
177
- { 'error' => 'InvalidDataKey' }
178
- ] }
179
- allow(response).to receive_messages(body: JSON.dump(body))
180
- error = Rpush::DeliveryError.new(nil, notification.id, 'Failed to deliver to all recipients. Errors: InvalidDataKey.')
181
- expect(delivery).to receive(:mark_failed).with(error)
182
- perform_with_rescue
183
- end
184
-
185
- describe 'all deliveries failed with Unavailable or InternalServerError' do
186
- let(:body) do
187
- {
188
- 'failure' => 2,
189
- 'success' => 0,
190
- 'results' => [
191
- { 'error' => 'Unavailable' },
192
- { 'error' => 'Unavailable' }
193
- ]
194
- }
195
- end
196
-
197
- before do
198
- allow(response).to receive_messages(body: JSON.dump(body))
199
- allow(notification).to receive_messages(registration_ids: %w(1 2))
200
- end
201
-
202
- it 'retries the notification respecting the Retry-After header' do
203
- allow(response).to receive_messages(header: { 'retry-after' => 10 })
204
- expect(delivery).to receive(:mark_retryable).with(notification, now + 10.seconds)
205
- perform
206
- end
207
-
208
- it 'retries the notification using exponential back-off if the Retry-After header is not present' do
209
- expect(delivery).to receive(:mark_retryable).with(notification, now + 2)
210
- perform
211
- end
212
-
213
- it 'does not mark the notification as failed' do
214
- expect(delivery).not_to receive(:mark_failed)
215
- perform
216
- end
217
-
218
- it 'logs that the notification will be retried' do
219
- notification.retries = 1
220
- notification.deliver_after = now + 2
221
- expect(Rpush.logger).to receive(:warn).with("[MyApp] All recipients unavailable. Notification #{notification.id} will be retried after 2012-10-14 00:00:02 (retry 1).")
222
- perform
223
- end
224
- end
225
-
226
- describe 'all deliveries failed with some as Unavailable or InternalServerError' do
227
- let(:body) do
228
- { 'failure' => 3,
229
- 'success' => 0,
230
- 'results' => [
231
- { 'error' => 'Unavailable' },
232
- { 'error' => 'InvalidDataKey' },
233
- { 'error' => 'Unavailable' }
234
- ]
235
- }
236
- end
237
- let(:error_description) { /#{Regexp.escape("Failed to deliver to recipients 0, 1, 2. Errors: Unavailable, InvalidDataKey, Unavailable. 0, 2 will be retried as notification")} [\d]+\./ }
238
- it_should_behave_like 'a notification with some delivery failures'
239
- end
240
-
241
- describe 'some deliveries failed with Unavailable or InternalServerError' do
242
- let(:body) do
243
- { 'failure' => 2,
244
- 'success' => 1,
245
- 'results' => [
246
- { 'error' => 'Unavailable' },
247
- { 'message_id' => '1:000' },
248
- { 'error' => 'InternalServerError' }
249
- ]
250
- }
251
- end
252
- let(:error_description) { /#{Regexp.escape("Failed to deliver to recipients 0, 2. Errors: Unavailable, InternalServerError. 0, 2 will be retried as notification")} [\d]+\./ }
253
- it_should_behave_like 'a notification with some delivery failures'
254
- end
255
- end
256
-
257
- describe 'a 503 response' do
258
- before { allow(response).to receive_messages(code: 503) }
259
-
260
- it 'logs a warning that the notification will be retried.' do
261
- notification.retries = 1
262
- notification.deliver_after = now + 2
263
- expect(logger).to receive(:warn).with("[MyApp] GCM responded with an Service Unavailable Error. Notification #{notification.id} will be retried after 2012-10-14 00:00:02 (retry 1).")
264
- perform
265
- end
266
-
267
- it 'respects an integer Retry-After header' do
268
- allow(response).to receive_messages(header: { 'retry-after' => 10 })
269
- expect(delivery).to receive(:mark_retryable).with(notification, now + 10.seconds)
270
- perform
271
- end
272
-
273
- it 'respects a HTTP-date Retry-After header' do
274
- allow(response).to receive_messages(header: { 'retry-after' => 'Wed, 03 Oct 2012 20:55:11 GMT' })
275
- expect(delivery).to receive(:mark_retryable).with(notification, Time.parse('Wed, 03 Oct 2012 20:55:11 GMT'))
276
- perform
277
- end
278
-
279
- it 'defaults to exponential back-off if the Retry-After header is not present' do
280
- expect(delivery).to receive(:mark_retryable).with(notification, now + 2**1)
281
- perform
282
- end
283
- end
284
-
285
- describe 'a 502 response' do
286
- before { allow(response).to receive_messages(code: 502) }
287
-
288
- it 'logs a warning that the notification will be retried.' do
289
- notification.retries = 1
290
- notification.deliver_after = now + 2
291
- expect(logger).to receive(:warn).with("[MyApp] GCM responded with a Bad Gateway Error. Notification #{notification.id} will be retried after 2012-10-14 00:00:02 (retry 1).")
292
- perform
293
- end
294
-
295
- it 'respects an integer Retry-After header' do
296
- allow(response).to receive_messages(header: { 'retry-after' => 10 })
297
- expect(delivery).to receive(:mark_retryable).with(notification, now + 10.seconds)
298
- perform
299
- end
300
-
301
- it 'respects a HTTP-date Retry-After header' do
302
- allow(response).to receive_messages(header: { 'retry-after' => 'Wed, 03 Oct 2012 20:55:11 GMT' })
303
- expect(delivery).to receive(:mark_retryable).with(notification, Time.parse('Wed, 03 Oct 2012 20:55:11 GMT'))
304
- perform
305
- end
306
-
307
- it 'defaults to exponential back-off if the Retry-After header is not present' do
308
- expect(delivery).to receive(:mark_retryable).with(notification, now + 2**1)
309
- perform
310
- end
311
- end
312
-
313
- describe 'a 500 response' do
314
- before do
315
- notification.update_attribute(:retries, 2)
316
- allow(response).to receive_messages(code: 500)
317
- end
318
-
319
- it 'logs a warning that the notification has been re-queued.' do
320
- notification.retries = 3
321
- notification.deliver_after = now + 2**3
322
- expect(Rpush.logger).to receive(:warn).with("[MyApp] GCM responded with an Internal Error. Notification #{notification.id} will be retried after #{(now + 2**3).strftime('%Y-%m-%d %H:%M:%S')} (retry 3).")
323
- perform
324
- end
325
-
326
- it 'retries the notification in accordance with the exponential back-off strategy.' do
327
- expect(delivery).to receive(:mark_retryable).with(notification, now + 2**3)
328
- perform
329
- end
330
- end
331
-
332
- describe 'a 5xx response' do
333
- before { allow(response).to receive_messages(code: 555) }
334
-
335
- it 'logs a warning that the notification will be retried.' do
336
- notification.retries = 1
337
- notification.deliver_after = now + 2
338
- expect(logger).to receive(:warn).with("[MyApp] GCM responded with a 5xx Error. Notification #{notification.id} will be retried after 2012-10-14 00:00:02 (retry 1).")
339
- perform
340
- end
341
-
342
- it 'respects an integer Retry-After header' do
343
- allow(response).to receive_messages(header: { 'retry-after' => 10 })
344
- expect(delivery).to receive(:mark_retryable).with(notification, now + 10.seconds)
345
- perform
346
- end
347
-
348
- it 'respects a HTTP-date Retry-After header' do
349
- allow(response).to receive_messages(header: { 'retry-after' => 'Wed, 03 Oct 2012 20:55:11 GMT' })
350
- expect(delivery).to receive(:mark_retryable).with(notification, Time.parse('Wed, 03 Oct 2012 20:55:11 GMT'))
351
- perform
352
- end
353
-
354
- it 'defaults to exponential back-off if the Retry-After header is not present' do
355
- expect(delivery).to receive(:mark_retryable).with(notification, now + 2**1)
356
- perform
357
- end
358
- end
359
-
360
- describe 'a 401 response' do
361
- before { allow(response).to receive_messages(code: 401) }
362
-
363
- it 'raises an error' do
364
- expect { perform }.to raise_error(Rpush::DeliveryError)
365
- end
366
- end
367
-
368
- describe 'a 400 response' do
369
- before { allow(response).to receive_messages(code: 400) }
370
-
371
- it 'marks the notification as failed' do
372
- error = Rpush::DeliveryError.new(400, notification.id, 'GCM failed to parse the JSON request. Possibly an Rpush bug, please open an issue.')
373
- expect(delivery).to receive(:mark_failed).with(error)
374
- perform_with_rescue
375
- end
376
- end
377
-
378
- describe 'an un-handled response' do
379
- before { allow(response).to receive_messages(code: 418) }
380
-
381
- it 'marks the notification as failed' do
382
- error = Rpush::DeliveryError.new(418, notification.id, "I'm a Teapot")
383
- expect(delivery).to receive(:mark_failed).with(error)
384
- perform_with_rescue
385
- end
386
- end
387
- end