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.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -4
  3. data/README.md +24 -44
  4. data/lib/generators/rpush_migration_generator.rb +1 -0
  5. data/lib/generators/templates/rpush.rb +23 -13
  6. data/lib/generators/templates/rpush_7_1_0_updates.rb +12 -0
  7. data/lib/rpush/client/active_model/apns/notification.rb +0 -4
  8. data/lib/rpush/client/active_model/{gcm → fcm}/app.rb +4 -3
  9. data/lib/rpush/client/active_model/{gcm → fcm}/expiry_collapse_key_mutual_inclusion_validator.rb +1 -1
  10. data/lib/rpush/client/active_model/fcm/notification.rb +129 -0
  11. data/lib/rpush/client/active_model/fcm/notification_keys_in_allowed_list_validator.rb +20 -0
  12. data/lib/rpush/client/active_model.rb +4 -3
  13. data/lib/rpush/client/active_record/{gcm → fcm}/app.rb +2 -2
  14. data/lib/rpush/client/active_record/{gcm → fcm}/notification.rb +2 -2
  15. data/lib/rpush/client/active_record.rb +2 -2
  16. data/lib/rpush/client/redis/app.rb +2 -0
  17. data/lib/rpush/client/redis/{gcm → fcm}/app.rb +2 -2
  18. data/lib/rpush/client/redis/{gcm → fcm}/notification.rb +2 -2
  19. data/lib/rpush/client/redis.rb +2 -2
  20. data/lib/rpush/configuration.rb +2 -19
  21. data/lib/rpush/daemon/apns2/delivery.rb +0 -1
  22. data/lib/rpush/daemon/apnsp8/delivery.rb +0 -1
  23. data/lib/rpush/daemon/fcm/delivery.rb +162 -0
  24. data/lib/rpush/daemon/{gcm.rb → fcm.rb} +1 -1
  25. data/lib/rpush/daemon/google_credential_cache.rb +41 -0
  26. data/lib/rpush/daemon/service_config_methods.rb +0 -2
  27. data/lib/rpush/daemon/store/active_record.rb +15 -12
  28. data/lib/rpush/daemon/store/interface.rb +3 -3
  29. data/lib/rpush/daemon/store/redis.rb +13 -9
  30. data/lib/rpush/daemon/webpush/delivery.rb +2 -2
  31. data/lib/rpush/daemon.rb +3 -9
  32. data/lib/rpush/reflection_collection.rb +3 -3
  33. data/lib/rpush/version.rb +2 -2
  34. data/lib/rpush.rb +1 -1
  35. data/spec/functional/apns2_spec.rb +2 -6
  36. data/spec/functional/cli_spec.rb +41 -15
  37. data/spec/functional/embed_spec.rb +57 -26
  38. data/spec/functional/{gcm_priority_spec.rb → fcm_priority_spec.rb} +13 -7
  39. data/spec/functional/fcm_spec.rb +77 -0
  40. data/spec/functional/retry_spec.rb +21 -4
  41. data/spec/functional/synchronization_spec.rb +1 -1
  42. data/spec/functional_spec_helper.rb +1 -7
  43. data/spec/spec_helper.rb +4 -1
  44. data/spec/support/active_record_setup.rb +3 -1
  45. data/spec/unit/client/active_record/{gcm → fcm}/app_spec.rb +2 -2
  46. data/spec/unit/client/active_record/fcm/notification_spec.rb +10 -0
  47. data/spec/unit/client/active_record/shared/app.rb +1 -1
  48. data/spec/unit/client/redis/fcm/app_spec.rb +5 -0
  49. data/spec/unit/client/redis/fcm/notification_spec.rb +5 -0
  50. data/spec/unit/client/shared/apns/notification.rb +0 -15
  51. data/spec/unit/client/shared/fcm/app.rb +4 -0
  52. data/spec/unit/client/shared/fcm/notification.rb +92 -0
  53. data/spec/unit/configuration_spec.rb +1 -1
  54. data/spec/unit/daemon/apnsp8/delivery_spec.rb +1 -1
  55. data/spec/unit/daemon/fcm/delivery_spec.rb +127 -0
  56. data/spec/unit/daemon/service_config_methods_spec.rb +1 -1
  57. data/spec/unit/daemon/shared/store.rb +0 -42
  58. data/spec/unit/daemon/wns/delivery_spec.rb +1 -1
  59. data/spec/unit/logger_spec.rb +1 -1
  60. data/spec/unit_spec_helper.rb +1 -1
  61. metadata +127 -69
  62. data/lib/rpush/apns_feedback.rb +0 -18
  63. data/lib/rpush/client/active_model/gcm/notification.rb +0 -62
  64. data/lib/rpush/daemon/apns/delivery.rb +0 -43
  65. data/lib/rpush/daemon/apns/feedback_receiver.rb +0 -91
  66. data/lib/rpush/daemon/apns.rb +0 -17
  67. data/lib/rpush/daemon/dispatcher/apns_tcp.rb +0 -152
  68. data/lib/rpush/daemon/dispatcher/tcp.rb +0 -22
  69. data/lib/rpush/daemon/gcm/delivery.rb +0 -241
  70. data/lib/rpush/daemon/tcp_connection.rb +0 -190
  71. data/spec/functional/apns_spec.rb +0 -162
  72. data/spec/functional/gcm_spec.rb +0 -46
  73. data/spec/functional/new_app_spec.rb +0 -44
  74. data/spec/unit/apns_feedback_spec.rb +0 -39
  75. data/spec/unit/client/active_record/gcm/notification_spec.rb +0 -14
  76. data/spec/unit/client/redis/gcm/app_spec.rb +0 -5
  77. data/spec/unit/client/redis/gcm/notification_spec.rb +0 -5
  78. data/spec/unit/client/shared/gcm/app.rb +0 -4
  79. data/spec/unit/client/shared/gcm/notification.rb +0 -77
  80. data/spec/unit/daemon/apns/delivery_spec.rb +0 -108
  81. data/spec/unit/daemon/apns/feedback_receiver_spec.rb +0 -137
  82. data/spec/unit/daemon/dispatcher/tcp_spec.rb +0 -32
  83. data/spec/unit/daemon/gcm/delivery_spec.rb +0 -387
  84. data/spec/unit/daemon/tcp_connection_spec.rb +0 -292
