rpush 4.2.0 → 5.4.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 (121) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +248 -163
  3. data/README.md +103 -17
  4. data/lib/generators/templates/add_gcm.rb +4 -4
  5. data/lib/generators/templates/add_rpush.rb +4 -4
  6. data/lib/generators/templates/rpush.rb +4 -0
  7. data/lib/generators/templates/rpush_3_3_1_updates.rb +2 -2
  8. data/lib/rpush/cli.rb +1 -1
  9. data/lib/rpush/client/active_model.rb +4 -1
  10. data/lib/rpush/client/active_model/adm/data_validator.rb +1 -1
  11. data/lib/rpush/client/active_model/apns/device_token_format_validator.rb +2 -2
  12. data/lib/rpush/client/active_model/apns/notification.rb +9 -1
  13. data/lib/rpush/client/active_model/apns/notification_payload_size_validator.rb +15 -0
  14. data/lib/rpush/client/active_model/apns2/notification.rb +14 -0
  15. data/lib/rpush/client/active_model/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +1 -1
  16. data/lib/rpush/client/active_model/gcm/notification.rb +2 -2
  17. data/lib/rpush/client/active_model/payload_data_size_validator.rb +1 -1
  18. data/lib/rpush/client/active_model/registration_ids_count_validator.rb +1 -1
  19. data/lib/rpush/client/active_model/webpush/app.rb +41 -0
  20. data/lib/rpush/client/active_model/webpush/notification.rb +66 -0
  21. data/lib/rpush/client/active_record.rb +4 -0
  22. data/lib/rpush/client/active_record/apns/active_record_serializable_notification.rb +65 -0
  23. data/lib/rpush/client/active_record/apns/notification.rb +1 -57
  24. data/lib/rpush/client/active_record/apns2/notification.rb +4 -1
  25. data/lib/rpush/client/active_record/apnsp8/notification.rb +1 -0
  26. data/lib/rpush/client/active_record/webpush/app.rb +11 -0
  27. data/lib/rpush/client/active_record/webpush/notification.rb +12 -0
  28. data/lib/rpush/client/redis.rb +3 -0
  29. data/lib/rpush/client/redis/apns2/notification.rb +1 -0
  30. data/lib/rpush/client/redis/apnsp8/notification.rb +2 -0
  31. data/lib/rpush/client/redis/pushy/notification.rb +0 -1
  32. data/lib/rpush/client/redis/webpush/app.rb +15 -0
  33. data/lib/rpush/client/redis/webpush/notification.rb +15 -0
  34. data/lib/rpush/configuration.rb +3 -2
  35. data/lib/rpush/daemon.rb +4 -1
  36. data/lib/rpush/daemon/apns/feedback_receiver.rb +1 -1
  37. data/lib/rpush/daemon/apns2/delivery.rb +13 -2
  38. data/lib/rpush/daemon/apnsp8/delivery.rb +7 -2
  39. data/lib/rpush/daemon/app_runner.rb +1 -1
  40. data/lib/rpush/daemon/batch.rb +12 -5
  41. data/lib/rpush/daemon/delivery.rb +1 -2
  42. data/lib/rpush/daemon/store/active_record/reconnectable.rb +1 -1
  43. data/lib/rpush/daemon/webpush.rb +10 -0
  44. data/lib/rpush/daemon/webpush/delivery.rb +114 -0
  45. data/lib/rpush/logger.rb +1 -0
  46. data/lib/rpush/version.rb +2 -2
  47. data/spec/functional/apns2_spec.rb +97 -2
  48. data/spec/functional/gcm_priority_spec.rb +40 -0
  49. data/spec/functional/webpush_spec.rb +30 -0
  50. data/spec/spec_helper.rb +2 -0
  51. data/spec/support/simplecov_helper.rb +1 -1
  52. data/spec/unit/client/active_record/adm/app_spec.rb +2 -54
  53. data/spec/unit/client/active_record/adm/notification_spec.rb +2 -39
  54. data/spec/unit/client/active_record/apns/app_spec.rb +3 -26
  55. data/spec/unit/client/active_record/apns/feedback_spec.rb +1 -5
  56. data/spec/unit/client/active_record/apns/notification_spec.rb +29 -293
  57. data/spec/unit/client/active_record/apns2/app_spec.rb +4 -0
  58. data/spec/unit/client/active_record/apns2/notification_spec.rb +65 -0
  59. data/spec/unit/client/active_record/apnsp8/notification_spec.rb +28 -0
  60. data/spec/unit/client/active_record/app_spec.rb +1 -26
  61. data/spec/unit/client/active_record/gcm/app_spec.rb +3 -1
  62. data/spec/unit/client/active_record/gcm/notification_spec.rb +6 -88
  63. data/spec/unit/client/active_record/notification_spec.rb +3 -11
  64. data/spec/unit/client/active_record/pushy/app_spec.rb +2 -13
  65. data/spec/unit/client/active_record/pushy/notification_spec.rb +2 -55
  66. data/spec/unit/client/active_record/shared/app.rb +14 -0
  67. data/spec/unit/{notification_shared.rb → client/active_record/shared/notification.rb} +12 -7
  68. data/spec/unit/client/active_record/webpush/app_spec.rb +6 -0
  69. data/spec/unit/client/active_record/webpush/notification_spec.rb +6 -0
  70. data/spec/unit/client/active_record/wns/badge_notification_spec.rb +1 -11
  71. data/spec/unit/client/active_record/wns/raw_notification_spec.rb +3 -12
  72. data/spec/unit/client/active_record/wpns/app_spec.rb +3 -1
  73. data/spec/unit/client/active_record/wpns/notification_spec.rb +2 -17
  74. data/spec/unit/client/redis/adm/app_spec.rb +5 -0
  75. data/spec/unit/client/redis/adm/notification_spec.rb +5 -0
  76. data/spec/unit/client/redis/apns/app_spec.rb +5 -0
  77. data/spec/unit/client/redis/apns/feedback_spec.rb +5 -0
  78. data/spec/unit/client/redis/apns/notification_spec.rb +50 -0
  79. data/spec/unit/client/redis/apns2/app_spec.rb +4 -0
  80. data/spec/unit/client/redis/apns2/notification_spec.rb +50 -0
  81. data/spec/unit/client/redis/apnsp8/notification_spec.rb +29 -0
  82. data/spec/unit/client/redis/app_spec.rb +5 -0
  83. data/spec/unit/client/redis/gcm/app_spec.rb +5 -0
  84. data/spec/unit/client/redis/gcm/notification_spec.rb +5 -0
  85. data/spec/unit/client/redis/notification_spec.rb +5 -0
  86. data/spec/unit/client/redis/pushy/app_spec.rb +5 -0
  87. data/spec/unit/client/redis/pushy/notification_spec.rb +5 -0
  88. data/spec/unit/client/redis/webpush/app_spec.rb +5 -0
  89. data/spec/unit/client/redis/webpush/notification_spec.rb +5 -0
  90. data/spec/unit/client/redis/wns/badge_notification_spec.rb +5 -0
  91. data/spec/unit/client/redis/wns/raw_notification_spec.rb +22 -0
  92. data/spec/unit/client/redis/wpns/app_spec.rb +5 -0
  93. data/spec/unit/client/redis/wpns/notification_spec.rb +5 -0
  94. data/spec/unit/client/shared/adm/app.rb +51 -0
  95. data/spec/unit/client/shared/adm/notification.rb +39 -0
  96. data/spec/unit/client/shared/apns/app.rb +29 -0
  97. data/spec/unit/client/shared/apns/feedback.rb +9 -0
  98. data/spec/unit/client/shared/apns/notification.rb +262 -0
  99. data/spec/unit/client/shared/app.rb +17 -0
  100. data/spec/unit/client/shared/gcm/app.rb +4 -0
  101. data/spec/unit/client/shared/gcm/notification.rb +77 -0
  102. data/spec/unit/client/shared/notification.rb +10 -0
  103. data/spec/unit/client/shared/pushy/app.rb +17 -0
  104. data/spec/unit/client/shared/pushy/notification.rb +55 -0
  105. data/spec/unit/client/shared/webpush/app.rb +33 -0
  106. data/spec/unit/client/shared/webpush/notification.rb +83 -0
  107. data/spec/unit/client/shared/wns/badge_notification.rb +15 -0
  108. data/spec/unit/client/shared/wns/raw_notification.rb +21 -0
  109. data/spec/unit/client/shared/wpns/app.rb +4 -0
  110. data/spec/unit/client/shared/wpns/notification.rb +18 -0
  111. data/spec/unit/daemon/batch_spec.rb +50 -2
  112. data/spec/unit/daemon/delivery_spec.rb +10 -0
  113. data/spec/unit/daemon/pushy/delivery_spec.rb +5 -3
  114. data/spec/unit/daemon/shared/store.rb +312 -0
  115. data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +7 -7
  116. data/spec/unit/daemon/store/active_record_spec.rb +2 -290
  117. data/spec/unit/daemon/store/redis_spec.rb +2 -291
  118. data/spec/unit/daemon/webpush/delivery_spec.rb +144 -0
  119. data/spec/unit_spec_helper.rb +3 -0
  120. metadata +135 -12
  121. data/lib/rpush/client/active_model/apns/binary_notification_validator.rb +0 -16
