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,12 +1,13 @@
|
|
1
1
|
require 'functional_spec_helper'
|
2
2
|
|
3
|
-
describe '
|
4
|
-
let(:app) { Rpush::
|
5
|
-
let(:notification) { Rpush::
|
6
|
-
let(:hydrated_notification) { Rpush::
|
3
|
+
describe 'FCM priority' do
|
4
|
+
let(:app) { Rpush::Fcm::App.new }
|
5
|
+
let(:notification) { Rpush::Fcm::Notification.new }
|
6
|
+
let(:hydrated_notification) { Rpush::Fcm::Notification.find(notification.id) }
|
7
7
|
let(:response) { double(Net::HTTPResponse, code: 200) }
|
8
8
|
let(:http) { double(Net::HTTP::Persistent, request: response, shutdown: nil) }
|
9
9
|
let(:priority) { 'normal' }
|
10
|
+
let(:fake_device_token) { 'a' * 108 }
|
10
11
|
|
11
12
|
before do
|
12
13
|
app.name = 'test'
|
@@ -14,8 +15,9 @@ describe 'GCM priority' do
|
|
14
15
|
app.save!
|
15
16
|
|
16
17
|
notification.app_id = app.id
|
17
|
-
notification.
|
18
|
+
notification.device_token = fake_device_token
|
18
19
|
notification.data = { message: 'test' }
|
20
|
+
notification.notification = { title: 'title' }
|
19
21
|
notification.priority = priority
|
20
22
|
notification.save!
|
21
23
|
|
@@ -23,14 +25,18 @@ describe 'GCM priority' do
|
|
23
25
|
end
|
24
26
|
|
25
27
|
it 'supports normal priority' do
|
26
|
-
|
28
|
+
json = hydrated_notification.as_json
|
29
|
+
expect(json["message"]["android"]["notification"]["notification_priority"]).to eq('PRIORITY_DEFAULT')
|
30
|
+
expect(json["message"]["android"]["priority"]).to eq('normal')
|
27
31
|
end
|
28
32
|
|
29
33
|
context 'high priority' do
|
30
34
|
let(:priority) { 'high' }
|
31
35
|
|
32
36
|
it 'supports high priority' do
|
33
|
-
|
37
|
+
json = hydrated_notification.as_json
|
38
|
+
expect(json["message"]["android"]["notification"]["notification_priority"]).to eq('PRIORITY_MAX')
|
39
|
+
expect(json["message"]["android"]["priority"]).to eq('high')
|
34
40
|
end
|
35
41
|
end
|
36
42
|
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'functional_spec_helper'
|
2
|
+
|
3
|
+
describe 'FCM' do
|
4
|
+
let(:app) { Rpush::Fcm::App.new }
|
5
|
+
let(:notification) { Rpush::Fcm::Notification.new }
|
6
|
+
let(:response) { double(Net::HTTPResponse, code: 200) }
|
7
|
+
let(:http) { double(Net::HTTP::Persistent, request: response, shutdown: nil) }
|
8
|
+
let(:fake_device_token) { 'a' * 108 }
|
9
|
+
let(:creds) {double(Google::Auth::UserRefreshCredentials)}
|
10
|
+
|
11
|
+
before do
|
12
|
+
app.name = 'test'
|
13
|
+
app.auth_key = 'abc123'
|
14
|
+
app.save!
|
15
|
+
|
16
|
+
notification.app_id = app.id
|
17
|
+
notification.device_token = fake_device_token
|
18
|
+
notification.data = { message: 'test' }
|
19
|
+
notification.save!
|
20
|
+
|
21
|
+
allow(Net::HTTP::Persistent).to receive_messages(new: http)
|
22
|
+
allow(creds).to receive(:fetch_access_token).and_return({'access_token': 'face_access_token'})
|
23
|
+
|
24
|
+
allow(::Google::Auth::ServiceAccountCredentials).to receive(:fetch_access_token).and_return({access_token: 'bbbbbb'})
|
25
|
+
allow(::Google::Auth::ServiceAccountCredentials).to receive(:make_creds).and_return(creds)
|
26
|
+
allow_any_instance_of(::Rpush::Daemon::Fcm::Delivery).to receive(:necessary_data_exists?).and_return(true)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'delivers a notification successfully' do
|
30
|
+
example_success_body = {
|
31
|
+
"multicast_id": 108,
|
32
|
+
"success": 1,
|
33
|
+
"failure": 0,
|
34
|
+
"canonical_ids": 0,
|
35
|
+
"results": [
|
36
|
+
{ "message_id": "1:08" }
|
37
|
+
]
|
38
|
+
}.to_json
|
39
|
+
allow(response).to receive_messages(body: example_success_body)
|
40
|
+
|
41
|
+
expect do
|
42
|
+
Rpush.push
|
43
|
+
notification.reload
|
44
|
+
end.to change(notification, :delivered).to(true)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'fails to deliver a notification successfully' do
|
48
|
+
example_error_body = {
|
49
|
+
"error": {
|
50
|
+
"code": 400,
|
51
|
+
"message": "The registration token is not a valid FCM registration token",
|
52
|
+
"errors": [
|
53
|
+
{
|
54
|
+
"message": "The registration token is not a valid FCM registration token",
|
55
|
+
"domain": "global",
|
56
|
+
"reason": "badRequest"
|
57
|
+
}
|
58
|
+
],
|
59
|
+
"status": "INVALID_ARGUMENT"
|
60
|
+
}
|
61
|
+
}.to_json
|
62
|
+
|
63
|
+
allow(response).to receive_messages(code: 400, body: example_error_body)
|
64
|
+
Rpush.push
|
65
|
+
notification.reload
|
66
|
+
expect(notification.delivered).to eq(false)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'retries notification that fail due to a SocketError' do
|
70
|
+
expect(http).to receive(:request).and_raise(SocketError.new)
|
71
|
+
expect(notification.deliver_after).to be_nil
|
72
|
+
expect do
|
73
|
+
Rpush.push
|
74
|
+
notification.reload
|
75
|
+
end.to change(notification, :deliver_after).to(kind_of(Time))
|
76
|
+
end
|
77
|
+
end
|
@@ -1,10 +1,12 @@
|
|
1
1
|
require 'functional_spec_helper'
|
2
2
|
|
3
3
|
describe 'Retries' do
|
4
|
-
let(:app) { Rpush::
|
5
|
-
let(:notification) { Rpush::
|
4
|
+
let(:app) { Rpush::Fcm::App.new }
|
5
|
+
let(:notification) { Rpush::Fcm::Notification.new }
|
6
6
|
let(:response) { double(Net::HTTPResponse, code: 200) }
|
7
7
|
let(:http) { double(Net::HTTP::Persistent, request: response, shutdown: nil) }
|
8
|
+
let(:fake_device_token) { 'a' * 108 }
|
9
|
+
let(:creds) {double(Google::Auth::UserRefreshCredentials)}
|
8
10
|
|
9
11
|
before do
|
10
12
|
Rpush::Daemon.common_init
|
@@ -14,7 +16,7 @@ describe 'Retries' do
|
|
14
16
|
app.save!
|
15
17
|
|
16
18
|
notification.app_id = app.id
|
17
|
-
notification.
|
19
|
+
notification.device_token = 'foo'
|
18
20
|
notification.data = { message: 'test' }
|
19
21
|
notification.save!
|
20
22
|
|
@@ -23,7 +25,22 @@ describe 'Retries' do
|
|
23
25
|
end
|
24
26
|
|
25
27
|
allow(Net::HTTP::Persistent).to receive_messages(new: http)
|
26
|
-
allow(
|
28
|
+
allow(creds).to receive(:fetch_access_token).and_return({'access_token': 'face_access_token'})
|
29
|
+
|
30
|
+
allow(::Google::Auth::ServiceAccountCredentials).to receive(:fetch_access_token).and_return({access_token: 'bbbbbb'})
|
31
|
+
allow(::Google::Auth::ServiceAccountCredentials).to receive(:make_creds).and_return(creds)
|
32
|
+
allow_any_instance_of(::Rpush::Daemon::Fcm::Delivery).to receive(:necessary_data_exists?).and_return(true)
|
33
|
+
|
34
|
+
example_success_body = {
|
35
|
+
"multicast_id": 108,
|
36
|
+
"success": 1,
|
37
|
+
"failure": 0,
|
38
|
+
"canonical_ids": 0,
|
39
|
+
"results": [
|
40
|
+
{ "message_id": "1:08" }
|
41
|
+
]
|
42
|
+
}.to_json
|
43
|
+
allow(response).to receive_messages(body: example_success_body)
|
27
44
|
end
|
28
45
|
|
29
46
|
it 'delivers a notification due to be retried' do
|
@@ -11,17 +11,11 @@ def timeout(&blk)
|
|
11
11
|
Timeout.timeout(10, &blk)
|
12
12
|
end
|
13
13
|
|
14
|
-
def stub_tcp_connection(tcp_socket, ssl_socket, io_double)
|
15
|
-
allow_any_instance_of(Rpush::Daemon::TcpConnection).to receive_messages(connect_socket: [tcp_socket, ssl_socket])
|
16
|
-
allow_any_instance_of(Rpush::Daemon::TcpConnection).to receive_messages(setup_ssl_context: double.as_null_object)
|
17
|
-
stub_const('Rpush::Daemon::TcpConnection::IO', io_double)
|
18
|
-
end
|
19
|
-
|
20
14
|
RSpec.configure do |config|
|
21
15
|
config.before(:each) do
|
22
16
|
Modis.with_connection do |redis|
|
23
17
|
redis.keys('rpush:*').each { |key| redis.del(key) }
|
24
|
-
end if redis?
|
18
|
+
end if redis? && functional_example?(self.class.metadata)
|
25
19
|
|
26
20
|
Rpush.config.logger = ::Logger.new(STDOUT) if functional_example?(self.class.metadata)
|
27
21
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -13,6 +13,7 @@ if !ENV['CI'] || (ENV['CI'] && ENV['QUALITY'] == 'true')
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
+
require 'debug'
|
16
17
|
require 'timecop'
|
17
18
|
require 'activerecord-jdbc-adapter' if defined? JRUBY_VERSION
|
18
19
|
|
@@ -46,7 +47,7 @@ path = File.join(File.dirname(__FILE__), 'support')
|
|
46
47
|
TEST_CERT = File.read(File.join(path, 'cert_without_password.pem'))
|
47
48
|
TEST_CERT_WITH_PASSWORD = File.read(File.join(path, 'cert_with_password.pem'))
|
48
49
|
|
49
|
-
VAPID_KEYPAIR =
|
50
|
+
VAPID_KEYPAIR = WebPush.generate_key.to_hash.merge(subject: 'rpush-test@example.org').to_json
|
50
51
|
|
51
52
|
def after_example_cleanup
|
52
53
|
Rpush.logger = nil
|
@@ -57,6 +58,8 @@ def after_example_cleanup
|
|
57
58
|
end
|
58
59
|
Rpush.plugins.values.each(&:unload)
|
59
60
|
Rpush.instance_variable_set('@plugins', {})
|
61
|
+
Rpush.reflection_stack.clear
|
62
|
+
Rpush.reflection_stack.push(Rpush::ReflectionCollection.new)
|
60
63
|
end
|
61
64
|
|
62
65
|
RSpec.configure do |config|
|
@@ -43,6 +43,7 @@ require 'generators/templates/rpush_3_3_1_updates'
|
|
43
43
|
require 'generators/templates/rpush_4_1_0_updates'
|
44
44
|
require 'generators/templates/rpush_4_1_1_updates'
|
45
45
|
require 'generators/templates/rpush_4_2_0_updates'
|
46
|
+
require 'generators/templates/rpush_7_1_0_updates'
|
46
47
|
|
47
48
|
migrations = [
|
48
49
|
AddRpush,
|
@@ -60,7 +61,8 @@ migrations = [
|
|
60
61
|
Rpush331Updates,
|
61
62
|
Rpush410Updates,
|
62
63
|
Rpush411Updates,
|
63
|
-
Rpush420Updates
|
64
|
+
Rpush420Updates,
|
65
|
+
Rpush710Updates
|
64
66
|
]
|
65
67
|
|
66
68
|
unless ENV['CI']
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'unit_spec_helper'
|
2
2
|
|
3
|
-
describe Rpush::Client::ActiveRecord::
|
4
|
-
it_behaves_like 'Rpush::Client::
|
3
|
+
describe Rpush::Client::ActiveRecord::Fcm::App do
|
4
|
+
it_behaves_like 'Rpush::Client::Fcm::App'
|
5
5
|
it_behaves_like 'Rpush::Client::ActiveRecord::App'
|
6
6
|
end if active_record?
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'unit_spec_helper'
|
2
|
+
|
3
|
+
describe Rpush::Client::ActiveRecord::Fcm::Notification do
|
4
|
+
it_behaves_like 'Rpush::Client::Fcm::Notification'
|
5
|
+
it_behaves_like 'Rpush::Client::ActiveRecord::Notification'
|
6
|
+
|
7
|
+
subject(:notification) { described_class.new }
|
8
|
+
let(:app) { Rpush::Fcm::App.create!(name: 'test', auth_key: 'abc') }
|
9
|
+
|
10
|
+
end if active_record?
|
@@ -8,7 +8,7 @@ shared_examples_for 'Rpush::Client::ActiveRecord::App' do
|
|
8
8
|
app = Rpush::Apns::App.new(name: 'test', environment: 'development', certificate: TEST_CERT)
|
9
9
|
expect(app.valid?).to eq(true)
|
10
10
|
|
11
|
-
app = Rpush::
|
11
|
+
app = Rpush::Fcm::App.new(name: 'test', environment: 'production', json_key: TEST_CERT)
|
12
12
|
expect(app.valid?).to eq(true)
|
13
13
|
end
|
14
14
|
end
|
@@ -165,21 +165,6 @@ shared_examples 'Rpush::Client::Apns::Notification' do
|
|
165
165
|
end
|
166
166
|
end
|
167
167
|
|
168
|
-
describe 'content_available?' do
|
169
|
-
context 'if not set' do
|
170
|
-
it 'should be false' do
|
171
|
-
expect(notification.content_available?).to be_falsey
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
context 'if set' do
|
176
|
-
it 'should be true' do
|
177
|
-
notification.content_available = true
|
178
|
-
expect(notification.content_available?).to be_truthy
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
168
|
describe 'url-args' do
|
184
169
|
it 'includes url-args in the payload' do
|
185
170
|
notification.url_args = ['url-arg-1']
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'unit_spec_helper'
|
2
|
+
|
3
|
+
shared_examples 'Rpush::Client::Fcm::Notification' do
|
4
|
+
let(:app) { Rpush::Fcm::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.app = app
|
9
|
+
notification.device_token = "valid"
|
10
|
+
notification.data = { key: "a" * 4096 }
|
11
|
+
expect(notification.valid?).to be_falsey
|
12
|
+
expect(notification.errors[:base]).to eq ["Notification payload data cannot be larger than 4096 bytes."]
|
13
|
+
end
|
14
|
+
|
15
|
+
it "validates notification keys" do
|
16
|
+
notification.app = app
|
17
|
+
notification.device_token = "valid"
|
18
|
+
notification.notification = { "title" => "valid", "body" => "valid", "color" => "valid for android", "garbage" => "invalid" }
|
19
|
+
expect(notification.valid?).to be_falsey
|
20
|
+
expect(notification.errors[:notification]).to eq ["contains invalid keys: garbage"]
|
21
|
+
end
|
22
|
+
|
23
|
+
it "allows notifications with either symbol keys or string keys" do
|
24
|
+
notification.app = app
|
25
|
+
notification.notification = { "title" => "title", body: "body" }
|
26
|
+
expect(notification.as_json['message']['notification']).to eq({"title"=>"title", "body"=>"body"})
|
27
|
+
end
|
28
|
+
|
29
|
+
it "moves notification keys to the correcdt location" do
|
30
|
+
notification.app = app
|
31
|
+
notification.device_token = "valid"
|
32
|
+
notification.notification = { "title" => "valid", "body" => "valid", "color" => "valid for android" }
|
33
|
+
expect(notification.valid?).to be_truthy
|
34
|
+
expect(notification.as_json['message']['notification']).to eq("title"=>"valid", "body"=>"valid")
|
35
|
+
expect(notification.as_json['message']['android']['notification']['color']).to eq('valid for android')
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'validates expiry is present if collapse_key is set' do
|
39
|
+
notification.collapse_key = 'test'
|
40
|
+
notification.expiry = nil
|
41
|
+
expect(notification.valid?).to be_falsey
|
42
|
+
expect(notification.errors[:expiry]).to eq ['must be set when using a collapse_key']
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'includes time_to_live in the payload' do
|
46
|
+
notification.expiry = 100
|
47
|
+
expect(notification.as_json['message']['android']['ttl']).to eq '100s'
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'includes content_available in the payload' do
|
51
|
+
notification.content_available = true
|
52
|
+
expect(notification.as_json["message"]["apns"]["payload"]["aps"]["content-available"]).to eq 1
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'sets the priority to high when set to high' do
|
56
|
+
notification.notification = { title: "Title" }
|
57
|
+
notification.priority = 'high'
|
58
|
+
expect(notification.as_json['message']['android']['priority']).to eq 'high'
|
59
|
+
expect(notification.as_json['message']['android']['notification']['notification_priority']).to eq 'PRIORITY_MAX'
|
60
|
+
# TODO Add notification_priority
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'sets the priority to normal when set to normal' do
|
64
|
+
notification.notification = { title: "Title" }
|
65
|
+
notification.priority = 'normal'
|
66
|
+
expect(notification.as_json['message']['android']['priority']).to eq 'normal'
|
67
|
+
expect(notification.as_json['message']['android']['notification']['notification_priority']).to eq 'PRIORITY_DEFAULT'
|
68
|
+
# TODO Add notification_priority
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'validates the priority is either "normal" or "high"' do
|
72
|
+
notification.priority = 'invalid'
|
73
|
+
expect(notification.errors[:priority]).to eq ['must be one of either "normal" or "high"']
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'excludes the priority if it is not defined' do
|
77
|
+
expect(notification.as_json['message']['android']).not_to have_key 'priority'
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'includes the notification payload if defined' do
|
81
|
+
notification.notification = { key: 'any key is allowed' }
|
82
|
+
expect(notification.as_json['message']).to have_key 'notification'
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'excludes the notification payload if undefined' do
|
86
|
+
expect(notification.as_json['message']).not_to have_key 'notification'
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'fails when trying to set the dry_run option' do
|
90
|
+
expect { notification.dry_run = true }.to raise_error(ArgumentError)
|
91
|
+
end
|
92
|
+
end
|
@@ -41,6 +41,6 @@ describe Rpush::Configuration do
|
|
41
41
|
it 'delegate redis_options to Modis' do
|
42
42
|
Rpush.config.client = :redis
|
43
43
|
Rpush.config.redis_options = { hi: :mom }
|
44
|
-
expect(Modis.redis_options).to eq(hi: :mom)
|
44
|
+
expect(Modis.redis_options[:default]).to eq(hi: :mom)
|
45
45
|
end
|
46
46
|
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'unit_spec_helper'
|
2
|
+
|
3
|
+
describe Rpush::Daemon::Fcm::Delivery do
|
4
|
+
let(:app) { Rpush::Fcm::App.create!(name: 'MyApp', firebase_project_id: 'abc123', json_key:'{}') }
|
5
|
+
let(:notification) { Rpush::Fcm::Notification.create!(app: app, device_token: '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::Fcm::Delivery.new(app, http, notification, batch) }
|
12
|
+
let(:store) { double(create_fcm_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
|
+
allow_any_instance_of(Rpush::Daemon::Fcm::Delivery).to receive_messages(obtain_access_token: "access_token")
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'a 200 response' do
|
31
|
+
before do
|
32
|
+
allow(response).to receive_messages(code: 200)
|
33
|
+
allow(response).to receive_messages(body: nil)
|
34
|
+
allow(notification).to receive_messages(device_token: '1')
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'reflects on ID which successfully received the notification' do
|
38
|
+
expect(delivery).to receive(:reflect).with(:fcm_delivered_to_recipient, notification)
|
39
|
+
perform
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'marks the notification as delivered' do
|
43
|
+
expect(delivery).to receive(:mark_delivered)
|
44
|
+
perform
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'logs that the notification was delivered' do
|
48
|
+
expect(logger).to receive(:info).with("[MyApp] #{notification.id} sent to 1")
|
49
|
+
perform
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe 'all deliveries failed with Unavailable or InternalServerError 503' do
|
54
|
+
before do
|
55
|
+
allow(notification).to receive_messages(device_token: '1')
|
56
|
+
allow(response).to receive_messages(code: 503, body: nil)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'reflects on any IDs which failed to receive the notification' do
|
60
|
+
pending("Determine the correct cases where FCM should send fcm_failed_to_recipient")
|
61
|
+
expect(delivery).to receive(:reflect).with(:fcm_failed_to_recipient, notification)
|
62
|
+
perform
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'retries the notification respecting the Retry-After header' do
|
66
|
+
allow(response).to receive_messages(header: { 'retry-after' => 10 })
|
67
|
+
expect(delivery).to receive(:mark_retryable).with(notification, now + 10.seconds)
|
68
|
+
perform
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'retries the notification using exponential back-off if the Retry-After header is not present' do
|
72
|
+
expect(delivery).to receive(:mark_retryable).with(notification, now + 2)
|
73
|
+
perform
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'does not mark the notification as failed' do
|
77
|
+
expect(delivery).not_to receive(:mark_failed)
|
78
|
+
perform
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'logs that the notification will be retried' do
|
82
|
+
notification.retries = 1
|
83
|
+
notification.deliver_after = now + 2
|
84
|
+
expect(Rpush.logger).to receive(:warn).with("[MyApp] FCM responded with an Service Unavailable Error. Notification #{notification.id} will be retried after 2012-10-14 00:00:02 (retry 1).")
|
85
|
+
perform
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe 'all deliveries failed with Unavailable or InternalServerError 500' do
|
90
|
+
before do
|
91
|
+
allow(notification).to receive_messages(device_token: '1')
|
92
|
+
allow(response).to receive_messages(code: 500, body: nil)
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'logs a warning that the notification has been re-queued.' do
|
96
|
+
notification.retries = 3
|
97
|
+
notification.deliver_after = now + 2**3
|
98
|
+
expect(Rpush.logger).to receive(:warn).with("[MyApp] FCM responded with an Internal Error. Notification #{notification.id} will be retried after #{(now + 2**3).strftime('%Y-%m-%d %H:%M:%S')} (retry 3).")
|
99
|
+
perform
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'retries the notification in accordance with the exponential back-off strategy.' do
|
103
|
+
notification.update_attribute(:retries, 2)
|
104
|
+
expect(delivery).to receive(:mark_retryable).with(notification, now + 2**3)
|
105
|
+
perform
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe 'all deliveries failed with invalid token' do
|
110
|
+
before do
|
111
|
+
allow(notification).to receive_messages(device_token: '1')
|
112
|
+
allow(response).to receive_messages(code: 404, body: { error: { status: 'NOT_FOUND', message: 'Requested entity was not found.' } }.to_json)
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'reflects on invalid IDs' do
|
116
|
+
expect(delivery).to receive(:reflect).with(:fcm_invalid_device_token, app, "NOT_FOUND: Requested entity was not found.", '1')
|
117
|
+
perform_with_rescue
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'marks a notification as failed if any ids are invalid' do
|
121
|
+
expect(delivery).to receive(:mark_failed)
|
122
|
+
expect(delivery).not_to receive(:mark_retryable)
|
123
|
+
expect(store).not_to receive(:create_fcm_notification)
|
124
|
+
perform_with_rescue
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -23,7 +23,7 @@ describe Rpush::Daemon::ServiceConfigMethods do
|
|
23
23
|
ServiceConfigMethodsSpec.dispatcher :http, an: :option
|
24
24
|
app = double
|
25
25
|
dispatcher = double
|
26
|
-
expect(Rpush::Daemon::Dispatcher::Http).to receive(:new).with(app, ServiceConfigMethodsSpec::Delivery, an: :option).and_return(dispatcher)
|
26
|
+
expect(Rpush::Daemon::Dispatcher::Http).to receive(:new).with(app, ServiceConfigMethodsSpec::Delivery, {an: :option}).and_return(dispatcher)
|
27
27
|
expect(ServiceConfigMethodsSpec.new_dispatcher(app)).to eq dispatcher
|
28
28
|
end
|
29
29
|
|
@@ -233,48 +233,6 @@ shared_examples 'Rpush::Daemon::Store' do
|
|
233
233
|
end
|
234
234
|
end
|
235
235
|
|
236
|
-
describe 'create_apns_feedback' do
|
237
|
-
it 'creates the Feedback record' do
|
238
|
-
expect(Rpush::Apns::Feedback).to receive(:create!).with(
|
239
|
-
failed_at: time, device_token: 'ab' * 32, app_id: app.id
|
240
|
-
)
|
241
|
-
store.create_apns_feedback(time, 'ab' * 32, app)
|
242
|
-
end
|
243
|
-
end
|
244
|
-
|
245
|
-
describe 'create_gcm_notification' do
|
246
|
-
let(:data) { { 'data' => true } }
|
247
|
-
let(:attributes) { { device_token: 'ab' * 32 } }
|
248
|
-
let(:registration_ids) { %w[123 456] }
|
249
|
-
let(:deliver_after) { time + 10.seconds }
|
250
|
-
let(:args) { [attributes, data, registration_ids, deliver_after, app] }
|
251
|
-
|
252
|
-
it 'sets the given attributes' do
|
253
|
-
new_notification = store.create_gcm_notification(*args)
|
254
|
-
expect(new_notification.device_token).to eq 'ab' * 32
|
255
|
-
end
|
256
|
-
|
257
|
-
it 'sets the given data' do
|
258
|
-
new_notification = store.create_gcm_notification(*args)
|
259
|
-
expect(new_notification.data['data']).to be_truthy
|
260
|
-
end
|
261
|
-
|
262
|
-
it 'sets the given registration IDs' do
|
263
|
-
new_notification = store.create_gcm_notification(*args)
|
264
|
-
expect(new_notification.registration_ids).to eq registration_ids
|
265
|
-
end
|
266
|
-
|
267
|
-
it 'sets the deliver_after timestamp' do
|
268
|
-
new_notification = store.create_gcm_notification(*args)
|
269
|
-
expect(new_notification.deliver_after).to eq deliver_after
|
270
|
-
end
|
271
|
-
|
272
|
-
it 'saves the new notification' do
|
273
|
-
new_notification = store.create_gcm_notification(*args)
|
274
|
-
expect(new_notification.new_record?).to be_falsey
|
275
|
-
end
|
276
|
-
end
|
277
|
-
|
278
236
|
describe 'create_adm_notification' do
|
279
237
|
let(:data) { { 'data' => true } }
|
280
238
|
let(:attributes) { { app_id: app.id, collapse_key: 'ckey', delay_while_idle: true } }
|
@@ -50,7 +50,7 @@ describe Rpush::Daemon::Wns::Delivery do
|
|
50
50
|
end
|
51
51
|
|
52
52
|
it 'set the access token for the app' do
|
53
|
-
expect(delivery).to receive(:update_access_token).with("access_token" => "dummy_access_token", "expires_in" => 60)
|
53
|
+
expect(delivery).to receive(:update_access_token).with({"access_token" => "dummy_access_token", "expires_in" => 60})
|
54
54
|
expect(store).to receive(:update_app).with app
|
55
55
|
perform
|
56
56
|
end
|
data/spec/unit/logger_spec.rb
CHANGED
@@ -92,7 +92,7 @@ describe Rpush::Logger do
|
|
92
92
|
now = Time.now
|
93
93
|
allow(Time).to receive(:now).and_return(now)
|
94
94
|
logger = Rpush::Logger.new
|
95
|
-
expect(@logger).to receive(:info).with(/#{Regexp.escape("[#{now.
|
95
|
+
expect(@logger).to receive(:info).with(/#{Regexp.escape("[#{now.to_formatted_s(:db)}]")}/)
|
96
96
|
logger.info("blah")
|
97
97
|
end
|
98
98
|
|
data/spec/unit_spec_helper.rb
CHANGED
@@ -12,7 +12,7 @@ RSpec.configure do |config|
|
|
12
12
|
config.before(:each) do
|
13
13
|
Modis.with_connection do |redis|
|
14
14
|
redis.keys('rpush:*').each { |key| redis.del(key) }
|
15
|
-
end if redis?
|
15
|
+
end if redis? && unit_example?(self.class.metadata)
|
16
16
|
|
17
17
|
if active_record? && unit_example?(self.class.metadata)
|
18
18
|
connection = ActiveRecord::Base.connection
|