rpush 4.1.0 → 5.2.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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +254 -162
  3. data/README.md +57 -16
  4. data/lib/generators/rpush_migration_generator.rb +2 -0
  5. data/lib/generators/templates/rpush.rb +4 -0
  6. data/lib/generators/templates/rpush_4_1_1_updates.rb +9 -0
  7. data/lib/generators/templates/rpush_4_2_0_updates.rb +10 -0
  8. data/lib/rpush/apns_feedback.rb +1 -0
  9. data/lib/rpush/cli.rb +9 -22
  10. data/lib/rpush/client/active_model.rb +1 -1
  11. data/lib/rpush/client/active_model/apns/notification.rb +9 -1
  12. data/lib/rpush/client/active_model/apns/notification_payload_size_validator.rb +15 -0
  13. data/lib/rpush/client/active_model/apns2/notification.rb +14 -0
  14. data/lib/rpush/client/active_model/gcm/notification.rb +4 -3
  15. data/lib/rpush/client/active_model/wns/notification.rb +8 -0
  16. data/lib/rpush/client/active_record.rb +1 -0
  17. data/lib/rpush/client/active_record/apns/active_record_serializable_notification.rb +65 -0
  18. data/lib/rpush/client/active_record/apns/notification.rb +1 -29
  19. data/lib/rpush/client/active_record/apns2/notification.rb +4 -1
  20. data/lib/rpush/client/active_record/app.rb +1 -1
  21. data/lib/rpush/client/active_record/notification.rb +1 -1
  22. data/lib/rpush/client/redis/apns2/notification.rb +1 -0
  23. data/lib/rpush/client/redis/app.rb +2 -1
  24. data/lib/rpush/client/redis/notification.rb +2 -1
  25. data/lib/rpush/client/redis/pushy/notification.rb +0 -1
  26. data/lib/rpush/configuration.rb +2 -1
  27. data/lib/rpush/daemon.rb +2 -2
  28. data/lib/rpush/daemon/apns/feedback_receiver.rb +2 -1
  29. data/lib/rpush/daemon/apns2/delivery.rb +6 -1
  30. data/lib/rpush/daemon/apnsp8/delivery.rb +7 -2
  31. data/lib/rpush/daemon/app_runner.rb +1 -1
  32. data/lib/rpush/daemon/batch.rb +12 -5
  33. data/lib/rpush/daemon/delivery.rb +1 -2
  34. data/lib/rpush/daemon/dispatcher/apns_http2.rb +4 -2
  35. data/lib/rpush/daemon/dispatcher/apnsp8_http2.rb +2 -1
  36. data/lib/rpush/daemon/signal_handler.rb +1 -1
  37. data/lib/rpush/daemon/store/active_record.rb +1 -1
  38. data/lib/rpush/daemon/store/active_record/reconnectable.rb +1 -1
  39. data/lib/rpush/daemon/store/redis.rb +1 -1
  40. data/lib/rpush/daemon/wns/badge_request.rb +8 -3
  41. data/lib/rpush/daemon/wns/raw_request.rb +9 -2
  42. data/lib/rpush/daemon/wns/toast_request.rb +6 -2
  43. data/lib/rpush/logger.rb +1 -0
  44. data/lib/rpush/version.rb +2 -2
  45. data/spec/.rubocop.yml +1 -1
  46. data/spec/functional/apns2_spec.rb +85 -0
  47. data/spec/functional/gcm_priority_spec.rb +40 -0
  48. data/spec/support/active_record_setup.rb +5 -1
  49. data/spec/support/simplecov_helper.rb +1 -1
  50. data/spec/unit/apns_feedback_spec.rb +17 -6
  51. data/spec/unit/client/active_record/adm/app_spec.rb +2 -54
  52. data/spec/unit/client/active_record/adm/notification_spec.rb +2 -39
  53. data/spec/unit/client/active_record/apns/app_spec.rb +3 -26
  54. data/spec/unit/client/active_record/apns/feedback_spec.rb +1 -5
  55. data/spec/unit/client/active_record/apns/notification_spec.rb +29 -288
  56. data/spec/unit/client/active_record/apns2/app_spec.rb +4 -0
  57. data/spec/unit/client/active_record/apns2/notification_spec.rb +65 -0
  58. data/spec/unit/client/active_record/app_spec.rb +1 -26
  59. data/spec/unit/client/active_record/gcm/app_spec.rb +3 -1
  60. data/spec/unit/client/active_record/gcm/notification_spec.rb +6 -83
  61. data/spec/unit/client/active_record/notification_spec.rb +10 -11
  62. data/spec/unit/client/active_record/pushy/app_spec.rb +2 -13
  63. data/spec/unit/client/active_record/pushy/notification_spec.rb +2 -55
  64. data/spec/unit/client/active_record/shared/app.rb +14 -0
  65. data/spec/unit/{notification_shared.rb → client/active_record/shared/notification.rb} +12 -7
  66. data/spec/unit/client/active_record/wns/badge_notification_spec.rb +1 -11
  67. data/spec/unit/client/active_record/wns/raw_notification_spec.rb +3 -12
  68. data/spec/unit/client/active_record/wpns/app_spec.rb +3 -1
  69. data/spec/unit/client/active_record/wpns/notification_spec.rb +2 -17
  70. data/spec/unit/client/redis/adm/app_spec.rb +5 -0
  71. data/spec/unit/client/redis/adm/notification_spec.rb +5 -0
  72. data/spec/unit/client/redis/apns/app_spec.rb +5 -0
  73. data/spec/unit/client/redis/apns/feedback_spec.rb +5 -0
  74. data/spec/unit/client/redis/apns/notification_spec.rb +50 -0
  75. data/spec/unit/client/redis/apns2/app_spec.rb +4 -0
  76. data/spec/unit/client/redis/apns2/notification_spec.rb +50 -0
  77. data/spec/unit/client/redis/app_spec.rb +5 -0
  78. data/spec/unit/client/redis/gcm/app_spec.rb +5 -0
  79. data/spec/unit/client/redis/gcm/notification_spec.rb +5 -0
  80. data/spec/unit/client/redis/notification_spec.rb +5 -0
  81. data/spec/unit/client/redis/pushy/app_spec.rb +5 -0
  82. data/spec/unit/client/redis/pushy/notification_spec.rb +5 -0
  83. data/spec/unit/client/redis/wns/badge_notification_spec.rb +5 -0
  84. data/spec/unit/client/redis/wns/raw_notification_spec.rb +22 -0
  85. data/spec/unit/client/redis/wpns/app_spec.rb +5 -0
  86. data/spec/unit/client/redis/wpns/notification_spec.rb +5 -0
  87. data/spec/unit/client/shared/adm/app.rb +51 -0
  88. data/spec/unit/client/shared/adm/notification.rb +39 -0
  89. data/spec/unit/client/shared/apns/app.rb +29 -0
  90. data/spec/unit/client/shared/apns/feedback.rb +9 -0
  91. data/spec/unit/client/shared/apns/notification.rb +262 -0
  92. data/spec/unit/client/shared/app.rb +17 -0
  93. data/spec/unit/client/shared/gcm/app.rb +4 -0
  94. data/spec/unit/client/shared/gcm/notification.rb +77 -0
  95. data/spec/unit/client/shared/notification.rb +10 -0
  96. data/spec/unit/client/shared/pushy/app.rb +17 -0
  97. data/spec/unit/client/shared/pushy/notification.rb +55 -0
  98. data/spec/unit/client/shared/wns/badge_notification.rb +15 -0
  99. data/spec/unit/client/shared/wns/raw_notification.rb +21 -0
  100. data/spec/unit/client/shared/wpns/app.rb +4 -0
  101. data/spec/unit/client/shared/wpns/notification.rb +18 -0
  102. data/spec/unit/daemon/apns/feedback_receiver_spec.rb +19 -1
  103. data/spec/unit/daemon/batch_spec.rb +50 -2
  104. data/spec/unit/daemon/delivery_spec.rb +10 -0
  105. data/spec/unit/daemon/gcm/delivery_spec.rb +1 -1
  106. data/spec/unit/daemon/shared/store.rb +312 -0
  107. data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +7 -7
  108. data/spec/unit/daemon/store/active_record_spec.rb +7 -295
  109. data/spec/unit/daemon/store/redis_spec.rb +4 -293
  110. data/spec/unit/daemon/wns/post_request_spec.rb +64 -0
  111. data/spec/unit_spec_helper.rb +3 -0
  112. metadata +114 -14
  113. data/lib/rpush/client/active_model/apns/binary_notification_validator.rb +0 -16