@@ -0,0 +1,33 @@
1
+ require 'unit_spec_helper'
2
+
3
+ shared_examples 'Rpush::Client::Webpush::App' do
4
+ describe 'validates' do
5
+ subject { described_class.new }
6
+
7
+ it 'validates presence of name' do
8
+ is_expected.not_to be_valid
9
+ expect(subject.errors[:name]).to eq ["can't be blank"]
10
+ end
11
+
12
+ it 'validates presence of vapid_keypair' do
13
+ is_expected.not_to be_valid
14
+ expect(subject.errors[:vapid_keypair]).to eq ["can't be blank"]
15
+ end
16
+
17
+ it 'should require the vapid keypair to have subject, public and private key' do
18
+ subject.vapid_keypair = {}.to_json
19
+ is_expected.not_to be_valid
20
+ expect(subject.errors[:vapid_keypair].sort).to eq [
21
+ 'must have a private_key entry',
22
+ 'must have a public_key entry',
23
+ 'must have a subject entry',
24
+ ]
25
+ end
26
+
27
+ it 'should require valid json for the keypair' do
28
+ subject.vapid_keypair = 'invalid'
29
+ is_expected.not_to be_valid
30
+ expect(subject.errors[:vapid_keypair].sort).to eq [ 'must be valid JSON' ]
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,83 @@
1
+ require 'unit_spec_helper'
2
+
3
+ shared_examples 'Rpush::Client::Webpush::Notification' do
4
+ subject(:notification) { described_class.new }
5
+
6
+ describe 'notification attributes' do
7
+ describe 'data' do
8
+ subject { described_class.new(data: { message: 'test', urgency: 'normal' } ) }
9
+ it 'has a message' do
10
+ expect(subject.message).to eq "test"
11
+ end
12
+ it 'has an urgency' do
13
+ expect(subject.urgency).to eq "normal"
14
+ end
15
+ end
16
+
17
+ describe 'subscription' do
18
+ let(:subscription){ { endpoint: 'https://push.example.org/foo', keys: {'foo' => 'bar'}} }
19
+ subject { described_class.new(registration_ids: [subscription]) }
20
+
21
+ it "has a subscription" do
22
+ expect(subject.subscription).to eq({ endpoint: 'https://push.example.org/foo', keys: {foo: 'bar'} })
23
+ end
24
+ end
25
+ end
26
+
27
+
28
+ describe 'validates' do
29
+ let(:app) { Rpush::Webpush::App.create!(name: 'MyApp', vapid_keypair: VAPID_KEYPAIR) }
30
+
31
+ describe 'data' do
32
+ subject { described_class.new(app: app, registration_ids: [{endpoint: 'https://push.example.org/foo', keys: {'foo' => 'bar'}}]) }
33
+ it 'validates presence' do
34
+ is_expected.not_to be_valid
35
+ expect(subject.errors[:data]).to eq ["can't be blank"]
36
+ end
37
+
38
+ it "has a 'data' payload limit of 4096 bytes" do
39
+ subject.data = { message: 'a' * 4096 }
40
+ is_expected.not_to be_valid
41
+ expected_errors = ["Notification payload data cannot be larger than 4096 bytes."]
42
+ expect(subject.errors[:base]).to eq expected_errors
43
+ end
44
+ end
45
+
46
+ describe 'registration_ids' do
47
+ subject { described_class.new(app: app, data: { message: 'test' }) }
48
+ it 'validates presence' do
49
+ is_expected.not_to be_valid
50
+ expect(subject.errors[:registration_ids]).to eq ["can't be blank"]
51
+ end
52
+
53
+ it 'limits the number of registration ids to exactly 1' do
54
+ subject.registration_ids = [{endpoint: 'string', keys: { 'a' => 'hash' }}] * 2
55
+ is_expected.not_to be_valid
56
+ expected_errors = ["Number of registration_ids cannot be larger than 1."]
57
+ expect(subject.errors[:base]).to eq expected_errors
58
+ end
59
+
60
+ it 'validates the structure of the registration' do
61
+ subject.registration_ids = ['a']
62
+ is_expected.not_to be_valid
63
+ expect(subject.errors[:base]).to eq [
64
+ "Registration must have :endpoint (String) and :keys (Hash) keys"
65
+ ]
66
+
67
+ subject.registration_ids = [{endpoint: 'string', keys: { 'a' => 'hash' }}]
68
+ is_expected.to be_valid
69
+ end
70
+ end
71
+
72
+ describe 'time_to_live' do
73
+ subject { described_class.new(app: app, data: { message: 'test' }, registration_ids: [{endpoint: 'https://push.example.org/foo', keys: {'foo' => 'bar'}}]) }
74
+
75
+ it 'should be > 0' do
76
+ subject.time_to_live = -1
77
+ is_expected.not_to be_valid
78
+ expect(subject.errors[:time_to_live]).to eq ['must be greater than 0']
79
+ end
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,15 @@
1
+ require 'unit_spec_helper'
2
+
3
+ shared_examples 'Rpush::Client::Wns::BadgeNotification' do
4
+ let(:notification) do
5
+ notif = described_class.new
6
+ notif.app = Rpush::Wns::App.create!(name: "MyApp", client_id: "someclient", client_secret: "somesecret")
7
+ notif.uri = 'https://db5.notify.windows.com/?token=TOKEN'
8
+ notif.badge = 42
9
+ notif
10
+ end
11
+
12
+ it 'should allow a notification without data' do
13
+ expect(notification.valid?).to be(true)
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ require 'unit_spec_helper'
2
+
3
+ shared_examples 'Rpush::Client::Wns::RawNotification' do
4
+ let(:notification) do
5
+ notif = described_class.new
6
+ notif.app = Rpush::Wns::App.create!(name: "MyApp", client_id: "someclient", client_secret: "somesecret")
7
+ notif.uri = 'https://db5.notify.windows.com/?token=TOKEN'
8
+ notif.data = { foo: 'foo', bar: 'bar' }
9
+ notif
10
+ end
11
+
12
+ it 'allows exact payload of 5 KB' do
13
+ allow(notification).to receive(:payload_data_size) { 5120 }
14
+ expect(notification.valid?).to be(true)
15
+ end
16
+
17
+ it 'allows the size of payload under 5 KB' do
18
+ allow(notification).to receive(:payload_data_size) { 5119 }
19
+ expect(notification.valid?).to be(true)
20
+ end
21
+ end
@@ -0,0 +1,4 @@
1
+ require 'unit_spec_helper'
2
+
3
+ shared_examples 'Rpush::Client::Wpns::App' do
4
+ end
@@ -0,0 +1,18 @@
1
+ require 'unit_spec_helper'
2
+
3
+ shared_examples 'Rpush::Client::Wpns::Notification' do
4
+ let(:app) { Rpush::Wpns::App.create!(name: 'test', auth_key: 'abc') }
5
+ let(:notification) { described_class.new }
6
+
7
+ it "should have an url in the uri parameter" do
8
+ notification = described_class.new(uri: "somthing")
9
+ notification.valid?
10
+ expect(notification.errors[:uri]).to include('is invalid')
11
+ end
12
+
13
+ it "should be invalid if there's no data" do
14
+ notification = described_class.new(data: {})
15
+ notification.valid?
16
+ expect(notification.errors[:data]).to include("can't be blank")
17
+ end
18
+ end
@@ -1,8 +1,8 @@
1
1
  require 'unit_spec_helper'
