rpush 5.0.0 → 5.1.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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -2
  3. data/lib/rpush/cli.rb +1 -1
  4. data/lib/rpush/client/active_model.rb +1 -1
  5. data/lib/rpush/client/active_model/apns/notification.rb +9 -1
  6. data/lib/rpush/client/active_model/apns/notification_payload_size_validator.rb +15 -0
  7. data/lib/rpush/client/active_model/apns2/notification.rb +14 -0
  8. data/lib/rpush/client/active_record.rb +1 -0
  9. data/lib/rpush/client/active_record/apns/active_record_serializable_notification.rb +65 -0
  10. data/lib/rpush/client/active_record/apns/notification.rb +1 -57
  11. data/lib/rpush/client/active_record/apns2/notification.rb +4 -1
  12. data/lib/rpush/client/redis/apns2/notification.rb +1 -0
  13. data/lib/rpush/client/redis/pushy/notification.rb +0 -1
  14. data/lib/rpush/daemon/apns2/delivery.rb +6 -1
  15. data/lib/rpush/daemon/apnsp8/delivery.rb +7 -2
  16. data/lib/rpush/daemon/store/active_record/reconnectable.rb +1 -1
  17. data/lib/rpush/version.rb +1 -1
  18. data/spec/functional/apns2_spec.rb +36 -0
  19. data/spec/support/simplecov_helper.rb +1 -1
  20. data/spec/unit/client/active_record/adm/app_spec.rb +2 -54
  21. data/spec/unit/client/active_record/adm/notification_spec.rb +2 -39
  22. data/spec/unit/client/active_record/apns/app_spec.rb +3 -26
  23. data/spec/unit/client/active_record/apns/feedback_spec.rb +1 -5
  24. data/spec/unit/client/active_record/apns/notification_spec.rb +29 -293
  25. data/spec/unit/client/active_record/apns2/app_spec.rb +4 -0
  26. data/spec/unit/client/active_record/apns2/notification_spec.rb +65 -0
  27. data/spec/unit/client/active_record/app_spec.rb +1 -26
  28. data/spec/unit/client/active_record/gcm/app_spec.rb +3 -1
  29. data/spec/unit/client/active_record/gcm/notification_spec.rb +6 -88
  30. data/spec/unit/client/active_record/notification_spec.rb +3 -11
  31. data/spec/unit/client/active_record/pushy/app_spec.rb +2 -13
  32. data/spec/unit/client/active_record/pushy/notification_spec.rb +2 -55
  33. data/spec/unit/client/active_record/shared/app.rb +14 -0
  34. data/spec/unit/{notification_shared.rb → client/active_record/shared/notification.rb} +12 -7
  35. data/spec/unit/client/active_record/wns/badge_notification_spec.rb +1 -11
  36. data/spec/unit/client/active_record/wns/raw_notification_spec.rb +3 -12
  37. data/spec/unit/client/active_record/wpns/app_spec.rb +3 -1
  38. data/spec/unit/client/active_record/wpns/notification_spec.rb +2 -17
  39. data/spec/unit/client/redis/adm/app_spec.rb +5 -0
  40. data/spec/unit/client/redis/adm/notification_spec.rb +5 -0
  41. data/spec/unit/client/redis/apns/app_spec.rb +5 -0
  42. data/spec/unit/client/redis/apns/feedback_spec.rb +5 -0
  43. data/spec/unit/client/redis/apns/notification_spec.rb +50 -0
  44. data/spec/unit/client/redis/apns2/app_spec.rb +4 -0
  45. data/spec/unit/client/redis/apns2/notification_spec.rb +50 -0
  46. data/spec/unit/client/redis/app_spec.rb +5 -0
  47. data/spec/unit/client/redis/gcm/app_spec.rb +5 -0
  48. data/spec/unit/client/redis/gcm/notification_spec.rb +5 -0
  49. data/spec/unit/client/redis/notification_spec.rb +5 -0
  50. data/spec/unit/client/redis/pushy/app_spec.rb +5 -0
  51. data/spec/unit/client/redis/pushy/notification_spec.rb +5 -0
  52. data/spec/unit/client/redis/wns/badge_notification_spec.rb +5 -0
  53. data/spec/unit/client/redis/wns/raw_notification_spec.rb +22 -0
  54. data/spec/unit/client/redis/wpns/app_spec.rb +5 -0
  55. data/spec/unit/client/redis/wpns/notification_spec.rb +5 -0
  56. data/spec/unit/client/shared/adm/app.rb +51 -0
  57. data/spec/unit/client/shared/adm/notification.rb +39 -0
  58. data/spec/unit/client/shared/apns/app.rb +29 -0
  59. data/spec/unit/client/shared/apns/feedback.rb +9 -0
  60. data/spec/unit/client/shared/apns/notification.rb +262 -0
  61. data/spec/unit/client/shared/app.rb +17 -0
  62. data/spec/unit/client/shared/gcm/app.rb +4 -0
  63. data/spec/unit/client/shared/gcm/notification.rb +77 -0
  64. data/spec/unit/client/shared/notification.rb +10 -0
  65. data/spec/unit/client/shared/pushy/app.rb +17 -0
  66. data/spec/unit/client/shared/pushy/notification.rb +55 -0
  67. data/spec/unit/client/shared/wns/badge_notification.rb +15 -0
  68. data/spec/unit/client/shared/wns/raw_notification.rb +21 -0
  69. data/spec/unit/client/shared/wpns/app.rb +4 -0
  70. data/spec/unit/client/shared/wpns/notification.rb +18 -0
  71. data/spec/unit/daemon/shared/store.rb +312 -0
  72. data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +7 -7
  73. data/spec/unit/daemon/store/active_record_spec.rb +2 -290
  74. data/spec/unit/daemon/store/redis_spec.rb +2 -291
  75. data/spec/unit_spec_helper.rb +3 -0
  76. metadata +89 -10
  77. data/lib/rpush/client/active_model/apns/binary_notification_validator.rb +0 -16