@@ -177,6 +177,13 @@ describe 'APNs http2 adapter' do
177
177
  end
178
178
 
179
179
  context 'when there is SocketError' do
180
+ let(:fake_http_resp_headers) {
181
+ {
182
+ ":status" => "500",
183
+ "apns-id"=>"C6D65840-5E3F-785A-4D91-B97D305C12F6"
184
+ }
185
+ }
186
+
180
187
  before(:each) do
181
188
  expect(fake_client).to receive(:call_async) { raise(SocketError) }
182
189
  end
@@ -201,6 +208,24 @@ describe 'APNs http2 adapter' do
201
208
  notification = create_notification
202
209
  Rpush.push
203
210
  end
211
+
212
+ context 'when specific notification was delivered before request failed' do
213
+ let(:fake_http_resp_headers) {
214
+ {
215
+ ":status" => "200",
216
+ "apns-id"=>"C6D65840-5E3F-785A-4D91-B97D305C12F6"
217
+ }
218
+ }
219
+
220
+ it 'fails but will not retry this notification' do
221
+ notification = create_notification
222
+ expect do
223
+ Rpush.push
224
+ notification.reload
225
+ end.to change(notification, :retries).by(0)
226
+ .and change(notification, :delivered).to(true)
227
+ end
228
+ end
204
229
  end
205
230
 
206
231
  context 'when any StandardError occurs' do