@@ -1,12 +1,13 @@
1
1
  require 'functional_spec_helper'
2
2
 
3
- describe 'GCM priority' do
4
- let(:app) { Rpush::Gcm::App.new }
5
- let(:notification) { Rpush::Gcm::Notification.new }
6
- let(:hydrated_notification) { Rpush::Gcm::Notification.find(notification.id) }
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.registration_ids = ['foo']
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
- expect(hydrated_notification.as_json['priority']).to eq('normal')
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
- expect(hydrated_notification.as_json['priority']).to eq('high')
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::Gcm::App.new }
5
- let(:notification) { Rpush::Gcm::Notification.new }
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.registration_ids = ['foo']
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(response).to receive_messages(body: JSON.dump(results: [{ message_id: notification.registration_ids.first.to_s }]))
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
@@ -2,7 +2,7 @@ require 'functional_spec_helper'
2
2
 
3
3
  describe 'Synchronization' do
4
4
  let(:timeout) { 10 }
5
- let(:app) { Rpush::Gcm::App.new }
5
+ let(:app) { Rpush::Fcm::App.new }
6
6
 
7
7
  def wait_for_num_dispatchers(num)
8
8
  Timeout.timeout(timeout) 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 = Webpush.generate_key.to_hash.merge(subject: 'rpush-test@example.org').to_json
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::Gcm::App do
4
- it_behaves_like 'Rpush::Client::Gcm::App'
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::Gcm::App.new(name: 'test', environment: 'production', auth_key: TEST_CERT)
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
@@ -0,0 +1,5 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Client::Redis::Fcm::App do
4
+ it_behaves_like 'Rpush::Client::Fcm::App'
5
+ end if redis?
@@ -0,0 +1,5 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Client::Redis::Fcm::Notification do
4
+ it_behaves_like 'Rpush::Client::Fcm::Notification'
5
+ end if redis?
@@ -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,4 @@
1
+ require 'unit_spec_helper'
2
+
3
+ shared_examples 'Rpush::Client::Fcm::App' do
4
+ end
@@ -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
@@ -42,7 +42,7 @@ describe Rpush::Daemon::Apnsp8::Delivery do
42
42
  loop do
43
43
  break unless thread.alive?
44
44
 
45
- if Time.now - start > 0.5
45
+ if Time.now - start > 1
46
46
  thread.kill
47
47
  fail 'Stuck in an infinite loop'
48
48
  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
@@ -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.to_s(:db)}]")}/)
95
+ expect(@logger).to receive(:info).with(/#{Regexp.escape("[#{now.to_formatted_s(:db)}]")}/)
96
96
  logger.info("blah")
97
97
  end
98
98
 
@@ -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