2
2
 
3
3
  describe Rpush::Daemon::Batch do
4
- let(:notification1) { double(:notification1, id: 1) }
5
- let(:notification2) { double(:notification2, id: 2) }
4
+ let(:notification1) { double(:notification1, id: 1, delivered: false, failed: false) }
5
+ let(:notification2) { double(:notification2, id: 2, delivered: false, failed: false) }
6
6
  let(:batch) { Rpush::Daemon::Batch.new([notification1, notification2]) }
7
7
  let(:store) { double.as_null_object }
8
8
  let(:time) { Time.now }
@@ -50,6 +50,54 @@ describe Rpush::Daemon::Batch do
50
50
  end
51
51
  end
52
52
 
53
+ describe 'mark_all_retryable' do
54
+ let(:error) { StandardError.new('Exception') }
55
+
56
+ it 'marks all notifications as retryable without persisting' do
57
+ expect(store).to receive(:mark_retryable).ordered.with(notification1, time, persist: false)
58
+ expect(store).to receive(:mark_retryable).ordered.with(notification2, time, persist: false)
59
+
60
+ batch.mark_all_retryable(time, error)
61
+ end
62
+
63
+ it 'defers persisting' do
64
+ batch.mark_all_retryable(time, error)
65
+ expect(batch.retryable).to eq(time => [notification1, notification2])
66
+ end
67
+
68
+ context 'when one of the notifications delivered' do
69
+ let(:notification2) { double(:notification2, id: 2, delivered: true, failed: false) }
70
+
71
+ it 'marks all only pending notification as retryable without persisting' do
72
+ expect(store).to receive(:mark_retryable).ordered.with(notification1, time, persist: false)
73
+ expect(store).not_to receive(:mark_retryable).ordered.with(notification2, time, persist: false)
74
+
75
+ batch.mark_all_retryable(time, error)
76
+ end
77
+
78
+ it 'defers persisting' do
79
+ batch.mark_all_retryable(time, error)
80
+ expect(batch.retryable).to eq(time => [notification1])
81
+ end
82
+ end
83
+
84
+ context 'when one of the notifications failed' do
85
+ let(:notification2) { double(:notification2, id: 2, delivered: false, failed: true) }
86
+
87
+ it 'marks all only pending notification as retryable without persisting' do
88
+ expect(store).to receive(:mark_retryable).ordered.with(notification1, time, persist: false)
89
+ expect(store).not_to receive(:mark_retryable).ordered.with(notification2, time, persist: false)
90
+
91
+ batch.mark_all_retryable(time, error)
92
+ end
93
+
94
+ it 'defers persisting' do
95
+ batch.mark_all_retryable(time, error)
96
+ expect(batch.retryable).to eq(time => [notification1])
97
+ end
98
+ end
99
+ end
100
+
53
101
  describe 'mark_failed' do