@@ -228,5 +253,65 @@ describe 'APNs http2 adapter' do
228
253
  Rpush.push
229
254
  end
230
255
  end
256
+
257
+ context 'when waiting for requests to complete times out' do
258
+ let(:on_close) do
259
+ proc { |&block| @thread = Thread.new { sleep(0.01) } }
260
+ end
261
+
262
+ before(:each) do
263
+ @thread = nil
264
+
265
+ expect(fake_http2_request).
266
+ to receive(:on).with(:close), &on_close
267
+
268
+ expect(fake_client).to receive(:join) { @thread.join; raise(NetHttp2::AsyncRequestTimeout) }
269
+ end
270
+
271
+ it 'closes the client' do
272
+ create_notification
273
+ expect(fake_client).to receive(:close)
274
+ Rpush.push
275
+ end
276
+
277
+ it 'reflects :error' do
278
+ reflected_error = false
279
+ Rpush.reflect do |on|
280
+ on.error do |error|
281
+ reflected_error = true
282
+ expect(error).to be_kind_of(StandardError)
283
+ reflector.accept
284
+ end
285
+ end
286
+
287
+ notification = create_notification
288
+ Rpush.push
289
+
290
+ expect(reflected_error).to be true
291
+ end
292
+
293
+ it 'fails but retries delivery several times' do
294
+ notification = create_notification
295
+ expect do
296
+ Rpush.push
297
+ notification.reload
298
+ end.to change(notification, :retries)
299
+ end
300
+
301
+ context 'when specific notification was delivered before another async call failed' do
302
+ let(:on_close) do
303
+ proc { |&block| @thread = Thread.new { sleep(0.01); block.call } }
304
+ end
305
+
306
+ it 'fails but retries delivery several times' do
307
+ notification = create_notification
308
+ expect do
309
+ Rpush.push
310
+ notification.reload
311
+ end.to change(notification, :retries).by(0)
312
+ .and change(notification, :delivered).to(true)
313
+ end
314
+ end
315
+ end
231
316
  end
232
317
  end
@@ -0,0 +1,40 @@
1
+ require 'functional_spec_helper'
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) }
7
+ let(:response) { double(Net::HTTPResponse, code: 200) }
8
+ let(:http) { double(Net::HTTP::Persistent, request: response, shutdown: nil) }
9
+ let(:priority) { 'normal' }
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.registration_ids = ['foo']
18
+ notification.data = { message: 'test' }
19
+ notification.priority = priority
20
+ notification.save!
21
+
22
+ allow(Net::HTTP::Persistent).to receive_messages(new: http)
23
+ end
24
+
25
+ it 'supports normal priority' do
26
+ expect(hydrated_notification.as_json['priority']).to eq('normal')
27
+ end
28
+
29
+ context 'high priority' do
30
+ let(:priority) { 'high' }
31
+
32
+ it 'supports high priority' do
33
+ expect(hydrated_notification.as_json['priority']).to eq('high')
34
+ end
35
+ end
36
+
37
+ it 'does not add an error when receiving expected priority' do
38
+ expect(hydrated_notification.errors.messages[:priority]).to be_empty
39
+ end
40
+ end
@@ -40,6 +40,8 @@ require 'generators/templates/rpush_3_2_4_updates'
40
40
  require 'generators/templates/rpush_3_3_0_updates'
41
41
  require 'generators/templates/rpush_3_3_1_updates'
42
42
  require 'generators/templates/rpush_4_1_0_updates'
43
+ require 'generators/templates/rpush_4_1_1_updates'
44
+ require 'generators/templates/rpush_4_2_0_updates'
43
45
 
44
46
  migrations = [
45
47
  AddRpush,
@@ -55,7 +57,9 @@ migrations = [
55
57
  Rpush324Updates,
56
58
  Rpush330Updates,
57
59
  Rpush331Updates,
58
- Rpush410Updates
60
+ Rpush410Updates,
61
+ Rpush411Updates,
62
+ Rpush420Updates
59
63
  ]
60
64
 
61
65
  unless ENV['TRAVIS']
@@ -18,7 +18,7 @@ module SimpleCovHelper
18
18
  end
19
19
  end
20
20
 
21
- formatter SimpleCov::Formatter::MultiFormatter[*formatters]
21
+ formatter SimpleCov::Formatter::MultiFormatter.new(formatters)
22
22
  end
23
23
  end
24
24
  end
@@ -1,13 +1,15 @@
1
1
  require 'unit_spec_helper'
2
2
 
3
3
  describe Rpush, 'apns_feedback' do
4
- let!(:apns_app) do
5
- Rpush::Apns::App.create!(name: 'test', environment: 'production', certificate: TEST_CERT)
6
- end
7
-
8
- let!(:gcm_app) do
9
- Rpush::Gcm::App.create!(name: 'MyApp', auth_key: 'abc123')
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
+ }
10
11
  end
12
+ let!(:gcm_app) { Rpush::Gcm::App.create!(name: 'MyApp', auth_key: 'abc123') }
11
13
 
