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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -6
- data/README.md +8 -65
- data/lib/generators/templates/rpush.rb +9 -16
- data/lib/rpush/client/active_model.rb +0 -4
- data/lib/rpush/client/active_record.rb +0 -3
- data/lib/rpush/client/redis.rb +0 -3
- data/lib/rpush/configuration.rb +2 -19
- data/lib/rpush/daemon/service_config_methods.rb +0 -2
- data/lib/rpush/daemon/store/active_record.rb +2 -14
- data/lib/rpush/daemon/store/interface.rb +2 -2
- data/lib/rpush/daemon/store/redis.rb +2 -11
- data/lib/rpush/daemon.rb +0 -10
- data/lib/rpush/reflection_collection.rb +1 -2
- data/lib/rpush/version.rb +1 -1
- data/lib/rpush.rb +0 -1
- data/spec/functional/cli_spec.rb +41 -15
- data/spec/functional/embed_spec.rb +57 -26
- data/spec/functional/retry_spec.rb +21 -4
- data/spec/functional/synchronization_spec.rb +1 -1
- data/spec/functional_spec_helper.rb +0 -6
- data/spec/spec_helper.rb +2 -0
- data/spec/unit/client/active_record/shared/app.rb +1 -1
- data/spec/unit/daemon/shared/store.rb +0 -42
- metadata +49 -55
- data/lib/rpush/apns_feedback.rb +0 -18
- data/lib/rpush/client/active_model/gcm/app.rb +0 -19
- data/lib/rpush/client/active_model/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +0 -14
- data/lib/rpush/client/active_model/gcm/notification.rb +0 -62
- data/lib/rpush/client/active_record/gcm/app.rb +0 -11
- data/lib/rpush/client/active_record/gcm/notification.rb +0 -11
- data/lib/rpush/client/redis/gcm/app.rb +0 -11
- data/lib/rpush/client/redis/gcm/notification.rb +0 -11
- data/lib/rpush/daemon/apns/delivery.rb +0 -43
- data/lib/rpush/daemon/apns/feedback_receiver.rb +0 -91
- data/lib/rpush/daemon/apns.rb +0 -17
- data/lib/rpush/daemon/dispatcher/apns_tcp.rb +0 -152
- data/lib/rpush/daemon/dispatcher/tcp.rb +0 -22
- data/lib/rpush/daemon/gcm/delivery.rb +0 -241
- data/lib/rpush/daemon/gcm.rb +0 -9
- data/lib/rpush/daemon/tcp_connection.rb +0 -190
- data/spec/functional/apns_spec.rb +0 -162
- data/spec/functional/gcm_priority_spec.rb +0 -40
- data/spec/functional/gcm_spec.rb +0 -46
- data/spec/functional/new_app_spec.rb +0 -44
- data/spec/unit/apns_feedback_spec.rb +0 -39
- data/spec/unit/client/active_record/gcm/app_spec.rb +0 -6
- data/spec/unit/client/active_record/gcm/notification_spec.rb +0 -14
- data/spec/unit/client/redis/gcm/app_spec.rb +0 -5
- data/spec/unit/client/redis/gcm/notification_spec.rb +0 -5
- data/spec/unit/client/shared/gcm/app.rb +0 -4
- data/spec/unit/client/shared/gcm/notification.rb +0 -77
- data/spec/unit/daemon/apns/delivery_spec.rb +0 -108
- data/spec/unit/daemon/apns/feedback_receiver_spec.rb +0 -137
- data/spec/unit/daemon/dispatcher/tcp_spec.rb +0 -32
- data/spec/unit/daemon/gcm/delivery_spec.rb +0 -387
- 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
|