54
102
  it 'marks the notification as failed without persisting' do
55
103
  expect(store).to receive(:mark_failed).with(notification1, 1, 'an error', time, persist: false)
@@ -41,6 +41,16 @@ describe Rpush::Daemon::Delivery do
41
41
  end
42
42
  end
43
43
 
44
+ describe 'mark_batch_retryable' do
45
+ let(:batch) { double(Rpush::Daemon::Batch) }
46
+ let(:error) { StandardError.new('Exception') }
47
+
48
+ it 'marks all notifications as retryable' do
49
+ expect(batch).to receive(:mark_all_retryable)
50
+ delivery.mark_batch_retryable(Time.now + 1.hour, error)
51
+ end
52
+ end
53
+
44
54
  describe 'mark_batch_failed' do
45
55
  it 'marks all notifications as delivered' do
46
56
  error = Rpush::DeliveryError.new(1, 42, 'an error')
@@ -61,10 +61,12 @@ describe Rpush::Daemon::Pushy::Delivery do
61
61
  it_behaves_like 'process notification'
62
62
  end
63
63
 
64
- shared_examples 'retry delivery' do |response_code:|
65
- let(:response_code) { response_code }
64
+ shared_examples 'retry delivery' do |options|
65
+ let(:response_code) { options[:response_code] }
66
+
67
+ shared_examples 'logs' do |log_options|
68
+ let(:deliver_after) { log_options[:deliver_after] }
66
69
 