12
14
  let(:receiver) { double(check_for_feedback: nil) }
13
15
 
@@ -25,4 +27,13 @@ describe Rpush, 'apns_feedback' do
25
27
  expect(receiver).to receive(:check_for_feedback)
26
28
  Rpush.apns_feedback
27
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
28
39
  end
@@ -1,58 +1,6 @@
1
1
  require 'unit_spec_helper'
2
2
 
3
3
  describe Rpush::Client::ActiveRecord::Adm::App do
4
- subject { Rpush::Client::ActiveRecord::Adm::App.new(name: 'test', environment: 'development', client_id: 'CLIENT_ID', client_secret: 'CLIENT_SECRET') }
5
- let(:existing_app) { Rpush::Client::ActiveRecord::Adm::App.create!(name: 'existing', environment: 'development', client_id: 'CLIENT_ID', client_secret: 'CLIENT_SECRET') }
6
-
7
- it 'should be valid if properly instantiated' do
8
- expect(subject).to be_valid
9
- end
10
-
11
- it 'should be invalid if name' do
12
- subject.name = nil
13
- expect(subject).not_to be_valid
14
- expect(subject.errors[:name]).to eq ["can't be blank"]
15
- end
16
-
17
- it 'should be invalid if name is not unique within scope' do
18
- subject.name = existing_app.name
19
- expect(subject).not_to be_valid
20
- expect(subject.errors[:name]).to eq ["has already been taken"]
21
- end
22
-
23
- it 'should be invalid if missing client_id' do
24
- subject.client_id = nil
25
- expect(subject).not_to be_valid
26
- expect(subject.errors[:client_id]).to eq ["can't be blank"]
27
- end
28
-
29
- it 'should be invalid if missing client_secret' do
30
- subject.client_secret = nil
31
- expect(subject).not_to be_valid
32
- expect(subject.errors[:client_secret]).to eq ["can't be blank"]
33
- end
34
-
35
- describe '#access_token_expired?' do
36
- before(:each) do
37
- Timecop.freeze(Time.now)
38
- end
39
-
40
- after do
41
- Timecop.return
42
- end
43
-
44
- it 'should return true if access_token_expiration is nil' do
45
- expect(subject.access_token_expired?).to eq(true)
46
- end
47
-
48
- it 'should return true if expired' do
49
- subject.access_token_expiration = Time.now - 5.minutes
50
- expect(subject.access_token_expired?).to eq(true)
51
- end
52
-
53
- it 'should return false if not expired' do
54
- subject.access_token_expiration = Time.now + 5.minutes
55
- expect(subject.access_token_expired?).to eq(false)
56
- end
57
- end
4
+ it_behaves_like 'Rpush::Client::Adm::App'
5
+ it_behaves_like 'Rpush::Client::ActiveRecord::App'
58
6
  end if active_record?
@@ -1,43 +1,6 @@
1
1
  require 'unit_spec_helper'
2
- require 'unit/notification_shared.rb'
3
2
 
4
3
  describe Rpush::Client::ActiveRecord::Adm::Notification do
5
- it_should_behave_like 'an Notification subclass'
6
-
7
- let(:app) { Rpush::Client::ActiveRecord::Adm::App.create!(name: 'test', client_id: 'CLIENT_ID', client_secret: 'CLIENT_SECRET') }
8
- let(:notification_class) { Rpush::Client::ActiveRecord::Adm::Notification }
9
- let(:notification) { notification_class.new }
10
-
11
- it "has a 'data' payload limit of 6144 bytes" do
12
- notification.data = { key: "a" * 6144 }
13
- expect(notification.valid?).to eq(false)
14
- expect(notification.errors[:base]).to eq ["Notification payload data cannot be larger than 6144 bytes."]
15
- end
16
-
17
- it 'limits the number of registration ids to 100' do
18
- notification.registration_ids = ['a'] * (100 + 1)
19
- expect(notification.valid?).to eq(false)
20
- expect(notification.errors[:base]).to eq ["Number of registration_ids cannot be larger than 100."]
21
- end
22
-
23
- it 'validates data can be blank if collapse_key is set' do
24
- notification.app = app
25
- notification.registration_ids = 'a'
26
- notification.collapse_key = 'test'
27
- notification.data = nil
28
- expect(notification.valid?).to eq(true)
29
- expect(notification.errors[:data]).to be_empty
30
- end
31
-
32
- it 'validates data is present if collapse_key is not set' do
33
- notification.collapse_key = nil
34
- notification.data = nil
35
- expect(notification.valid?).to eq(false)
36
- expect(notification.errors[:data]).to eq ['must be set unless collapse_key is specified']
37
- end
38
-
39
- it 'includes expiresAfter in the payload' do
40
- notification.expiry = 100
41
- expect(notification.as_json['expiresAfter']).to eq 100
42
- end
4
+ it_behaves_like 'Rpush::Client::Adm::Notification'
5
+ it_behaves_like 'Rpush::Client::ActiveRecord::Notification'
43
6
  end if active_record?
