rpush 7.0.1 → 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 +34 -4
- data/README.md +24 -44
- data/lib/generators/rpush_migration_generator.rb +1 -0
- data/lib/generators/templates/rpush.rb +23 -13
- data/lib/generators/templates/rpush_7_1_0_updates.rb +12 -0
- data/lib/rpush/client/active_model/apns/notification.rb +0 -4
- data/lib/rpush/client/active_model/{gcm → fcm}/app.rb +4 -3
- data/lib/rpush/client/active_model/{gcm → fcm}/expiry_collapse_key_mutual_inclusion_validator.rb +1 -1
- data/lib/rpush/client/active_model/fcm/notification.rb +129 -0
- data/lib/rpush/client/active_model/fcm/notification_keys_in_allowed_list_validator.rb +20 -0
- data/lib/rpush/client/active_model.rb +4 -3
- data/lib/rpush/client/active_record/{gcm → fcm}/app.rb +2 -2
- data/lib/rpush/client/active_record/{gcm → fcm}/notification.rb +2 -2
- data/lib/rpush/client/active_record.rb +2 -2
- data/lib/rpush/client/redis/app.rb +2 -0
- data/lib/rpush/client/redis/{gcm → fcm}/app.rb +2 -2
- data/lib/rpush/client/redis/{gcm → fcm}/notification.rb +2 -2
- data/lib/rpush/client/redis.rb +2 -2
- data/lib/rpush/configuration.rb +2 -19
- data/lib/rpush/daemon/apns2/delivery.rb +0 -1
- data/lib/rpush/daemon/apnsp8/delivery.rb +0 -1
- data/lib/rpush/daemon/fcm/delivery.rb +162 -0
- data/lib/rpush/daemon/{gcm.rb → fcm.rb} +1 -1
- data/lib/rpush/daemon/google_credential_cache.rb +41 -0
- data/lib/rpush/daemon/service_config_methods.rb +0 -2
- data/lib/rpush/daemon/store/active_record.rb +15 -12
- data/lib/rpush/daemon/store/interface.rb +3 -3
- data/lib/rpush/daemon/store/redis.rb +13 -9
- data/lib/rpush/daemon/webpush/delivery.rb +2 -2
- data/lib/rpush/daemon.rb +3 -9
- data/lib/rpush/reflection_collection.rb +3 -3
- data/lib/rpush/version.rb +2 -2
- data/lib/rpush.rb +1 -1
- data/spec/functional/apns2_spec.rb +2 -6
- data/spec/functional/cli_spec.rb +41 -15
- data/spec/functional/embed_spec.rb +57 -26
- data/spec/functional/{gcm_priority_spec.rb → fcm_priority_spec.rb} +13 -7
- data/spec/functional/fcm_spec.rb +77 -0
- data/spec/functional/retry_spec.rb +21 -4
- data/spec/functional/synchronization_spec.rb +1 -1
- data/spec/functional_spec_helper.rb +1 -7
- data/spec/spec_helper.rb +4 -1
- data/spec/support/active_record_setup.rb +3 -1
- data/spec/unit/client/active_record/{gcm → fcm}/app_spec.rb +2 -2
- data/spec/unit/client/active_record/fcm/notification_spec.rb +10 -0
- data/spec/unit/client/active_record/shared/app.rb +1 -1
- data/spec/unit/client/redis/fcm/app_spec.rb +5 -0
- data/spec/unit/client/redis/fcm/notification_spec.rb +5 -0
- data/spec/unit/client/shared/apns/notification.rb +0 -15
- data/spec/unit/client/shared/fcm/app.rb +4 -0
- data/spec/unit/client/shared/fcm/notification.rb +92 -0
- data/spec/unit/configuration_spec.rb +1 -1
- data/spec/unit/daemon/apnsp8/delivery_spec.rb +1 -1
- data/spec/unit/daemon/fcm/delivery_spec.rb +127 -0
- data/spec/unit/daemon/service_config_methods_spec.rb +1 -1
- data/spec/unit/daemon/shared/store.rb +0 -42
- data/spec/unit/daemon/wns/delivery_spec.rb +1 -1
- data/spec/unit/logger_spec.rb +1 -1
- data/spec/unit_spec_helper.rb +1 -1
- metadata +127 -69
- data/lib/rpush/apns_feedback.rb +0 -18
- data/lib/rpush/client/active_model/gcm/notification.rb +0 -62
- 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/tcp_connection.rb +0 -190
- data/spec/functional/apns_spec.rb +0 -162
- 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/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 -292
@@ -1,162 +0,0 @@
|
|
1
|
-
require 'functional_spec_helper'
|
2
|
-
|
3
|
-
describe 'APNs' do
|
4
|
-
let(:app) { create_app }
|
5
|
-
let(:tcp_socket) { double(TCPSocket, setsockopt: nil, close: nil) }
|
6
|
-
let(:ssl_socket) { double(OpenSSL::SSL::SSLSocket, :sync= => nil, connect: nil, write: nil, flush: nil, read: nil, close: nil) }
|
7
|
-
let(:io_double) { double(select: nil) }
|
8
|
-
let(:delivered_ids) { [] }
|
9
|
-
let(:failed_ids) { [] }
|
10
|
-
let(:retry_ids) { [] }
|
11
|
-
|
12
|
-
before do
|
13
|
-
Rpush.config.push_poll = 0.5
|
14
|
-
stub_tcp_connection(tcp_socket, ssl_socket, io_double)
|
15
|
-
end
|
16
|
-
|
17
|
-
def create_app
|
18
|
-
app = Rpush::Apns::App.new
|
19
|
-
app.certificate = TEST_CERT
|
20
|
-
app.name = 'test'
|
21
|
-
app.environment = 'sandbox'
|
22
|
-
app.save!
|
23
|
-
app
|
24
|
-
end
|
25
|
-
|
26
|
-
def create_notification
|
27
|
-
notification = Rpush::Apns::Notification.new
|
28
|
-
notification.app = app
|
29
|
-
notification.alert = 'test'
|
30
|
-
notification.device_token = 'a' * 108
|
31
|
-
notification.save!
|
32
|
-
notification
|
33
|
-
end
|
34
|
-
|
35
|
-
def wait
|
36
|
-
sleep 0.1
|
37
|
-
end
|
38
|
-
|
39
|
-
def wait_for_notification_to_deliver(notification)
|
40
|
-
timeout { wait until delivered_ids.include?(notification.id) }
|
41
|
-
end
|
42
|
-
|
43
|
-
def wait_for_notification_to_fail(notification)
|
44
|
-
timeout { wait until failed_ids.include?(notification.id) }
|
45
|
-
end
|
46
|
-
|
47
|
-
def wait_for_notification_to_retry(notification)
|
48
|
-
timeout { wait until retry_ids.include?(notification.id) }
|
49
|
-
end
|
50
|
-
|
51
|
-
def fail_notification(notification)
|
52
|
-
allow(ssl_socket).to receive_messages(read: [8, 4, notification.id].pack('ccN'))
|
53
|
-
enable_io_select
|
54
|
-
end
|
55
|
-
|
56
|
-
def enable_io_select
|
57
|
-
called = false
|
58
|
-
allow(io_double).to receive(:select) do
|
59
|
-
if called
|
60
|
-
nil
|
61
|
-
else
|
62
|
-
called = true
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
it 'delivers a notification successfully' do
|
68
|
-
notification = create_notification
|
69
|
-
expect do
|
70
|
-
Rpush.push
|
71
|
-
notification.reload
|
72
|
-
end.to change(notification, :delivered).to(true)
|
73
|
-
end
|
74
|
-
|
75
|
-
it 'receives feedback' do
|
76
|
-
app
|
77
|
-
tuple = "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"
|
78
|
-
allow(ssl_socket).to receive(:read).and_return(tuple, nil)
|
79
|
-
Rpush.apns_feedback
|
80
|
-
feedback = Rpush::Apns::Feedback.all.first
|
81
|
-
expect(feedback).not_to be_nil
|
82
|
-
expect(feedback.app_id).to eq(app.id)
|
83
|
-
expect(feedback.device_token).to eq('834f786655eb9f84614a05ad7d00af31e5cfe93ac3ea078f1da44d2a4eb0ce17')
|
84
|
-
end
|
85
|
-
|
86
|
-
describe 'delivery failures' do
|
87
|
-
before do
|
88
|
-
Rpush.reflect do |on|
|
89
|
-
on.notification_delivered do |n|
|
90
|
-
delivered_ids << n.id
|
91
|
-
end
|
92
|
-
|
93
|
-
on.notification_id_failed do |_, n_id|
|
94
|
-
failed_ids << n_id
|
95
|
-
end
|
96
|
-
|
97
|
-
on.notification_id_will_retry do |_, n_id|
|
98
|
-
retry_ids << n_id
|
99
|
-
end
|
100
|
-
|
101
|
-
on.notification_will_retry do |n|
|
102
|
-
retry_ids << n.id
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
Rpush.embed
|
107
|
-
end
|
108
|
-
|
109
|
-
after do
|
110
|
-
Rpush.reflection_stack.clear
|
111
|
-
Rpush.reflection_stack.push(Rpush::ReflectionCollection.new)
|
112
|
-
|
113
|
-
timeout { Rpush.shutdown }
|
114
|
-
end
|
115
|
-
|
116
|
-
it 'fails to deliver a notification' do
|
117
|
-
notification = create_notification
|
118
|
-
wait_for_notification_to_deliver(notification)
|
119
|
-
fail_notification(notification)
|
120
|
-
wait_for_notification_to_fail(notification)
|
121
|
-
end
|
122
|
-
|
123
|
-
describe 'with a failed connection' do
|
124
|
-
it 'retries all notifications' do
|
125
|
-
allow_any_instance_of(Rpush::Daemon::TcpConnection).to receive_messages(sleep: nil)
|
126
|
-
expect(ssl_socket).to receive(:write).at_least(1).times.and_raise(Errno::EPIPE)
|
127
|
-
notifications = 2.times.map { create_notification }
|
128
|
-
notifications.each { |n| wait_for_notification_to_retry(n) }
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
describe 'with multiple notifications' do
|
133
|
-
let(:notification1) { create_notification }
|
134
|
-
let(:notification2) { create_notification }
|
135
|
-
let(:notification3) { create_notification }
|
136
|
-
let(:notification4) { create_notification }
|
137
|
-
let(:notifications) { [notification1, notification2, notification3, notification4] }
|
138
|
-
|
139
|
-
it 'marks the correct notification as failed' do
|
140
|
-
notifications.each { |n| wait_for_notification_to_deliver(n) }
|
141
|
-
fail_notification(notification2)
|
142
|
-
wait_for_notification_to_fail(notification2)
|
143
|
-
end
|
144
|
-
|
145
|
-
it 'does not mark prior notifications as failed' do
|
146
|
-
notifications.each { |n| wait_for_notification_to_deliver(n) }
|
147
|
-
fail_notification(notification2)
|
148
|
-
wait_for_notification_to_fail(notification2)
|
149
|
-
|
150
|
-
expect(failed_ids).to_not include(notification1.id)
|
151
|
-
notification1.reload
|
152
|
-
expect(notification1.delivered).to eq(true)
|
153
|
-
end
|
154
|
-
|
155
|
-
it 'marks notifications following the failed one as retryable' do
|
156
|
-
notifications.each { |n| wait_for_notification_to_deliver(n) }
|
157
|
-
fail_notification(notification2)
|
158
|
-
[notification3, notification4].each { |n| wait_for_notification_to_retry(n) }
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
data/spec/functional/gcm_spec.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
require 'functional_spec_helper'
|
2
|
-
|
3
|
-
describe 'GCM' do
|
4
|
-
let(:app) { Rpush::Gcm::App.new }
|
5
|
-
let(:notification) { Rpush::Gcm::Notification.new }
|
6
|
-
let(:response) { double(Net::HTTPResponse, code: 200) }
|
7
|
-
let(:http) { double(Net::HTTP::Persistent, request: response, shutdown: nil) }
|
8
|
-
|
9
|
-
before do
|
10
|
-
app.name = 'test'
|
11
|
-
app.auth_key = 'abc123'
|
12
|
-
app.save!
|
13
|
-
|
14
|
-
notification.app_id = app.id
|
15
|
-
notification.registration_ids = ['foo']
|
16
|
-
notification.data = { message: 'test' }
|
17
|
-
notification.save!
|
18
|
-
|
19
|
-
allow(Net::HTTP::Persistent).to receive_messages(new: http)
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'delivers a notification successfully' do
|
23
|
-
allow(response).to receive_messages(body: JSON.dump(results: [{ message_id: notification.registration_ids.first.to_s }]))
|
24
|
-
|
25
|
-
expect do
|
26
|
-
Rpush.push
|
27
|
-
notification.reload
|
28
|
-
end.to change(notification, :delivered).to(true)
|
29
|
-
end
|
30
|
-
|
31
|
-
it 'fails to deliver a notification successfully' do
|
32
|
-
allow(response).to receive_messages(body: JSON.dump(results: [{ error: 'Err' }]))
|
33
|
-
Rpush.push
|
34
|
-
notification.reload
|
35
|
-
expect(notification.delivered).to eq(false)
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'retries notification that fail due to a SocketError' do
|
39
|
-
expect(http).to receive(:request).and_raise(SocketError.new)
|
40
|
-
expect(notification.deliver_after).to be_nil
|
41
|
-
expect do
|
42
|
-
Rpush.push
|
43
|
-
notification.reload
|
44
|
-
end.to change(notification, :deliver_after).to(kind_of(Time))
|
45
|
-
end
|
46
|
-
end
|
@@ -1,44 +0,0 @@
|
|
1
|
-
require 'functional_spec_helper'
|
2
|
-
|
3
|
-
describe 'New app loading' do
|
4
|
-
let(:timeout) { 10 }
|
5
|
-
let(:app) { create_app }
|
6
|
-
let(:tcp_socket) { double(TCPSocket, setsockopt: nil, close: nil) }
|
7
|
-
let(:ssl_socket) { double(OpenSSL::SSL::SSLSocket, :sync= => nil, connect: nil, write: nil, flush: nil, read: nil, close: nil) }
|
8
|
-
let(:io_double) { double(select: nil) }
|
9
|
-
|
10
|
-
before do
|
11
|
-
stub_tcp_connection
|
12
|
-
end
|
13
|
-
|
14
|
-
def create_app
|
15
|
-
app = Rpush::Apns::App.new
|
16
|
-
app.certificate = TEST_CERT
|
17
|
-
app.name = 'test'
|
18
|
-
app.environment = 'sandbox'
|
19
|
-
app.save!
|
20
|
-
app
|
21
|
-
end
|
22
|
-
|
23
|
-
def create_notification
|
24
|
-
notification = Rpush::Apns::Notification.new
|
25
|
-
notification.app = app
|
26
|
-
notification.alert = 'test'
|
27
|
-
notification.device_token = 'a' * 108
|
28
|
-
notification.save!
|
29
|
-
notification
|
30
|
-
end
|
31
|
-
|
32
|
-
def stub_tcp_connection
|
33
|
-
allow_any_instance_of(Rpush::Daemon::TcpConnection).to receive_messages(connect_socket: [tcp_socket, ssl_socket])
|
34
|
-
allow_any_instance_of(Rpush::Daemon::TcpConnection).to receive_messages(setup_ssl_context: double.as_null_object)
|
35
|
-
stub_const('Rpush::Daemon::TcpConnection::IO', io_double)
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'delivers a notification successfully' do
|
39
|
-
notification = create_notification
|
40
|
-
Rpush.push
|
41
|
-
notification.reload
|
42
|
-
expect(notification.delivered).to eq(true)
|
43
|
-
end
|
44
|
-
end
|
@@ -1,39 +0,0 @@
|
|
1
|
-
require 'unit_spec_helper'
|
2
|
-
|
3
|
-
describe Rpush, 'apns_feedback' do
|
4
|
-
let!(:apns_app) { Rpush::Apns::App.create!(apns_app_params) }
|
5
|
-
let(:apns_app_params) do
|
6
|
-
{
|
7
|
-
name: 'test',
|
8
|
-
environment: 'production',
|
9
|
-
certificate: TEST_CERT
|
10
|
-
}
|
11
|
-
end
|
12
|
-
let!(:gcm_app) { Rpush::Gcm::App.create!(name: 'MyApp', auth_key: 'abc123') }
|
13
|
-
|
14
|
-
let(:receiver) { double(check_for_feedback: nil) }
|
15
|
-
|
16
|
-
before do
|
17
|
-
allow(Rpush::Daemon::Apns::FeedbackReceiver).to receive(:new) { receiver }
|
18
|
-
end
|
19
|
-
|
20
|
-
it 'initializes the daemon' do
|
21
|
-
expect(Rpush::Daemon).to receive(:common_init)
|
22
|
-
Rpush.apns_feedback
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'checks feedback for each app' do
|
26
|
-
expect(Rpush::Daemon::Apns::FeedbackReceiver).to receive(:new).with(apns_app).and_return(receiver)
|
27
|
-
expect(receiver).to receive(:check_for_feedback)
|
28
|
-
Rpush.apns_feedback
|
29
|
-
end
|
30
|
-
|
31
|
-
context 'feedback disabled' do
|
32
|
-
let(:apns_app_params) { super().merge(feedback_enabled: false) }
|
33
|
-
|
34
|
-
it 'does not initialize feedback receiver' do
|
35
|
-
expect(Rpush::Daemon::Apns::FeedbackReceiver).not_to receive(:new)
|
36
|
-
Rpush.apns_feedback
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
@@ -1,14 +0,0 @@
|
|
1
|
-
require 'unit_spec_helper'
|
2
|
-
|
3
|
-
describe Rpush::Client::ActiveRecord::Gcm::Notification do
|
4
|
-
it_behaves_like 'Rpush::Client::Gcm::Notification'
|
5
|
-
it_behaves_like 'Rpush::Client::ActiveRecord::Notification'
|
6
|
-
|
7
|
-
subject(:notification) { described_class.new }
|
8
|
-
let(:app) { Rpush::Gcm::App.create!(name: 'test', auth_key: 'abc') }
|
9
|
-
|
10
|
-
it 'accepts non-booleans as a truthy value' do
|
11
|
-
notification.dry_run = 'Not a boolean'
|
12
|
-
expect(notification.as_json['dry_run']).to eq true
|
13
|
-
end
|
14
|
-
end if active_record?
|
@@ -1,77 +0,0 @@
|
|
1
|
-
require 'unit_spec_helper'
|
2
|
-
|
3
|
-
shared_examples 'Rpush::Client::Gcm::Notification' do
|
4
|
-
let(:app) { Rpush::Gcm::App.create!(name: 'test', auth_key: 'abc') }
|
5
|
-
let(:notification) { described_class.new }
|
6
|
-
|
7
|
-
it "has a 'data' payload limit of 4096 bytes" do
|
8
|
-
notification.data = { key: "a" * 4096 }
|
9
|
-
expect(notification.valid?).to be_falsey
|
10
|
-
expect(notification.errors[:base]).to eq ["Notification payload data cannot be larger than 4096 bytes."]
|
11
|
-
end
|
12
|
-
|
13
|
-
it 'limits the number of registration ids to 1000' do
|
14
|
-
notification.registration_ids = ['a'] * (1000 + 1)
|
15
|
-
expect(notification.valid?).to be_falsey
|
16
|
-
expect(notification.errors[:base]).to eq ["Number of registration_ids cannot be larger than 1000."]
|
17
|
-
end
|
18
|
-
|
19
|
-
it 'validates expiry is present if collapse_key is set' do
|
20
|
-
notification.collapse_key = 'test'
|
21
|
-
notification.expiry = nil
|
22
|
-
expect(notification.valid?).to be_falsey
|
23
|
-
expect(notification.errors[:expiry]).to eq ['must be set when using a collapse_key']
|
24
|
-
end
|
25
|
-
|
26
|
-
it 'includes time_to_live in the payload' do
|
27
|
-
notification.expiry = 100
|
28
|
-
expect(notification.as_json['time_to_live']).to eq 100
|
29
|
-
end
|
30
|
-
|
31
|
-
it 'includes content_available in the payload' do
|
32
|
-
notification.content_available = true
|
33
|
-
expect(notification.as_json['content_available']).to eq true
|
34
|
-
end
|
35
|
-
|
36
|
-
it 'includes mutable_content in the payload' do
|
37
|
-
notification.mutable_content = true
|
38
|
-
expect(notification.as_json['mutable_content']).to eq true
|
39
|
-
end
|
40
|
-
|
41
|
-
it 'sets the priority to high when set to high' do
|
42
|
-
notification.priority = 'high'
|
43
|
-
expect(notification.as_json['priority']).to eq 'high'
|
44
|
-
end
|
45
|
-
|
46
|
-
it 'sets the priority to normal when set to normal' do
|
47
|
-
notification.priority = 'normal'
|
48
|
-
expect(notification.as_json['priority']).to eq 'normal'
|
49
|
-
end
|
50
|
-
|
51
|
-
it 'validates the priority is either "normal" or "high"' do
|
52
|
-
notification.priority = 'invalid'
|
53
|
-
expect(notification.errors[:priority]).to eq ['must be one of either "normal" or "high"']
|
54
|
-
end
|
55
|
-
|
56
|
-
it 'excludes the priority if it is not defined' do
|
57
|
-
expect(notification.as_json).not_to have_key 'priority'
|
58
|
-
end
|
59
|
-
|
60
|
-
it 'includes the notification payload if defined' do
|
61
|
-
notification.notification = { key: 'any key is allowed' }
|
62
|
-
expect(notification.as_json).to have_key 'notification'
|
63
|
-
end
|
64
|
-
|
65
|
-
it 'excludes the notification payload if undefined' do
|
66
|
-
expect(notification.as_json).not_to have_key 'notification'
|
67
|
-
end
|
68
|
-
|
69
|
-
it 'includes the dry_run payload if defined' do
|
70
|
-
notification.dry_run = true
|
71
|
-
expect(notification.as_json['dry_run']).to eq true
|
72
|
-
end
|
73
|
-
|
74
|
-
it 'excludes the dry_run payload if undefined' do
|
75
|
-
expect(notification.as_json).not_to have_key 'dry_run'
|
76
|
-
end
|
77
|
-
end
|
@@ -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
|