67
- shared_examples 'logs' do |deliver_after:|
68
70
  let(:expected_log_message) do
69
71
  "Pushy responded with a #{response_code} error. Notification #{notification.id} " \
70
72
  "will be retried after #{deliver_after} (retry 1)."
@@ -0,0 +1,312 @@
1
+ require 'unit_spec_helper'
2
+
3
+ shared_examples 'Rpush::Daemon::Store' do
4
+ subject(:store) { described_class.new }
5
+
6
+ let(:app) { Rpush::Apns::App.create!(name: 'my_app', environment: 'development', certificate: TEST_CERT) }
7
+ let(:notification) { Rpush::Apns::Notification.create!(device_token: "a" * 64, app: app) }
8
+ let(:time) { Time.parse('2019/06/06 02:45').utc }
9
+ let(:logger) { double(Rpush::Logger, error: nil, internal_logger: nil) }
10
+
11
+ before do
12
+ allow(Rpush).to receive_messages(logger: logger)
13
+ end
14
+
15
+ before(:each) do
16
+ Timecop.freeze(time)
17
+ end
18
+
19
+ after do
20
+ Timecop.return
21
+ end
22
+
23
+ it 'updates an notification' do
24
+ expect(notification).to receive(:save!)
25
+ store.update_notification(notification)
26
+ end
27
+
28
+ it 'updates an app' do
29
+ expect(app).to receive(:save!)
30
+ store.update_app(app)
31
+ end
32
+
33
+ it 'finds an app by ID' do
34
+ expect(store.app(app.id)).to eq(app)
35
+ end
36
+
37
+ it 'finds all apps' do
38
+ app
39
+ expect(store.all_apps).to eq([app])
40
+ end
41
+
42
+ it 'translates an Integer notification ID' do
43
+ expect(store.translate_integer_notification_id(notification.id)).to eq(notification.id)
44
+ end
45
+
46
+ it 'returns the pending notification count' do
47
+ notification
48
+ expect(store.pending_delivery_count).to eq(1)
49
+ end
50
+
51
+ describe 'mark_retryable' do
52
+ it 'increments the retry count' do
53
+ expect do
54
+ store.mark_retryable(notification, time)
55
+ end.to change(notification, :retries).by(1)
56
+ end
57
+
58
+ it 'sets the deliver after timestamp' do
59
+ deliver_after = (time + 10.seconds)
60
+ expect do
61
+ store.mark_retryable(notification, deliver_after)
62
+ end.to change(notification, :deliver_after).to(deliver_after)
63
+ end
64
+
65
+ it 'saves the notification without validation' do
66
+ expect(notification).to receive(:save!).with(validate: false)
67
+ store.mark_retryable(notification, time)
68
+ end
69
+
70
+ it 'does not save the notification if persist: false' do
71
+ expect(notification).not_to receive(:save!)
72
+ store.mark_retryable(notification, time, persist: false)
73
+ end
74
+ end
75
+
76
+ describe 'mark_batch_retryable' do
77
+ let(:deliver_after) { time + 10.seconds }
78
+
79
+ it 'sets the attributes on the object for use in reflections' do
80
+ store.mark_batch_retryable([notification], deliver_after)
81
+ expect(notification.deliver_after.to_s).to eq deliver_after.to_s
82
+ expect(notification.retries).to eq 1
83
+ end
84
+
85
+ it 'increments the retired count' do
86
+ expect do
87
+ store.mark_batch_retryable([notification], deliver_after)
88
+ notification.reload
89
+ end.to change(notification, :retries).by(1)
90
+ end
91
+
92
+ it 'sets the deliver after timestamp' do
93
+ expect do
94
+ store.mark_batch_retryable([notification], deliver_after)
95
+ notification.reload
96
+ end.to change { notification.deliver_after.try(:utc).to_s }.to(deliver_after.utc.to_s)
97
+ end
98
+ end
99
+
100
+ describe 'mark_delivered' do
101
+ it 'marks the notification as delivered' do
102
+ expect do
103
+ store.mark_delivered(notification, time)
104
+ end.to change(notification, :delivered).to(true)
105
+ end
106
+
107
+ it 'sets the time the notification was delivered' do
108
+ expect do
109
+ store.mark_delivered(notification, time)
110
+ notification.reload
111
+ end.to change { notification.delivered_at.try(:utc).to_s }.to(time.to_s)
112
+ end
113
+
114
+ it 'saves the notification without validation' do
115
+ expect(notification).to receive(:save!).with(validate: false)
116
+ store.mark_delivered(notification, time)
117
+ end
118
+
119
+ it 'does not save the notification if persist: false' do
120
+ expect(notification).not_to receive(:save!)
121
+ store.mark_delivered(notification, time, persist: false)
122
+ end
123
+ end
124
+
125
+ describe 'mark_batch_delivered' do
126
+ it 'sets the attributes on the object for use in reflections' do
127
+ store.mark_batch_delivered([notification])
128
+ expect(notification.delivered_at.to_s).to eq time.to_s
129
+ expect(notification.delivered).to be_truthy
130
+ end
131
+
132
+ it 'marks the notifications as delivered' do
133
+ expect do
134
+ store.mark_batch_delivered([notification])
135
+ notification.reload
136
+ end.to change(notification, :delivered).to(true)
137
+ end
138
+
139
+ it 'sets the time the notifications were delivered' do
140
+ expect do
141
+ store.mark_batch_delivered([notification])
142
+ notification.reload
143
+ end.to change { notification.delivered_at.try(:utc)&.to_s }.to(time.to_s)
144
+ end
145
+ end
146
+
147
+ describe 'mark_failed' do
148
+ it 'marks the notification as not delivered' do
149
+ store.mark_failed(notification, nil, '', time)
150
+ expect(notification.delivered).to eq(false)
151
+ end
152
+
153
+ it 'marks the notification as failed' do
154
+ expect do
155
+ store.mark_failed(notification, nil, '', time)
156
+ notification.reload
157
+ end.to change(notification, :failed).to(true)
158
+ end
159
+
160
+ it 'sets the time the notification delivery failed' do
161
+ expect do
162
+ store.mark_failed(notification, nil, '', time)
163
+ notification.reload
164
+ end.to change { notification.failed_at.try(:utc).to_s }.to(time.to_s)
165
+ end
166
+
167
+ it 'sets the error code' do
168
+ expect do
169
+ store.mark_failed(notification, 42, '', time)
170
+ end.to change(notification, :error_code).to(42)
171
+ end
172
+
173
+ it 'sets the error description' do
174
+ expect do
175
+ store.mark_failed(notification, 42, 'Weeee', time)
176
+ end.to change(notification, :error_description).to('Weeee')
177
+ end
178
+
179
+ it 'saves the notification without validation' do
180
+ expect(notification).to receive(:save!).with(validate: false)
181
+ store.mark_failed(notification, nil, '', time)
182
+ end
183
+
184
+ it 'does not save the notification if persist: false' do
185
+ expect(notification).not_to receive(:save!)
186
+ store.mark_failed(notification, nil, '', time, persist: false)
187
+ end
188
+ end
189
+
190
+ describe 'mark_batch_failed' do
191
+ it 'sets the attributes on the object for use in reflections' do
192
+ store.mark_batch_failed([notification], 123, 'an error')
193
+ expect(notification.failed_at.to_s).to eq time.to_s
194
+ expect(notification.delivered_at).to be_nil
195
+ expect(notification.delivered).to eq(false)
196
+ expect(notification.failed).to be_truthy
197
+ expect(notification.error_code).to eq 123
198
+ expect(notification.error_description).to eq 'an error'
199
+ end
200
+
201
+ it 'marks the notification as not delivered' do
202
+ store.mark_batch_failed([notification], nil, '')
203
+ notification.reload
204
+ expect(notification.delivered).to be_falsey
205
+ end
206
+
207
+ it 'marks the notification as failed' do
208
+ expect do
209
+ store.mark_batch_failed([notification], nil, '')
210
+ notification.reload
211
+ end.to change(notification, :failed).to(true)
212
+ end
213
+
214
+ it 'sets the time the notification delivery failed' do
215
+ expect do
216
+ store.mark_batch_failed([notification], nil, '')
217
+ notification.reload
218
+ end.to change { notification.failed_at.try(:utc) }.to(time)
219
+ end
220
+
221
+ it 'sets the error code' do
222
+ expect do
223
+ store.mark_batch_failed([notification], 42, '')
224
+ notification.reload
225
+ end.to change(notification, :error_code).to(42)
226
+ end
227
+
228
+ it 'sets the error description' do
229
+ expect do
230
+ store.mark_batch_failed([notification], 42, 'Weeee')
231
+ notification.reload
232
+ end.to change(notification, :error_description).to('Weeee')
233
+ end
234
+ end
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
+ describe 'create_adm_notification' do
279
+ let(:data) { { 'data' => true } }
280
+ let(:attributes) { { app_id: app.id, collapse_key: 'ckey', delay_while_idle: true } }
281
+ let(:registration_ids) { %w[123 456] }
282
+ let(:deliver_after) { time + 10.seconds }
283
+ let(:args) { [attributes, data, registration_ids, deliver_after, app] }
284
+
285
+ it 'sets the given attributes' do
286
+ new_notification = store.create_adm_notification(*args)
287
+ expect(new_notification.app_id).to eq app.id
288
+ expect(new_notification.collapse_key).to eq 'ckey'
289
+ expect(new_notification.delay_while_idle).to be_truthy
290
+ end
291
+
292
+ it 'sets the given data' do
293
+ new_notification = store.create_adm_notification(*args)
294
+ expect(new_notification.data['data']).to be_truthy
295
+ end
296
+
297
+ it 'sets the given registration IDs' do
298
+ new_notification = store.create_adm_notification(*args)
299
+ expect(new_notification.registration_ids).to eq registration_ids
300
+ end
301
+
302
+ it 'sets the deliver_after timestamp' do
303
+ new_notification = store.create_adm_notification(*args)
304
+ expect(new_notification.deliver_after.to_s).to eq deliver_after.to_s
305
+ end
306
+
307
+ it 'saves the new notification' do
308
+ new_notification = store.create_adm_notification(*args)
309
+ expect(new_notification.new_record?).to be_falsey
310
+ end
311
+ end
312
+ end