@@ -1,29 +1,6 @@
1
1
  require 'unit_spec_helper'
2
2
 
3
- describe Rpush::Client::ActiveRecord::App do
4
- it 'does not validate an app with an invalid certificate' do
5
- app = Rpush::Client::ActiveRecord::Apns::App.new(name: 'test', environment: 'development', certificate: 'foo')
6
- app.valid?
7
- expect(app.errors[:certificate]).to eq ['value must contain a certificate and a private key.']
8
- end
9
-
10
- it 'validates a certificate without a password' do
11
- app = Rpush::Client::ActiveRecord::Apns::App.new name: 'test', environment: 'development', certificate: TEST_CERT
12
- app.valid?
13
- expect(app.errors[:certificate]).to eq []
14
- end
15
-
16
- it 'validates a certificate with a password' do
17
- app = Rpush::Client::ActiveRecord::Apns::App.new name: 'test', environment: 'development',
18
- certificate: TEST_CERT_WITH_PASSWORD, password: 'fubar'
19
- app.valid?
20
- expect(app.errors[:certificate]).to eq []
21
- end
22
-
23
- it 'validates a certificate with an incorrect password' do
24
- app = Rpush::Client::ActiveRecord::Apns::App.new name: 'test', environment: 'development',
25
- certificate: TEST_CERT_WITH_PASSWORD, password: 'incorrect'
26
- app.valid?
27
- expect(app.errors[:certificate]).to eq ['value must contain a certificate and a private key.']
28
- end
3
+ describe Rpush::Client::ActiveRecord::Apns::App do
4
+ it_behaves_like 'Rpush::Client::Apns::App'
5
+ it_behaves_like 'Rpush::Client::ActiveRecord::App'
29
6
  end if active_record?
@@ -1,9 +1,5 @@
1
1
  require 'unit_spec_helper'
2
2
 
3
3
  describe Rpush::Client::ActiveRecord::Apns::Feedback do
4
- it 'validates the format of the device_token' do
5
- notification = Rpush::Client::ActiveRecord::Apns::Feedback.new(device_token: "{$%^&*()}")
6
- expect(notification.valid?).to be_falsey
7
- expect(notification.errors[:device_token]).to include('is invalid')
8
- end
4
+ it_behaves_like 'Rpush::Client::Apns::Feedback'
9
5
  end if active_record?
@@ -1,324 +1,65 @@
1
- # encoding: US-ASCII
2
-
3
1
  require "unit_spec_helper"
4
- require 'unit/notification_shared.rb'
5
2
 
6
3
  describe Rpush::Client::ActiveRecord::Apns::Notification do
7
- it_should_behave_like 'an Notification subclass'
8
-
9
- let(:app) { Rpush::Client::ActiveRecord::Apns::App.create!(name: 'my_app', environment: 'development', certificate: TEST_CERT) }
10
- let(:notification_class) { Rpush::Client::ActiveRecord::Apns::Notification }
11
- let(:notification) { notification_class.new }
4
+ subject(:notification) { described_class.new }
12
5
 
13
- it "should validate the format of the device_token" do
14
- notification = Rpush::Client::ActiveRecord::Apns::Notification.new(device_token: "{$%^&*()}")
15
- expect(notification.valid?).to be_falsey
16
- expect(notification.errors[:device_token].include?("is invalid")).to be_truthy
17
- end
6
+ it_behaves_like 'Rpush::Client::Apns::Notification'
7
+ it_behaves_like 'Rpush::Client::ActiveRecord::Notification'
18
8
 
19
9
  it "should validate the length of the binary conversion of the notification" do
10
+ notification = described_class.new
11
+ notification.app = Rpush::Apns2::App.create(name: 'test', environment: 'development')
20
12
  notification.device_token = "a" * 108
21
- notification.alert = "way too long!" * 200
22
- expect(notification.valid?).to be_falsey
23
- expect(notification.errors[:base].include?("APN notification cannot be larger than 2048 bytes. Try condensing your alert and device attributes.")).to be_truthy
24
- end
13
+ notification.alert = ""
25
14
 
26
- it "should store long alerts" do
27
- notification.app = app
28
- notification.device_token = "a" * 108
29
- notification.alert = "*" * 300
15
+ notification.alert << "a" until notification.payload.bytesize == 2048
30
16
  expect(notification.valid?).to be_truthy
17
+ expect(notification.errors[:base]).to be_empty
31
18
 
