rpush 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +99 -0
- data/LICENSE +7 -0
- data/README.md +189 -0
- data/bin/rpush +36 -0
- data/config/database.yml +44 -0
- data/lib/generators/rpush_generator.rb +44 -0
- data/lib/generators/templates/add_adm.rb +23 -0
- data/lib/generators/templates/add_alert_is_json_to_rapns_notifications.rb +9 -0
- data/lib/generators/templates/add_app_to_rapns.rb +11 -0
- data/lib/generators/templates/add_fail_after_to_rpush_notifications.rb +9 -0
- data/lib/generators/templates/add_gcm.rb +102 -0
- data/lib/generators/templates/add_rpush.rb +349 -0
- data/lib/generators/templates/add_wpns.rb +16 -0
- data/lib/generators/templates/create_rapns_apps.rb +16 -0
- data/lib/generators/templates/create_rapns_feedback.rb +18 -0
- data/lib/generators/templates/create_rapns_notifications.rb +29 -0
- data/lib/generators/templates/rename_rapns_to_rpush.rb +63 -0
- data/lib/generators/templates/rpush.rb +104 -0
- data/lib/rpush.rb +62 -0
- data/lib/rpush/TODO +3 -0
- data/lib/rpush/adm/app.rb +15 -0
- data/lib/rpush/adm/data_validator.rb +11 -0
- data/lib/rpush/adm/notification.rb +29 -0
- data/lib/rpush/apns/app.rb +29 -0
- data/lib/rpush/apns/binary_notification_validator.rb +12 -0
- data/lib/rpush/apns/device_token_format_validator.rb +12 -0
- data/lib/rpush/apns/feedback.rb +16 -0
- data/lib/rpush/apns/notification.rb +84 -0
- data/lib/rpush/apns_feedback.rb +13 -0
- data/lib/rpush/app.rb +18 -0
- data/lib/rpush/configuration.rb +75 -0
- data/lib/rpush/daemon.rb +140 -0
- data/lib/rpush/daemon/adm.rb +9 -0
- data/lib/rpush/daemon/adm/delivery.rb +222 -0
- data/lib/rpush/daemon/apns.rb +16 -0
- data/lib/rpush/daemon/apns/certificate_expired_error.rb +20 -0
- data/lib/rpush/daemon/apns/delivery.rb +64 -0
- data/lib/rpush/daemon/apns/disconnection_error.rb +20 -0
- data/lib/rpush/daemon/apns/feedback_receiver.rb +79 -0
- data/lib/rpush/daemon/app_runner.rb +187 -0
- data/lib/rpush/daemon/batch.rb +115 -0
- data/lib/rpush/daemon/constants.rb +59 -0
- data/lib/rpush/daemon/delivery.rb +28 -0
- data/lib/rpush/daemon/delivery_error.rb +19 -0
- data/lib/rpush/daemon/dispatcher/http.rb +21 -0
- data/lib/rpush/daemon/dispatcher/tcp.rb +30 -0
- data/lib/rpush/daemon/dispatcher_loop.rb +54 -0
- data/lib/rpush/daemon/dispatcher_loop_collection.rb +33 -0
- data/lib/rpush/daemon/feeder.rb +68 -0
- data/lib/rpush/daemon/gcm.rb +9 -0
- data/lib/rpush/daemon/gcm/delivery.rb +222 -0
- data/lib/rpush/daemon/interruptible_sleep.rb +61 -0
- data/lib/rpush/daemon/loggable.rb +31 -0
- data/lib/rpush/daemon/reflectable.rb +13 -0
- data/lib/rpush/daemon/retry_header_parser.rb +23 -0
- data/lib/rpush/daemon/retryable_error.rb +20 -0
- data/lib/rpush/daemon/service_config_methods.rb +33 -0
- data/lib/rpush/daemon/store/active_record.rb +154 -0
- data/lib/rpush/daemon/store/active_record/reconnectable.rb +68 -0
- data/lib/rpush/daemon/tcp_connection.rb +143 -0
- data/lib/rpush/daemon/too_many_requests_error.rb +20 -0
- data/lib/rpush/daemon/wpns.rb +9 -0
- data/lib/rpush/daemon/wpns/delivery.rb +132 -0
- data/lib/rpush/deprecatable.rb +23 -0
- data/lib/rpush/deprecation.rb +23 -0
- data/lib/rpush/embed.rb +28 -0
- data/lib/rpush/gcm/app.rb +11 -0
- data/lib/rpush/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +11 -0
- data/lib/rpush/gcm/notification.rb +30 -0
- data/lib/rpush/logger.rb +63 -0
- data/lib/rpush/multi_json_helper.rb +16 -0
- data/lib/rpush/notification.rb +69 -0
- data/lib/rpush/notifier.rb +52 -0
- data/lib/rpush/payload_data_size_validator.rb +10 -0
- data/lib/rpush/push.rb +16 -0
- data/lib/rpush/railtie.rb +11 -0
- data/lib/rpush/reflection.rb +58 -0
- data/lib/rpush/registration_ids_count_validator.rb +10 -0
- data/lib/rpush/version.rb +3 -0
- data/lib/rpush/wpns/app.rb +9 -0
- data/lib/rpush/wpns/notification.rb +26 -0
- data/lib/tasks/cane.rake +18 -0
- data/lib/tasks/rpush.rake +16 -0
- data/lib/tasks/test.rake +38 -0
- data/spec/functional/adm_spec.rb +43 -0
- data/spec/functional/apns_spec.rb +58 -0
- data/spec/functional/embed_spec.rb +49 -0
- data/spec/functional/gcm_spec.rb +42 -0
- data/spec/functional/wpns_spec.rb +41 -0
- data/spec/support/cert_with_password.pem +90 -0
- data/spec/support/cert_without_password.pem +59 -0
- data/spec/support/install.sh +32 -0
- data/spec/support/simplecov_helper.rb +20 -0
- data/spec/support/simplecov_quality_formatter.rb +8 -0
- data/spec/tmp/.gitkeep +0 -0
- data/spec/unit/adm/app_spec.rb +58 -0
- data/spec/unit/adm/notification_spec.rb +45 -0
- data/spec/unit/apns/app_spec.rb +29 -0
- data/spec/unit/apns/feedback_spec.rb +9 -0
- data/spec/unit/apns/notification_spec.rb +208 -0
- data/spec/unit/apns_feedback_spec.rb +21 -0
- data/spec/unit/app_spec.rb +30 -0
- data/spec/unit/configuration_spec.rb +45 -0
- data/spec/unit/daemon/adm/delivery_spec.rb +243 -0
- data/spec/unit/daemon/apns/certificate_expired_error_spec.rb +11 -0
- data/spec/unit/daemon/apns/delivery_spec.rb +101 -0
- data/spec/unit/daemon/apns/disconnection_error_spec.rb +18 -0
- data/spec/unit/daemon/apns/feedback_receiver_spec.rb +117 -0
- data/spec/unit/daemon/app_runner_spec.rb +292 -0
- data/spec/unit/daemon/batch_spec.rb +232 -0
- data/spec/unit/daemon/delivery_error_spec.rb +13 -0
- data/spec/unit/daemon/delivery_spec.rb +38 -0
- data/spec/unit/daemon/dispatcher/http_spec.rb +33 -0
- data/spec/unit/daemon/dispatcher/tcp_spec.rb +38 -0
- data/spec/unit/daemon/dispatcher_loop_collection_spec.rb +37 -0
- data/spec/unit/daemon/dispatcher_loop_spec.rb +71 -0
- data/spec/unit/daemon/feeder_spec.rb +98 -0
- data/spec/unit/daemon/gcm/delivery_spec.rb +310 -0
- data/spec/unit/daemon/interruptible_sleep_spec.rb +68 -0
- data/spec/unit/daemon/reflectable_spec.rb +27 -0
- data/spec/unit/daemon/retryable_error_spec.rb +14 -0
- data/spec/unit/daemon/service_config_methods_spec.rb +33 -0
- data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +114 -0
- data/spec/unit/daemon/store/active_record_spec.rb +357 -0
- data/spec/unit/daemon/tcp_connection_spec.rb +287 -0
- data/spec/unit/daemon/too_many_requests_error_spec.rb +14 -0
- data/spec/unit/daemon/wpns/delivery_spec.rb +159 -0
- data/spec/unit/daemon_spec.rb +159 -0
- data/spec/unit/deprecatable_spec.rb +32 -0
- data/spec/unit/deprecation_spec.rb +15 -0
- data/spec/unit/embed_spec.rb +50 -0
- data/spec/unit/gcm/app_spec.rb +4 -0
- data/spec/unit/gcm/notification_spec.rb +36 -0
- data/spec/unit/logger_spec.rb +127 -0
- data/spec/unit/notification_shared.rb +105 -0
- data/spec/unit/notification_spec.rb +15 -0
- data/spec/unit/notifier_spec.rb +49 -0
- data/spec/unit/push_spec.rb +43 -0
- data/spec/unit/reflection_spec.rb +30 -0
- data/spec/unit/rpush_spec.rb +9 -0
- data/spec/unit/wpns/app_spec.rb +4 -0
- data/spec/unit/wpns/notification_spec.rb +30 -0
- data/spec/unit_spec_helper.rb +101 -0
- metadata +276 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require "unit_spec_helper"
|
|
2
|
+
|
|
3
|
+
describe Rpush::Daemon::InterruptibleSleep do
|
|
4
|
+
|
|
5
|
+
let(:rd) { double(:close => nil) }
|
|
6
|
+
let(:wr) { double(:close => nil) }
|
|
7
|
+
|
|
8
|
+
subject { Rpush::Daemon::InterruptibleSleep.new }
|
|
9
|
+
|
|
10
|
+
it 'creates a new pipe' do
|
|
11
|
+
IO.should_receive(:pipe)
|
|
12
|
+
subject
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'selects on the reader' do
|
|
16
|
+
IO.stub(:pipe => [rd, wr])
|
|
17
|
+
IO.should_receive(:select).with([rd], nil, nil, 1)
|
|
18
|
+
subject.sleep(1)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'closes the writer' do
|
|
22
|
+
IO.stub(:pipe => [rd, wr])
|
|
23
|
+
rd.should_receive(:close)
|
|
24
|
+
wr.should_receive(:close)
|
|
25
|
+
subject.close
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'returns false when timeout occurs' do
|
|
29
|
+
expect(subject.sleep(0.01)).to be_false
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'returns true when sleep does not timeout' do
|
|
33
|
+
subject.interrupt_sleep
|
|
34
|
+
expect(subject.sleep(0.01)).to be_true
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
context 'with UDP socket connected' do
|
|
38
|
+
before :each do
|
|
39
|
+
@host, @port = subject.enable_wake_on_udp('127.0.0.1', 0)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'times out with no udp activity' do
|
|
43
|
+
expect(subject.sleep(0.01)).to be_false
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
unless defined? JRUBY_VERSION
|
|
47
|
+
it 'wakes on UDPSocket' do
|
|
48
|
+
waker = UDPSocket.new
|
|
49
|
+
waker.connect(@host, @port)
|
|
50
|
+
waker.write('x')
|
|
51
|
+
expect(subject.sleep(0.01)).to be_true
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'consumes all data on udp socket' do
|
|
55
|
+
waker = UDPSocket.new
|
|
56
|
+
waker.connect(@host, @port)
|
|
57
|
+
waker.send('x', 0)
|
|
58
|
+
waker.send('x', 0)
|
|
59
|
+
waker.send('x', 0)
|
|
60
|
+
# true since there is data to be read => no timeout
|
|
61
|
+
expect(subject.sleep(0.01)).to be_true
|
|
62
|
+
# false since data is consumed => wait for full timeout
|
|
63
|
+
expect(subject.sleep(0.01)).to be_false
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'unit_spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Rpush::Daemon::Reflectable do
|
|
4
|
+
class TestReflectable
|
|
5
|
+
include Rpush::Daemon::Reflectable
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
let(:logger) { double(:error => nil) }
|
|
9
|
+
let(:test_reflectable) { TestReflectable.new }
|
|
10
|
+
|
|
11
|
+
before do
|
|
12
|
+
Rpush.reflections.stub(:__dispatch)
|
|
13
|
+
Rpush.stub(:logger => logger)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'dispatches the given reflection' do
|
|
17
|
+
Rpush.reflections.should_receive(:__dispatch).with(:error)
|
|
18
|
+
test_reflectable.reflect(:error)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'logs errors raise by the reflection' do
|
|
22
|
+
error = StandardError.new
|
|
23
|
+
Rpush.reflections.stub(:__dispatch).and_raise(error)
|
|
24
|
+
Rpush.logger.should_receive(:error).with(error)
|
|
25
|
+
test_reflectable.reflect(:error)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require "unit_spec_helper"
|
|
2
|
+
|
|
3
|
+
describe Rpush::RetryableError do
|
|
4
|
+
let(:response) { double(:code => 401, :header => { 'retry-after' => 3600 }) }
|
|
5
|
+
let(:error) { Rpush::RetryableError.new(401, 12, "Unauthorized", response) }
|
|
6
|
+
|
|
7
|
+
it "returns an informative message" do
|
|
8
|
+
error.to_s.should eq "Retryable error for 12, received error 401 (Unauthorized) - retry after 3600"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "returns the error code" do
|
|
12
|
+
error.code.should eq 401
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require 'unit_spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Rpush::Daemon::ServiceConfigMethods do
|
|
4
|
+
module ServiceConfigMethodsSpec
|
|
5
|
+
extend Rpush::Daemon::ServiceConfigMethods
|
|
6
|
+
class Delivery; end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it 'returns the delivery class' do
|
|
10
|
+
ServiceConfigMethodsSpec.delivery_class.should eq ServiceConfigMethodsSpec::Delivery
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it 'gets & sets loops' do
|
|
14
|
+
loop_class = double
|
|
15
|
+
ServiceConfigMethodsSpec.loops loop_class
|
|
16
|
+
ServiceConfigMethodsSpec.loops.should eq [loop_class]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it 'returns a new dispatcher' do
|
|
20
|
+
ServiceConfigMethodsSpec.dispatcher :http, an: :option
|
|
21
|
+
app = double
|
|
22
|
+
dispatcher = double
|
|
23
|
+
Rpush::Daemon::Dispatcher::Http.should_receive(:new).with(app, ServiceConfigMethodsSpec::Delivery, an: :option).and_return(dispatcher)
|
|
24
|
+
ServiceConfigMethodsSpec.new_dispatcher(app).should eq dispatcher
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it 'raises a NotImplementedError for an unknown dispatcher type' do
|
|
28
|
+
expect do
|
|
29
|
+
ServiceConfigMethodsSpec.dispatcher :unknown
|
|
30
|
+
ServiceConfigMethodsSpec.dispatcher_class
|
|
31
|
+
end.to raise_error(NotImplementedError)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
require 'unit_spec_helper'
|
|
2
|
+
require 'rpush/daemon/store/active_record/reconnectable'
|
|
3
|
+
|
|
4
|
+
describe Rpush::Daemon::Store::ActiveRecord::Reconnectable do
|
|
5
|
+
class TestDouble
|
|
6
|
+
include Rpush::Daemon::Store::ActiveRecord::Reconnectable
|
|
7
|
+
|
|
8
|
+
attr_reader :name
|
|
9
|
+
|
|
10
|
+
def initialize(error, max_calls)
|
|
11
|
+
@error = error
|
|
12
|
+
@max_calls = max_calls
|
|
13
|
+
@calls = 0
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def perform
|
|
17
|
+
with_database_reconnect_and_retry do
|
|
18
|
+
@calls += 1
|
|
19
|
+
raise @error if @calls <= @max_calls
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
let(:adapter_error_class) do
|
|
25
|
+
case $adapter
|
|
26
|
+
when 'postgresql'
|
|
27
|
+
PGError
|
|
28
|
+
when 'mysql'
|
|
29
|
+
Mysql::Error
|
|
30
|
+
when 'mysql2'
|
|
31
|
+
Mysql2::Error
|
|
32
|
+
when 'jdbcpostgresql'
|
|
33
|
+
ActiveRecord::JDBCError
|
|
34
|
+
when 'jdbcmysql'
|
|
35
|
+
ActiveRecord::JDBCError
|
|
36
|
+
when 'jdbch2'
|
|
37
|
+
ActiveRecord::JDBCError
|
|
38
|
+
when 'sqlite3'
|
|
39
|
+
SQLite3::Exception
|
|
40
|
+
else
|
|
41
|
+
raise "Please update #{__FILE__} for adapter #{$adapter}"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
let(:error) { adapter_error_class.new("db down!") }
|
|
45
|
+
let(:test_double) { TestDouble.new(error, 1) }
|
|
46
|
+
|
|
47
|
+
before do
|
|
48
|
+
@logger = double("Logger", :info => nil, :error => nil, :warn => nil)
|
|
49
|
+
Rpush.stub(:logger).and_return(@logger)
|
|
50
|
+
|
|
51
|
+
ActiveRecord::Base.stub(:clear_all_connections!)
|
|
52
|
+
ActiveRecord::Base.stub(:establish_connection)
|
|
53
|
+
test_double.stub(:sleep)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "should log the error raised" do
|
|
57
|
+
Rpush.logger.should_receive(:error).with(error)
|
|
58
|
+
test_double.perform
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "should log that the database is being reconnected" do
|
|
62
|
+
Rpush.logger.should_receive(:warn).with("Lost connection to database, reconnecting...")
|
|
63
|
+
test_double.perform
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "should log the reconnection attempt" do
|
|
67
|
+
Rpush.logger.should_receive(:warn).with("Attempt 1")
|
|
68
|
+
test_double.perform
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "should clear all connections" do
|
|
72
|
+
ActiveRecord::Base.should_receive(:clear_all_connections!)
|
|
73
|
+
test_double.perform
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "should establish a new connection" do
|
|
77
|
+
ActiveRecord::Base.should_receive(:establish_connection)
|
|
78
|
+
test_double.perform
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "should test out the new connection by performing a count" do
|
|
82
|
+
Rpush::Notification.should_receive(:count)
|
|
83
|
+
test_double.perform
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
context "when the reconnection attempt is not successful" do
|
|
87
|
+
before do
|
|
88
|
+
class << Rpush::Notification
|
|
89
|
+
def count
|
|
90
|
+
@count_calls += 1
|
|
91
|
+
return if @count_calls == 2
|
|
92
|
+
raise @error
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
Rpush::Notification.instance_variable_set("@count_calls", 0)
|
|
96
|
+
Rpush::Notification.instance_variable_set("@error", error)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it "should log the 2nd attempt" do
|
|
100
|
+
Rpush.logger.should_receive(:warn).with("Attempt 2")
|
|
101
|
+
test_double.perform
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it "should log errors raised when the reconnection is not successful" do
|
|
105
|
+
Rpush.logger.should_receive(:error).with(error)
|
|
106
|
+
test_double.perform
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it "should sleep to avoid thrashing when the database is down" do
|
|
110
|
+
test_double.should_receive(:sleep).with(2)
|
|
111
|
+
test_double.perform
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
require 'unit_spec_helper'
|
|
2
|
+
require 'rpush/daemon/store/active_record'
|
|
3
|
+
|
|
4
|
+
describe Rpush::Daemon::Store::ActiveRecord do
|
|
5
|
+
let(:app) { Rpush::Apns::App.create!(:name => 'my_app', :environment => 'development', :certificate => TEST_CERT) }
|
|
6
|
+
let(:notification) { Rpush::Apns::Notification.create!(:device_token => "a" * 64, :app => app) }
|
|
7
|
+
let(:store) { Rpush::Daemon::Store::ActiveRecord.new }
|
|
8
|
+
let(:time) { Time.now.utc }
|
|
9
|
+
let(:logger) { double(Rpush::Logger, error: nil) }
|
|
10
|
+
|
|
11
|
+
before do
|
|
12
|
+
Rpush.stub(logger: logger)
|
|
13
|
+
Time.stub(:now => time)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'reconnects after daemonize' do
|
|
17
|
+
store.should_receive(:reconnect_database)
|
|
18
|
+
store.after_daemonize
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'can update a notification' do
|
|
22
|
+
notification.should_receive(:save!)
|
|
23
|
+
store.update_notification(notification)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'can update a app' do
|
|
27
|
+
app.should_receive(:save!)
|
|
28
|
+
store.update_app(app)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'can release a connection' do
|
|
32
|
+
ActiveRecord::Base.connection_pool.should_receive(:release_connection)
|
|
33
|
+
store.release_connection
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it 'logs errors raised when trying to release the connection' do
|
|
37
|
+
e = StandardError.new
|
|
38
|
+
ActiveRecord::Base.connection_pool.stub(:release_connection).and_raise(e)
|
|
39
|
+
Rpush.logger.should_receive(:error).with(e)
|
|
40
|
+
store.release_connection
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
describe 'deliverable_notifications' do
|
|
44
|
+
it 'checks for new notifications with the ability to reconnect the database' do
|
|
45
|
+
store.should_receive(:with_database_reconnect_and_retry)
|
|
46
|
+
store.deliverable_notifications(app)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'loads notifications in batches' do
|
|
50
|
+
Rpush.config.batch_size = 5000
|
|
51
|
+
Rpush.config.push = false
|
|
52
|
+
relation = double.as_null_object
|
|
53
|
+
relation.should_receive(:limit).with(5000)
|
|
54
|
+
Rpush::Notification.stub(:ready_for_delivery => relation)
|
|
55
|
+
store.deliverable_notifications([app])
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'does not load notification in batches if in push mode' do
|
|
59
|
+
Rpush.config.push = true
|
|
60
|
+
relation = double.as_null_object
|
|
61
|
+
relation.should_not_receive(:limit)
|
|
62
|
+
Rpush::Notification.stub(:ready_for_delivery => relation)
|
|
63
|
+
store.deliverable_notifications([app])
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it 'loads an undelivered notification without deliver_after set' do
|
|
67
|
+
notification.update_attributes!(:delivered => false, :deliver_after => nil)
|
|
68
|
+
store.deliverable_notifications([app]).should eq [notification]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it 'loads an notification with a deliver_after time in the past' do
|
|
72
|
+
notification.update_attributes!(:delivered => false, :deliver_after => 1.hour.ago)
|
|
73
|
+
store.deliverable_notifications([app]).should eq [notification]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it 'does not load an notification with a deliver_after time in the future' do
|
|
77
|
+
notification.update_attributes!(:delivered => false, :deliver_after => 1.hour.from_now)
|
|
78
|
+
store.deliverable_notifications([app]).should be_empty
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it 'does not load a previously delivered notification' do
|
|
82
|
+
notification.update_attributes!(:delivered => true, :delivered_at => time)
|
|
83
|
+
store.deliverable_notifications([app]).should be_empty
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it "does not enqueue a notification that has previously failed delivery" do
|
|
87
|
+
notification.update_attributes!(:delivered => false, :failed => true)
|
|
88
|
+
store.deliverable_notifications([app]).should be_empty
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it 'does not load notifications for apps that are still processing the previous batch' do
|
|
92
|
+
notification
|
|
93
|
+
store.deliverable_notifications([]).should be_empty
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
describe 'mark_retryable' do
|
|
98
|
+
it 'increments the retry count' do
|
|
99
|
+
expect do
|
|
100
|
+
store.mark_retryable(notification, time)
|
|
101
|
+
end.to change(notification, :retries).by(1)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it 'sets the deliver after timestamp' do
|
|
105
|
+
deliver_after = time + 10.seconds
|
|
106
|
+
expect do
|
|
107
|
+
store.mark_retryable(notification, deliver_after)
|
|
108
|
+
end.to change(notification, :deliver_after).to(deliver_after)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it 'saves the notification without validation' do
|
|
112
|
+
notification.should_receive(:save!).with(:validate => false)
|
|
113
|
+
store.mark_retryable(notification, time)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it 'does not save the notification if :persist => false' do
|
|
117
|
+
notification.should_not_receive(:save!)
|
|
118
|
+
store.mark_retryable(notification, time, :persist => false)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
describe 'mark_batch_retryable' do
|
|
123
|
+
let(:deliver_after) { time + 10.seconds }
|
|
124
|
+
|
|
125
|
+
it 'sets the attributes on the object for use in reflections' do
|
|
126
|
+
store.mark_batch_retryable([notification], deliver_after)
|
|
127
|
+
notification.deliver_after.should eq deliver_after
|
|
128
|
+
notification.retries.should eq 1
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it 'increments the retired count' do
|
|
132
|
+
expect do
|
|
133
|
+
store.mark_batch_retryable([notification], deliver_after)
|
|
134
|
+
notification.reload
|
|
135
|
+
end.to change(notification, :retries).by(1)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it 'sets the deliver after timestamp' do
|
|
139
|
+
expect do
|
|
140
|
+
store.mark_batch_retryable([notification], deliver_after)
|
|
141
|
+
notification.reload
|
|
142
|
+
end.to change { notification.deliver_after.try(:utc).to_s }.to(deliver_after.utc.to_s)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
describe 'mark_delivered' do
|
|
147
|
+
it 'marks the notification as delivered' do
|
|
148
|
+
expect do
|
|
149
|
+
store.mark_delivered(notification, time)
|
|
150
|
+
end.to change(notification, :delivered).to(true)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it 'sets the time the notification was delivered' do
|
|
154
|
+
expect do
|
|
155
|
+
store.mark_delivered(notification, time)
|
|
156
|
+
notification.reload
|
|
157
|
+
end.to change { notification.delivered_at.try(:utc).to_s }.to(time.to_s)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it 'saves the notification without validation' do
|
|
161
|
+
notification.should_receive(:save!).with(:validate => false)
|
|
162
|
+
store.mark_delivered(notification, time)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it 'does not save the notification if :persist => false' do
|
|
166
|
+
notification.should_not_receive(:save!)
|
|
167
|
+
store.mark_delivered(notification, time, :persist => false)
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
describe 'mark_batch_delivered' do
|
|
172
|
+
it 'sets the attributes on the object for use in reflections' do
|
|
173
|
+
store.mark_batch_delivered([notification])
|
|
174
|
+
notification.delivered_at.should eq time
|
|
175
|
+
notification.delivered.should be_true
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
it 'marks the notifications as delivered' do
|
|
179
|
+
expect do
|
|
180
|
+
store.mark_batch_delivered([notification])
|
|
181
|
+
notification.reload
|
|
182
|
+
end.to change(notification, :delivered).to(true)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
it 'sets the time the notifications were delivered' do
|
|
186
|
+
expect do
|
|
187
|
+
store.mark_batch_delivered([notification])
|
|
188
|
+
notification.reload
|
|
189
|
+
end.to change { notification.delivered_at.try(:utc).to_s }.to(time.to_s)
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
describe 'mark_failed' do
|
|
194
|
+
it 'marks the notification as not delivered' do
|
|
195
|
+
store.mark_failed(notification, nil, '', time)
|
|
196
|
+
notification.delivered.should be_false
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
it 'marks the notification as failed' do
|
|
200
|
+
expect do
|
|
201
|
+
store.mark_failed(notification, nil, '', time)
|
|
202
|
+
notification.reload
|
|
203
|
+
end.to change(notification, :failed).to(true)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
it 'sets the time the notification delivery failed' do
|
|
207
|
+
expect do
|
|
208
|
+
store.mark_failed(notification, nil, '', time)
|
|
209
|
+
notification.reload
|
|
210
|
+
end.to change { notification.failed_at.try(:utc).to_s }.to(time.to_s)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
it 'sets the error code' do
|
|
214
|
+
expect do
|
|
215
|
+
store.mark_failed(notification, 42, '', time)
|
|
216
|
+
end.to change(notification, :error_code).to(42)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
it 'sets the error description' do
|
|
220
|
+
expect do
|
|
221
|
+
store.mark_failed(notification, 42, 'Weeee', time)
|
|
222
|
+
end.to change(notification, :error_description).to('Weeee')
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
it 'saves the notification without validation' do
|
|
226
|
+
notification.should_receive(:save!).with(:validate => false)
|
|
227
|
+
store.mark_failed(notification, nil, '', time)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
it 'does not save the notification if :persist => false' do
|
|
231
|
+
notification.should_not_receive(:save!)
|
|
232
|
+
store.mark_failed(notification, nil, '', time, :persist => false)
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
describe 'mark_batch_failed' do
|
|
237
|
+
it 'sets the attributes on the object for use in reflections' do
|
|
238
|
+
store.mark_batch_failed([notification], 123, 'an error')
|
|
239
|
+
notification.failed_at.should eq time
|
|
240
|
+
notification.delivered_at.should be_nil
|
|
241
|
+
notification.delivered.should be_false
|
|
242
|
+
notification.failed.should be_true
|
|
243
|
+
notification.error_code.should eq 123
|
|
244
|
+
notification.error_description.should eq 'an error'
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
it 'marks the notification as not delivered' do
|
|
248
|
+
store.mark_batch_failed([notification], nil, '')
|
|
249
|
+
notification.reload
|
|
250
|
+
notification.delivered.should be_false
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
it 'marks the notification as failed' do
|
|
254
|
+
expect do
|
|
255
|
+
store.mark_batch_failed([notification], nil, '')
|
|
256
|
+
notification.reload
|
|
257
|
+
end.to change(notification, :failed).to(true)
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
it 'sets the time the notification delivery failed' do
|
|
261
|
+
expect do
|
|
262
|
+
store.mark_batch_failed([notification], nil, '')
|
|
263
|
+
notification.reload
|
|
264
|
+
end.to change { notification.failed_at.try(:utc).to_s }.to(time.to_s)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
it 'sets the error code' do
|
|
268
|
+
expect do
|
|
269
|
+
store.mark_batch_failed([notification], 42, '')
|
|
270
|
+
notification.reload
|
|
271
|
+
end.to change(notification, :error_code).to(42)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
it 'sets the error description' do
|
|
275
|
+
expect do
|
|
276
|
+
store.mark_batch_failed([notification], 42, 'Weeee')
|
|
277
|
+
notification.reload
|
|
278
|
+
end.to change(notification, :error_description).to('Weeee')
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
describe 'create_apns_feedback' do
|
|
283
|
+
it 'creates the Feedback record' do
|
|
284
|
+
Rpush::Apns::Feedback.should_receive(:create!).with(
|
|
285
|
+
:failed_at => time, :device_token => 'ab' * 32, :app => app)
|
|
286
|
+
store.create_apns_feedback(time, 'ab' * 32, app)
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
describe 'create_gcm_notification' do
|
|
291
|
+
let(:data) { { :data => true } }
|
|
292
|
+
let(:attributes) { { :device_token => 'ab' * 32 } }
|
|
293
|
+
let(:registration_ids) { ['123', '456'] }
|
|
294
|
+
let(:deliver_after) { time + 10.seconds }
|
|
295
|
+
let(:args) { [attributes, data, registration_ids, deliver_after, app] }
|
|
296
|
+
|
|
297
|
+
it 'sets the given attributes' do
|
|
298
|
+
new_notification = store.create_gcm_notification(*args)
|
|
299
|
+
new_notification.device_token.should eq 'ab' * 32
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
it 'sets the given data' do
|
|
303
|
+
new_notification = store.create_gcm_notification(*args)
|
|
304
|
+
new_notification.data['data'].should be_true
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
it 'sets the given registration IDs' do
|
|
308
|
+
new_notification = store.create_gcm_notification(*args)
|
|
309
|
+
new_notification.registration_ids.should eq registration_ids
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
it 'sets the deliver_after timestamp' do
|
|
313
|
+
new_notification = store.create_gcm_notification(*args)
|
|
314
|
+
new_notification.deliver_after.to_s.should eq deliver_after.to_s
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
it 'saves the new notification' do
|
|
318
|
+
new_notification = store.create_gcm_notification(*args)
|
|
319
|
+
new_notification.new_record?.should be_false
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
describe 'create_adm_notification' do
|
|
324
|
+
let(:data) { { :data => true } }
|
|
325
|
+
let(:attributes) { {:app_id => app.id, :collapse_key => 'ckey', :delay_while_idle => true} }
|
|
326
|
+
let(:registration_ids) { ['123', '456'] }
|
|
327
|
+
let(:deliver_after) { time + 10.seconds }
|
|
328
|
+
let(:args) { [attributes, data, registration_ids, deliver_after, app] }
|
|
329
|
+
|
|
330
|
+
it 'sets the given attributes' do
|
|
331
|
+
new_notification = store.create_adm_notification(*args)
|
|
332
|
+
new_notification.app_id.should eq app.id
|
|
333
|
+
new_notification.collapse_key.should eq 'ckey'
|
|
334
|
+
new_notification.delay_while_idle.should be_true
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
it 'sets the given data' do
|
|
338
|
+
new_notification = store.create_adm_notification(*args)
|
|
339
|
+
new_notification.data['data'].should be_true
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
it 'sets the given registration IDs' do
|
|
343
|
+
new_notification = store.create_adm_notification(*args)
|
|
344
|
+
new_notification.registration_ids.should eq registration_ids
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
it 'sets the deliver_after timestamp' do
|
|
348
|
+
new_notification = store.create_adm_notification(*args)
|
|
349
|
+
new_notification.deliver_after.to_s.should eq deliver_after.to_s
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
it 'saves the new notification' do
|
|
353
|
+
new_notification = store.create_adm_notification(*args)
|
|
354
|
+
new_notification.new_record?.should be_false
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
end
|