@@ -0,0 +1,5 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Client::Redis::Adm::App do
4
+ it_behaves_like 'Rpush::Client::Adm::App'
5
+ end if redis?
@@ -0,0 +1,5 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Client::Redis::Adm::Notification do
4
+ it_behaves_like 'Rpush::Client::Adm::Notification'
5
+ end if redis?
@@ -0,0 +1,5 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Client::Redis::Apns::App do
4
+ it_behaves_like 'Rpush::Client::Apns::App'
5
+ end if redis?
@@ -0,0 +1,5 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Client::Redis::Apns::Feedback do
4
+ it_behaves_like 'Rpush::Client::Apns::Feedback'
5
+ end if redis?
@@ -0,0 +1,50 @@
1
+ # encoding: US-ASCII
2
+
3
+ require "unit_spec_helper"
4
+
5
+ describe Rpush::Client::Redis::Apns::Notification do
6
+ it_behaves_like 'Rpush::Client::Apns::Notification'
7
+
8
+ it "should validate the length of the binary conversion of the notification" do
9
+ notification = described_class.new
10
+ notification.app = Rpush::Apns2::App.create(name: 'test', environment: 'development')
11
+ notification.device_token = "a" * 108
12
+ notification.alert = ""
13
+
14
+ notification.alert << "a" until notification.payload.bytesize == 2048
15
+ expect(notification.valid?).to be_truthy
16
+ expect(notification.errors[:base]).to be_empty
17
+
18
+ notification.alert << "a"
19
+ expect(notification.valid?).to be_falsey
20
+ expect(notification.errors[:base].include?("APN notification cannot be larger than 2048 bytes. Try condensing your alert and device attributes.")).to be_truthy
21
+ end
22
+
23
+ it "should default the sound to 'default'" do
24
+ notification = described_class.new
25
+ expect(notification.sound).to eq('default')
26
+ end
27
+
28
+ # skipping these tests because data= for redis doesn't merge existing data
29
+ xit 'does not overwrite the mutable-content flag when setting attributes for the device' do
30
+ notification.mutable_content = true
31
+ notification.data = { 'hi' => 'mom' }
32
+ expect(notification.as_json['aps']['mutable-content']).to eq 1
33
+ expect(notification.as_json['hi']).to eq 'mom'
34
+ end
35
+
36
+ xit 'does not overwrite the content-available flag when setting attributes for the device' do
37
+ notification.content_available = true
38
+ notification.data = { 'hi' => 'mom' }
39
+ expect(notification.as_json['aps']['content-available']).to eq 1
40
+ expect(notification.as_json['hi']).to eq 'mom'
41
+ end
42
+
43
+ # redis does not use alert_is_json - unclear if that is a bug or desired behavior
44
+ xit 'does confuse a JSON looking string as JSON if the alert_is_json attribute is not present' do
45
+ notification = described_class.new
46
+ allow(notification).to receive_messages(has_attribute?: false)
47
+ notification.alert = "{\"one\":2}"
48
+ expect(notification.alert).to eq('one' => 2)
49
+ end
50
+ end if redis?
@@ -0,0 +1,4 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Client::Redis::Apns2::App do
4
+ end if redis?
@@ -0,0 +1,50 @@
1
+ # encoding: US-ASCII
2
+
3
+ require "unit_spec_helper"
4
+
5
+ describe Rpush::Client::Redis::Apns2::Notification do
6
+ it_behaves_like 'Rpush::Client::Apns::Notification'
7
+
8
+ it "should validate the length of the binary conversion of the notification" do
9
+ notification = described_class.new
10
+ notification.app = Rpush::Apns2::App.create(name: 'test', environment: 'development')
11
+ notification.device_token = "a" * 108
12
+ notification.alert = ""
13
+
14
+ notification.alert << "a" until notification.payload.bytesize == 4096
15
+ expect(notification.valid?).to be_truthy
16
+ expect(notification.errors[:base]).to be_empty
17
+
18
+ notification.alert << "a"
19
+ expect(notification.valid?).to be_falsey
20
+ expect(notification.errors[:base].include?("APN notification cannot be larger than 4096 bytes. Try condensing your alert and device attributes.")).to be_truthy
21
+ end
22
+
23
+ it "should default the sound to 'default'" do
24
+ notification = described_class.new
25
+ expect(notification.sound).to eq('default')
26
+ end
27
+
28
+ # skipping these tests because data= for redis doesn't merge existing data
29
+ xit 'does not overwrite the mutable-content flag when setting attributes for the device' do
30
+ notification.mutable_content = true
31
+ notification.data = { 'hi' => 'mom' }
32
+ expect(notification.as_json['aps']['mutable-content']).to eq 1
33
+ expect(notification.as_json['hi']).to eq 'mom'
34
+ end
35
+
36
+ xit 'does not overwrite the content-available flag when setting attributes for the device' do
37
+ notification.content_available = true
38
+ notification.data = { 'hi' => 'mom' }
39
+ expect(notification.as_json['aps']['content-available']).to eq 1
40
+ expect(notification.as_json['hi']).to eq 'mom'
41
+ end
42
+
43
+ # redis does not use alert_is_json - unclear if that is a bug or desired behavior
44
+ xit 'does confuse a JSON looking string as JSON if the alert_is_json attribute is not present' do
45
+ notification = described_class.new
46
+ allow(notification).to receive_messages(has_attribute?: false)
47
+ notification.alert = "{\"one\":2}"
48
+ expect(notification.alert).to eq('one' => 2)
49
+ end
50
+ end if redis?
@@ -0,0 +1,5 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Client::Redis::App do
4
+ it_behaves_like 'Rpush::Client::App'
5
+ end if redis?
@@ -0,0 +1,5 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Client::Redis::Gcm::App do
4
+ it_behaves_like 'Rpush::Client::Gcm::App'
5
+ end if redis?
@@ -0,0 +1,5 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Client::Redis::Gcm::Notification do
4
+ it_behaves_like 'Rpush::Client::Gcm::Notification'
5
+ end if redis?
@@ -0,0 +1,5 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Client::Redis::Notification do
4
+ it_behaves_like 'Rpush::Client::Notification'
5
+ end if redis?
@@ -0,0 +1,5 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Client::Redis::Pushy::App do
4
+ it_behaves_like 'Rpush::Client::Pushy::App'
5
+ end if redis?
@@ -0,0 +1,5 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Client::Redis::Pushy::Notification do
4
+ it_behaves_like 'Rpush::Client::Pushy::Notification'
5
+ end if redis?
@@ -0,0 +1,5 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Client::Redis::Wns::BadgeNotification do
4
+ it_behaves_like 'Rpush::Client::Wns::BadgeNotification'
5
+ end if redis?
@@ -0,0 +1,22 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Client::Redis::Wns::RawNotification do
4
+ it_behaves_like 'Rpush::Client::Wns::RawNotification'
5
+
6
+ subject(:notification) do
7
+ notif = described_class.new
8
+ notif.app = Rpush::Wns::App.create!(name: "MyApp", client_id: "someclient", client_secret: "somesecret")
9
+ notif.uri = 'https://db5.notify.windows.com/?token=TOKEN'
10
+ notif.data = { foo: 'foo', bar: 'bar' }
11
+ notif
12
+ end
13
+
14
+ # This fails because the length validation is only on active record
15
+ # Attempting to move to active model in rails 6 fails
16
+ # because wns_notification#as_json is not defined
17
+ # and the active_model#as_json version results in a stack level too deep error
18
+ xit 'does not allow the size of payload over 5 KB' do
19
+ allow(notification).to receive(:payload_data_size) { 5121 }
20
+ expect(notification.valid?).to be(false)
21
+ end
22
+ end if redis?
@@ -0,0 +1,5 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Client::Redis::Wpns::App do
4
+ it_behaves_like 'Rpush::Client::Wpns::App'
5
+ end if redis?
@@ -0,0 +1,5 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Client::Redis::Wpns::Notification do
4
+ it_behaves_like 'Rpush::Client::Wpns::Notification'
5
+ end if redis?
@@ -0,0 +1,51 @@
1
+ require 'unit_spec_helper'
2
+
3
+ shared_examples 'Rpush::Client::Adm::App' do
4
+ subject { described_class.new(name: 'test', environment: 'development', client_id: 'CLIENT_ID', client_secret: 'CLIENT_SECRET') }
5
+
6
+ it 'should be valid if properly instantiated' do
7
+ expect(subject).to be_valid
8
+ end
9
+
10
+ it 'should be invalid if name' do
11
+ subject.name = nil
12
+ expect(subject).not_to be_valid
13
+ expect(subject.errors[:name]).to eq ["can't be blank"]
14
+ end
15
+
16
+ it 'should be invalid if missing client_id' do
17
+ subject.client_id = nil
18
+ expect(subject).not_to be_valid
19
+ expect(subject.errors[:client_id]).to eq ["can't be blank"]
20
+ end
21
+
22
+ it 'should be invalid if missing client_secret' do
23
+ subject.client_secret = nil
24
+ expect(subject).not_to be_valid
25
+ expect(subject.errors[:client_secret]).to eq ["can't be blank"]
26
+ end
27
+
28
+ describe '#access_token_expired?' do
29
+ before(:each) do
30
+ Timecop.freeze(Time.now)
31
+ end
32
+
33
+ after do
34
+ Timecop.return
35
+ end
36
+
37
+ it 'should return true if access_token_expiration is nil' do
38
+ expect(subject.access_token_expired?).to eq(true)
39
+ end
40
+
41
+ it 'should return true if expired' do
42
+ subject.access_token_expiration = Time.now - 5.minutes
43
+ expect(subject.access_token_expired?).to eq(true)
44
+ end
45
+
46
+ it 'should return false if not expired' do
47
+ subject.access_token_expiration = Time.now + 5.minutes
48
+ expect(subject.access_token_expired?).to eq(false)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,39 @@
1
+ require 'unit_spec_helper'
2
+
3
+ shared_examples 'Rpush::Client::Adm::Notification' do
4
+ let(:app) { Rpush::Adm::App.create!(name: 'test', client_id: 'CLIENT_ID', client_secret: 'CLIENT_SECRET') }
5
+ let(:notification) { described_class.new }
6
+
7
+ it "has a 'data' payload limit of 6144 bytes" do
8
+ notification.data = { key: "a" * 6144 }
9
+ expect(notification.valid?).to eq(false)
10
+ expect(notification.errors[:base]).to eq ["Notification payload data cannot be larger than 6144 bytes."]
11
+ end
12
+
13
+ it 'limits the number of registration ids to 100' do
14
+ notification.registration_ids = ['a'] * (100 + 1)
15
+ expect(notification.valid?).to eq(false)
16
+ expect(notification.errors[:base]).to eq ["Number of registration_ids cannot be larger than 100."]
17
+ end
18
+
19
+ it 'validates data can be blank if collapse_key is set' do
20
+ notification.app = app
21
+ notification.registration_ids = ['a']
22
+ notification.collapse_key = 'test'
23
+ notification.data = nil
24
+ expect(notification.valid?).to eq(true)
25
+ expect(notification.errors[:data]).to be_empty
26
+ end
27
+
28
+ it 'validates data is present if collapse_key is not set' do
29
+ notification.collapse_key = nil
30
+ notification.data = nil
31
+ expect(notification.valid?).to eq(false)
32
+ expect(notification.errors[:data]).to eq ['must be set unless collapse_key is specified']
33
+ end
34
+
35
+ it 'includes expiresAfter in the payload' do
36
+ notification.expiry = 100
37
+ expect(notification.as_json['expiresAfter']).to eq 100
38
+ end
39
+ end
@@ -0,0 +1,29 @@
1
+ require 'unit_spec_helper'
2
+
3
+ shared_examples 'Rpush::Client::Apns::App' do
4
+ it 'does not validate an app with an invalid certificate' do
5
+ app = described_class.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 = described_class.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 = described_class.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 = described_class.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
29
+ end
@@ -0,0 +1,9 @@
1
+ require 'unit_spec_helper'
2
+
3
+ shared_examples 'Rpush::Client::Apns::Feedback' do
4
+ it 'validates the format of the device_token' do
5
+ notification = described_class.new(device_token: "{$%^&*()}")
6
+ expect(notification.valid?).to be_falsey
7
+ expect(notification.errors[:device_token]).to include('is invalid')
8
+ end
9
+ end
@@ -0,0 +1,262 @@
1
+ # encoding: US-ASCII
2
+
3
+ require "unit_spec_helper"
4
+
5
+ shared_examples 'Rpush::Client::Apns::Notification' do
6
+ let(:notification) { described_class.new }
7
+ let(:app) { Rpush::Apns::App.create!(name: 'my_app', environment: 'development', certificate: TEST_CERT) }
8
+
9
+ it "should validate the format of the device_token" do
10
+ notification = described_class.new(device_token: "{$%^&*()}")
11
+ expect(notification.valid?).to be_falsey
12
+ expect(notification.errors[:device_token].include?("is invalid")).to be_truthy
13
+ end
14
+
15
+ it "should store long alerts" do
16
+ notification.app = app
17
+ notification.device_token = "a" * 108
18
+ notification.alert = "*" * 300
19
+ expect(notification.valid?).to be_truthy
20
+
21
+ notification.save!
22
+ notification.reload
23
+ expect(notification.alert).to eq("*" * 300)
24
+ end
25
+
26
+ it "should default the expiry to 1 day" do
27
+ expect(notification.expiry).to eq 1.day.to_i
28
+ end
29
+
30
+ describe "when assigning the device token" do
31
+ it "should strip spaces from the given string" do
32
+ notification = described_class.new(device_token: "o m g")
33
+ expect(notification.device_token).to eq "omg"
34
+ end
35
+
36
+ it "should strip chevrons from the given string" do
37
+ notification = described_class.new(device_token: "<omg>")
38
+ expect(notification.device_token).to eq "omg"
39
+ end
40
+ end
41
+
42
+ describe "as_json" do
43
+ it "should include the alert if present" do
44
+ notification = described_class.new(alert: "hi mom")
45
+ expect(notification.as_json["aps"]["alert"]).to eq "hi mom"
46
+ end
47
+
48
+ it "should not include the alert key if the alert is not present" do
49
+ notification = described_class.new(alert: nil)
50
+ expect(notification.as_json["aps"].key?("alert")).to be_falsey
51
+ end
52
+
53
+ it "should encode the alert as JSON if it is a Hash" do
54
+ notification = described_class.new(alert: { 'body' => "hi mom", 'alert-loc-key' => "View" })
55
+ expect(notification.as_json["aps"]["alert"]).to eq('body' => "hi mom", 'alert-loc-key' => "View")
56
+ end
57
+
58
+ it "should include the badge if present" do
59
+ notification = described_class.new(badge: 6)
60
+ expect(notification.as_json["aps"]["badge"]).to eq 6
61
+ end
62
+
63
+ it "should not include the badge key if the badge is not present" do
64
+ notification = described_class.new(badge: nil)
65
+ expect(notification.as_json["aps"].key?("badge")).to be_falsey
66
+ end
67
+
68
+ it "should include the sound if present" do
69
+ notification = described_class.new(sound: "my_sound.aiff")
70
+ expect(notification.as_json["aps"]["sound"]).to eq "my_sound.aiff"
71
+ end
72
+
73
+ it "should not include the sound key if the sound is not present" do
74
+ notification = described_class.new(sound: nil)
75
+ expect(notification.as_json["aps"].key?("sound")).to be_falsey
76
+ end
77
+
78
+ it "should encode the sound as JSON if it is a Hash" do
79
+ notification = described_class.new(sound: { 'name' => "my_sound.aiff", 'critical' => 1, 'volume' => 0.5 })
80
+ expect(notification.as_json["aps"]["sound"]).to eq('name' => "my_sound.aiff", 'critical' => 1, 'volume' => 0.5)
81
+ end
82
+
83
+ it "should include attributes for the device" do
84
+ notification = described_class.new
85
+ notification.data = { 'omg' => 'lol', 'wtf' => 'dunno' }
86
+ expect(notification.as_json["omg"]).to eq "lol"
87
+ expect(notification.as_json["wtf"]).to eq "dunno"
88
+ end
89
+
90
+ it "should allow attributes to include a hash" do
91
+ notification = described_class.new
92
+ notification.data = { 'omg' => { 'ilike' => 'hashes' } }
93
+ expect(notification.as_json["omg"]["ilike"]).to eq "hashes"
94
+ end
95
+ end
96
+
97
+ describe 'MDM' do
98
+ let(:magic) { 'abc123' }
99
+
100
+ before do
101
+ notification.device_token = "a" * 108
102
+ notification.id = 1234
103
+ end
104
+
105
+ it 'includes the mdm magic in the payload' do
106
+ notification.mdm = magic
107
+ expect(notification.as_json).to eq('mdm' => magic)
108
+ end
109
+
110
+ it 'does not include aps attribute' do
111
+ notification.alert = "i'm doomed"
112
+ notification.mdm = magic
113
+ expect(notification.as_json.key?('aps')).to be_falsey
114
+ end
115
+
116
+ it 'can be converted to binary' do
117
+ notification.mdm = magic
118
+ expect(notification.to_binary).to be_present
119
+ end
120
+ end
121
+
122
+ describe 'mutable-content' do
123
+ it 'includes mutable-content in the payload' do
124
+ notification.mutable_content = true
125
+ expect(notification.as_json['aps']['mutable-content']).to eq 1
126
+ end
127
+
128
+ it 'does not include content-available in the payload if not set' do
129
+ expect(notification.as_json['aps'].key?('mutable-content')).to be_falsey
130
+ end
131
+
132
+ it 'does not include mutable-content as a non-aps attribute' do
133
+ notification.mutable_content = true
134
+ expect(notification.as_json.key?('mutable-content')).to be_falsey
135
+ end
136
+
137
+ it 'does not overwrite existing attributes for the device' do
138
+ notification.data = { 'hi' => 'mom' }
139
+ notification.mutable_content = true
140
+ expect(notification.as_json['aps']['mutable-content']).to eq 1
141
+ expect(notification.as_json['hi']).to eq 'mom'
142
+ end
143
+ end
144
+
145
+ describe 'content-available' do
146
+ it 'includes content-available in the payload' do
147
+ notification.content_available = true
148
+ expect(notification.as_json['aps']['content-available']).to eq 1
149
+ end
150
+
151
+ it 'does not include content-available in the payload if not set' do
152
+ expect(notification.as_json['aps'].key?('content-available')).to be_falsey
153
+ end
154
+
155
+ it 'does not include content-available as a non-aps attribute' do
156
+ notification.content_available = true
157
+ expect(notification.as_json.key?('content-available')).to be_falsey
158
+ end
159
+
160
+ it 'does not overwrite existing attributes for the device' do
161
+ notification.data = { 'hi' => 'mom' }
162
+ notification.content_available = true
163
+ expect(notification.as_json['aps']['content-available']).to eq 1
164
+ expect(notification.as_json['hi']).to eq 'mom'
165
+ end
166
+ end
167
+
168
+ describe 'url-args' do
169
+ it 'includes url-args in the payload' do
170
+ notification.url_args = ['url-arg-1']
171
+ expect(notification.as_json['aps']['url-args']).to eq ['url-arg-1']
172
+ end
173
+
174
+ it 'does not include url-args in the payload if not set' do
175
+ expect(notification.as_json['aps'].key?('url-args')).to be_falsey
176
+ end
177
+ end
178
+
179
+ describe 'category' do
180
+ it 'includes category in the payload' do
181
+ notification.category = 'INVITE_CATEGORY'
182
+ expect(notification.as_json['aps']['category']).to eq 'INVITE_CATEGORY'
183
+ end
184
+
185
+ it 'does not include category in the payload if not set' do
186
+ expect(notification.as_json['aps'].key?('category')).to be_falsey
187
+ end
188
+ end
189
+
190
+ describe 'to_binary' do
191
+ before do
192
+ notification.device_token = "a" * 108
193
+ notification.id = 1234
194
+ end
195
+
196
+ it 'uses APNS_PRIORITY_CONSERVE_POWER if content-available is the only key' do
197
+ notification.alert = nil
198
+ notification.badge = nil
199
+ notification.sound = nil
200
+ notification.content_available = true
201
+ bytes = notification.to_binary.bytes.to_a[-4..-1]
202
+ expect(bytes.first).to eq 5 # priority item ID
203
+ expect(bytes.last).to eq described_class::APNS_PRIORITY_CONSERVE_POWER
204
+ end
205
+
206
+ it 'uses APNS_PRIORITY_IMMEDIATE if content-available is not the only key' do
207
+ notification.alert = "New stuff!"
208
+ notification.badge = nil
209
+ notification.sound = nil
210
+ notification.content_available = true
211
+ bytes = notification.to_binary.bytes.to_a[-4..-1]
212
+ expect(bytes.first).to eq 5 # priority item ID
213
+ expect(bytes.last).to eq described_class::APNS_PRIORITY_IMMEDIATE
214
+ end
215
+
216
+ it "should correctly convert the notification to binary" do
217
+ notification.sound = "1.aiff"
218
+ notification.badge = 3
219
+ notification.alert = "Don't panic Mr Mainwaring, don't panic!"
220
+ notification.data = { hi: :mom }
221
+ notification.expiry = 86_400 # 1 day
222
+ notification.priority = described_class::APNS_PRIORITY_IMMEDIATE
223
+ notification.app = app
224
+ now = Time.now
225
+ allow(Time).to receive_messages(now: now)
226
+ 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"
227
+ end
228
+ end
229
+
230
+ describe "bug #31" do
231
+ it 'does not confuse a JSON looking string as JSON' do
232
+ notification = described_class.new
233
+ notification.alert = "{\"one\":2}"
234
+ expect(notification.alert).to eq "{\"one\":2}"
235
+ end
236
+ end
237
+
238
+ describe "bug #35" do
239
+ it "should limit payload size to 256 bytes but not the entire packet" do
240
+ notification = described_class.new.tap do |n|
241
+ n.device_token = "a" * 108
242
+ n.alert = "a" * 210
243
+ n.app = app
244
+ end
245
+
246
+ expect(notification.to_binary(for_validation: true).bytesize).to be > 256
247
+ expect(notification.payload.bytesize).to be < 256
248
+ expect(notification).to be_valid
249
+ end
250
+ end
251
+
252
+ describe 'thread-id' do
253
+ it 'includes thread-id in the payload' do
254
+ notification.thread_id = 'THREAD-ID'
255
+ expect(notification.as_json['aps']['thread-id']).to eq 'THREAD-ID'
256
+ end
257
+
258
+ it 'does not include thread-id in the payload if not set' do
259
+ expect(notification.as_json['aps']).to_not have_key('thread-id')
260
+ end
261
+ end
262
+ end