32
- notification.save!
33
- notification.reload
34
- expect(notification.alert).to eq("*" * 300)
35
- end
36
-
37
- it "should default the sound to nil" do
38
- expect(notification.sound).to be_nil
39
- end
40
-
41
- it "should default the expiry to 1 day" do
42
- expect(notification.expiry).to eq 1.day.to_i
43
- end
44
- end if active_record?
45
-
46
- describe Rpush::Client::ActiveRecord::Apns::Notification, "when assigning the device token" do
47
- it "should strip spaces from the given string" do
48
- notification = Rpush::Client::ActiveRecord::Apns::Notification.new(device_token: "o m g")
49
- expect(notification.device_token).to eq "omg"
50
- end
51
-
52
- it "should strip chevrons from the given string" do
53
- notification = Rpush::Client::ActiveRecord::Apns::Notification.new(device_token: "<omg>")
54
- expect(notification.device_token).to eq "omg"
55
- end
56
- end if active_record?
57
-
58
- describe Rpush::Client::ActiveRecord::Apns::Notification, "as_json" do
59
- it "should include the alert if present" do
60
- notification = Rpush::Client::ActiveRecord::Apns::Notification.new(alert: "hi mom")
61
- expect(notification.as_json["aps"]["alert"]).to eq "hi mom"
62
- end
63
-
64
- it "should not include the alert key if the alert is not present" do
65
- notification = Rpush::Client::ActiveRecord::Apns::Notification.new(alert: nil)
66
- expect(notification.as_json["aps"].key?("alert")).to be_falsey
67
- end
68
-
69
- it "should encode the alert as JSON if it is a Hash" do
70
- notification = Rpush::Client::ActiveRecord::Apns::Notification.new(alert: { 'body' => "hi mom", 'alert-loc-key' => "View" })
71
- expect(notification.as_json["aps"]["alert"]).to eq('body' => "hi mom", 'alert-loc-key' => "View")
72
- end
73
-
74
- it "should include the badge if present" do
75
- notification = Rpush::Client::ActiveRecord::Apns::Notification.new(badge: 6)
76
- expect(notification.as_json["aps"]["badge"]).to eq 6
77
- end
78
-
79
- it "should not include the badge key if the badge is not present" do
80
- notification = Rpush::Client::ActiveRecord::Apns::Notification.new(badge: nil)
81
- expect(notification.as_json["aps"].key?("badge")).to be_falsey
82
- end
83
-
84
- it "should include the sound if present" do
85
- notification = Rpush::Client::ActiveRecord::Apns::Notification.new(alert: "my_sound.aiff")
86
- expect(notification.as_json["aps"]["alert"]).to eq "my_sound.aiff"
87
- end
88
-
89
- it "should not include the sound key if the sound is not present" do
90
- notification = Rpush::Client::ActiveRecord::Apns::Notification.new(sound: nil)
91
- expect(notification.as_json["aps"].key?("sound")).to be_falsey
92
- end
93
-
94
- it "should include attributes for the device" do
95
- notification = Rpush::Client::ActiveRecord::Apns::Notification.new
96
- notification.data = { omg: :lol, wtf: :dunno }
97
- expect(notification.as_json["omg"]).to eq "lol"
98
- expect(notification.as_json["wtf"]).to eq "dunno"
99
- end
100
-
101
- it "should allow attributes to include a hash" do
102
- notification = Rpush::Client::ActiveRecord::Apns::Notification.new
103
- notification.data = { omg: { ilike: :hashes } }
104
- expect(notification.as_json["omg"]["ilike"]).to eq "hashes"
105
- end
106
- end if active_record?
107
-
108
- describe Rpush::Client::ActiveRecord::Apns::Notification, 'MDM' do
109
- let(:magic) { 'abc123' }
110
- let(:notification) { Rpush::Client::ActiveRecord::Apns::Notification.new }
111
-
112
- before do
113
- notification.device_token = "a" * 108
114
- notification.id = 1234
115
- end
116
-
117
- it 'includes the mdm magic in the payload' do
118
- notification.mdm = magic
119
- expect(notification.as_json).to eq('mdm' => magic)
120
- end
121
-
122
- it 'does not include aps attribute' do
123
- notification.alert = "i'm doomed"
124
- notification.mdm = magic
125
- expect(notification.as_json.key?('aps')).to be_falsey
126
- end
127
-
128
- it 'can be converted to binary' do
129
- notification.mdm = magic
130
- expect(notification.to_binary).to be_present
19
+ notification.alert << "a"
20
+ expect(notification.valid?).to be_falsey
21
+ expect(notification.errors[:base].include?("APN notification cannot be larger than 2048 bytes. Try condensing your alert and device attributes.")).to be_truthy
131
22
  end
132
- end if active_record?
133
23
 
134
- describe Rpush::Client::ActiveRecord::Apns::Notification, 'mutable-content' do
135
- let(:notification) { Rpush::Client::ActiveRecord::Apns::Notification.new }
24
+ describe "multi_json usage" do
25
+ describe "alert" do
26
+ subject(:notification) { described_class.new(alert: { a: 1 }, alert_is_json: true) }
136
27
 
137
- it 'includes mutable-content in the payload' do
138
- notification.mutable_content = true
139
- expect(notification.as_json['aps']['mutable-content']).to eq 1
140
- end
28
+ it "should call MultiJson.load when multi_json version is 1.3.0" do
29
+ allow(Gem).to receive(:loaded_specs).and_return('multi_json' => Gem::Specification.new('multi_json', '1.3.0'))
30
+ expect(MultiJson).to receive(:load).with(any_args)
31
+ notification.alert
32
+ end
141
33
 
142
- it 'does not include content-available in the payload if not set' do
143
- expect(notification.as_json['aps'].key?('mutable-content')).to be_falsey
144
- end
145
-
146
- it 'does not include mutable-content as a non-aps attribute' do
147
- notification.mutable_content = true
148
- expect(notification.as_json.key?('mutable-content')).to be_falsey
34
+ it "should call MultiJson.decode when multi_json version is 1.2.9" do
35
+ allow(Gem).to receive(:loaded_specs).and_return('multi_json' => Gem::Specification.new('multi_json', '1.2.9'))
36
+ expect(MultiJson).to receive(:decode).with(any_args)
37
+ notification.alert
38
+ end
39
+ end
149
40
  end
150
41
 
151
- it 'does not overwrite existing attributes for the device' do
152
- notification.data = { hi: :mom }
153
- notification.mutable_content = true
154
- expect(notification.as_json['aps']['mutable-content']).to eq 1
155
- expect(notification.as_json['hi']).to eq 'mom'
42
+ it "should default the sound to nil" do
43
+ expect(notification.sound).to be_nil
156
44
  end
157
45
 
158
46
  it 'does not overwrite the mutable-content flag when setting attributes for the device' do
159
47
  notification.mutable_content = true
160
- notification.data = { hi: :mom }
48
+ notification.data = { 'hi' => 'mom' }
161
49
  expect(notification.as_json['aps']['mutable-content']).to eq 1
162
50
  expect(notification.as_json['hi']).to eq 'mom'
163
51
  end
164
- end if active_record?
165
-
166
- describe Rpush::Client::ActiveRecord::Apns::Notification, 'content-available' do
167
- let(:notification) { Rpush::Client::ActiveRecord::Apns::Notification.new }
168
-
169
- it 'includes content-available in the payload' do
170
- notification.content_available = true
171
- expect(notification.as_json['aps']['content-available']).to eq 1
172
- end
173
-
174
- it 'does not include content-available in the payload if not set' do
175
- expect(notification.as_json['aps'].key?('content-available')).to be_falsey
176
- end
177
-
178
- it 'does not include content-available as a non-aps attribute' do
179
- notification.content_available = true
180
- expect(notification.as_json.key?('content-available')).to be_falsey
181
- end
182
-
183
- it 'does not overwrite existing attributes for the device' do
184
- notification.data = { hi: :mom }
185
- notification.content_available = true
186
- expect(notification.as_json['aps']['content-available']).to eq 1
187
- expect(notification.as_json['hi']).to eq 'mom'
188
- end
189
52
 
190
53
  it 'does not overwrite the content-available flag when setting attributes for the device' do
191
54
  notification.content_available = true
192
- notification.data = { hi: :mom }
55
+ notification.data = { 'hi' => 'mom' }
193
56
  expect(notification.as_json['aps']['content-available']).to eq 1
194
57
  expect(notification.as_json['hi']).to eq 'mom'
195
58
  end
196
- end if active_record?
197
-
198
- describe Rpush::Client::ActiveRecord::Apns::Notification, 'url-args' do
199
- let(:notification) { Rpush::Client::ActiveRecord::Apns::Notification.new }
200
-
201
- it 'includes url-args in the payload' do
202
- notification.url_args = ['url-arg-1']
203
- expect(notification.as_json['aps']['url-args']).to eq ['url-arg-1']
204
- end
205
-
206
- it 'does not include url-args in the payload if not set' do
207
- expect(notification.as_json['aps'].key?('url-args')).to be_falsey
208
- end
209
- end if active_record?
210
-
211
- describe Rpush::Client::ActiveRecord::Apns::Notification, 'category' do
212
- let(:notification) { Rpush::Client::ActiveRecord::Apns::Notification.new }
213
-
214
- it 'includes category in the payload' do
215
- notification.category = 'INVITE_CATEGORY'
216
- expect(notification.as_json['aps']['category']).to eq 'INVITE_CATEGORY'
217
- end
218
-
219
- it 'does not include category in the payload if not set' do
220
- expect(notification.as_json['aps'].key?('category')).to be_falsey
221
- end
222
- end if active_record?
223
-
224
- describe Rpush::Client::ActiveRecord::Apns::Notification, 'to_binary' do
225
- let(:notification) { Rpush::Client::ActiveRecord::Apns::Notification.new }
226
-
227
- before do
228
- notification.device_token = "a" * 108
229
- notification.id = 1234
230
- end
231
-
232
- it 'uses APNS_PRIORITY_CONSERVE_POWER if content-available is the only key' do
233
- notification.alert = nil
234
- notification.badge = nil
235
- notification.sound = nil
236
- notification.content_available = true
237
- bytes = notification.to_binary.bytes.to_a[-4..-1]
238
- expect(bytes.first).to eq 5 # priority item ID
239
- expect(bytes.last).to eq Rpush::Client::ActiveRecord::Apns::Notification::APNS_PRIORITY_CONSERVE_POWER
240
- end
241
-
242
- it 'uses APNS_PRIORITY_IMMEDIATE if content-available is not the only key' do
243
- notification.alert = "New stuff!"
244
- notification.badge = nil
245
- notification.sound = nil
246
- notification.content_available = true
247
- bytes = notification.to_binary.bytes.to_a[-4..-1]
248
- expect(bytes.first).to eq 5 # priority item ID
249
- expect(bytes.last).to eq Rpush::Client::ActiveRecord::Apns::Notification::APNS_PRIORITY_IMMEDIATE
250
- end
251
-
252
- it "should correctly convert the notification to binary" do
253
- notification.sound = "1.aiff"
254
- notification.badge = 3
255
- notification.alert = "Don't panic Mr Mainwaring, don't panic!"
256
- notification.data = { hi: :mom }
257
- notification.expiry = 86_400 # 1 day
258
- notification.priority = Rpush::Client::ActiveRecord::Apns::Notification::APNS_PRIORITY_IMMEDIATE
259
- notification.app = Rpush::Client::ActiveRecord::Apns::App.new(name: 'my_app', environment: 'development', certificate: TEST_CERT)
260
- now = Time.now
261
- allow(Time).to receive_messages(now: now)
262
- expect(notification.to_binary).to eq "\x02\x00\x00\x00\xAF\x01\x00 \xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\x02\x00a{\"aps\":{\"alert\":\"Don't panic Mr Mainwaring, don't panic!\",\"badge\":3,\"sound\":\"1.aiff\"},\"hi\":\"mom\"}\x03\x00\x04\x00\x00\x04\xD2\x04\x00\x04#{[now.to_i + 86_400].pack('N')}\x05\x00\x01\n"
263
- end
264
- end if active_record?
265
-
266
- describe Rpush::Client::ActiveRecord::Apns::Notification, "bug #31" do
267
- it 'does not confuse a JSON looking string as JSON' do
268
- notification = Rpush::Client::ActiveRecord::Apns::Notification.new
269
- notification.alert = "{\"one\":2}"
270
- expect(notification.alert).to eq "{\"one\":2}"
271
- end
272
59
 
273
60
  it 'does confuse a JSON looking string as JSON if the alert_is_json attribute is not present' do
274
- notification = Rpush::Client::ActiveRecord::Apns::Notification.new
275
61
  allow(notification).to receive_messages(has_attribute?: false)
276
62
  notification.alert = "{\"one\":2}"
277
63
  expect(notification.alert).to eq('one' => 2)
278
64
  end
279
65
  end if active_record?
280
-
281
- describe Rpush::Client::ActiveRecord::Apns::Notification, "bug #35" do
282
- it "should limit payload size to 256 bytes but not the entire packet" do
283
- notification = Rpush::Client::ActiveRecord::Apns::Notification.new do |n|
284
- n.device_token = "a" * 108
285
- n.alert = "a" * 210
286
- n.app = Rpush::Client::ActiveRecord::Apns::App.create!(name: 'my_app', environment: 'development', certificate: TEST_CERT)
287
- end
288
-
289
- expect(notification.to_binary(for_validation: true).bytesize).to be > 256
290
- expect(notification.payload.bytesize).to be < 256
291
- expect(notification).to be_valid
292
- end
293
- end if active_record?
294
-
295
- describe Rpush::Client::ActiveRecord::Apns::Notification, "multi_json usage" do
296
- describe Rpush::Client::ActiveRecord::Apns::Notification, "alert" do
297
- it "should call MultiJson.load when multi_json version is 1.3.0" do
298
- notification = Rpush::Client::ActiveRecord::Apns::Notification.new(alert: { a: 1 }, alert_is_json: true)
299
- allow(Gem).to receive(:loaded_specs).and_return('multi_json' => Gem::Specification.new('multi_json', '1.3.0'))
300
- expect(MultiJson).to receive(:load).with(any_args)
301
- notification.alert
302
- end
303
-
304
- it "should call MultiJson.decode when multi_json version is 1.2.9" do
305
- notification = Rpush::Client::ActiveRecord::Apns::Notification.new(alert: { a: 1 }, alert_is_json: true)
306
- allow(Gem).to receive(:loaded_specs).and_return('multi_json' => Gem::Specification.new('multi_json', '1.2.9'))
307
- expect(MultiJson).to receive(:decode).with(any_args)
308
- notification.alert
309
- end
310
- end
311
- end if active_record?
312
-
313
- describe Rpush::Client::ActiveRecord::Apns::Notification, 'thread-id' do
314
- let(:notification) { Rpush::Client::ActiveRecord::Apns::Notification.new }
315
-
316
- it 'includes thread-id in the payload' do
317
- notification.thread_id = 'THREAD-ID'
318
- expect(notification.as_json['aps']['thread-id']).to eq 'THREAD-ID'
319
- end
320
-
321
- it 'does not include thread-id in the payload if not set' do
322
- expect(notification.as_json['aps']).to_not have_key('thread-id')
323
- end
324
- end if active_record?