rpush_extended 3.2.5
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 +365 -0
- data/LICENSE +7 -0
- data/README.md +393 -0
- data/bin/rpush +4 -0
- data/lib/generators/rpush_config_generator.rb +7 -0
- data/lib/generators/rpush_migration_generator.rb +66 -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 +117 -0
- data/lib/generators/templates/add_rpush.rb +402 -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 +25 -0
- data/lib/generators/templates/create_rapns_notifications.rb +36 -0
- data/lib/generators/templates/rename_rapns_to_rpush.rb +87 -0
- data/lib/generators/templates/rpush.rb +135 -0
- data/lib/generators/templates/rpush_2_0_0_updates.rb +79 -0
- data/lib/generators/templates/rpush_2_1_0_updates.rb +11 -0
- data/lib/generators/templates/rpush_2_6_0_updates.rb +10 -0
- data/lib/generators/templates/rpush_2_7_0_updates.rb +12 -0
- data/lib/generators/templates/rpush_3_0_0_updates.rb +11 -0
- data/lib/generators/templates/rpush_3_0_1_updates.rb +13 -0
- data/lib/generators/templates/rpush_3_1_0_add_pushy.rb +9 -0
- data/lib/generators/templates/rpush_3_1_1_updates.rb +15 -0
- data/lib/generators/templates/rpush_3_2_0_add_apns_p8.rb +15 -0
- data/lib/generators/templates/rpush_3_2_4_updates.rb +9 -0
- data/lib/generators/templates/rpush_3_3_0_updates.rb +9 -0
- data/lib/generators/templates/rpush_3_3_1_updates.rb +11 -0
- data/lib/rpush/apns_feedback.rb +17 -0
- data/lib/rpush/cli.rb +213 -0
- data/lib/rpush/client/active_model/adm/app.rb +23 -0
- data/lib/rpush/client/active_model/adm/data_validator.rb +14 -0
- data/lib/rpush/client/active_model/adm/notification.rb +28 -0
- data/lib/rpush/client/active_model/apns/app.rb +37 -0
- data/lib/rpush/client/active_model/apns/binary_notification_validator.rb +16 -0
- data/lib/rpush/client/active_model/apns/device_token_format_validator.rb +14 -0
- data/lib/rpush/client/active_model/apns/notification.rb +104 -0
- data/lib/rpush/client/active_model/apns2/app.rb +15 -0
- data/lib/rpush/client/active_model/apns2/notification.rb +9 -0
- data/lib/rpush/client/active_model/apnsp8/app.rb +23 -0
- data/lib/rpush/client/active_model/apnsp8/notification.rb +9 -0
- data/lib/rpush/client/active_model/gcm/app.rb +19 -0
- data/lib/rpush/client/active_model/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +14 -0
- data/lib/rpush/client/active_model/gcm/notification.rb +59 -0
- data/lib/rpush/client/active_model/notification.rb +22 -0
- data/lib/rpush/client/active_model/payload_data_size_validator.rb +13 -0
- data/lib/rpush/client/active_model/pushy/app.rb +20 -0
- data/lib/rpush/client/active_model/pushy/notification.rb +31 -0
- data/lib/rpush/client/active_model/pushy/time_to_live_validator.rb +14 -0
- data/lib/rpush/client/active_model/registration_ids_count_validator.rb +13 -0
- data/lib/rpush/client/active_model/wns/app.rb +23 -0
- data/lib/rpush/client/active_model/wns/notification.rb +32 -0
- data/lib/rpush/client/active_model/wpns/app.rb +13 -0
- data/lib/rpush/client/active_model/wpns/notification.rb +28 -0
- data/lib/rpush/client/active_model.rb +34 -0
- data/lib/rpush/client/active_record/adm/app.rb +11 -0
- data/lib/rpush/client/active_record/adm/notification.rb +11 -0
- data/lib/rpush/client/active_record/apns/app.rb +11 -0
- data/lib/rpush/client/active_record/apns/feedback.rb +18 -0
- data/lib/rpush/client/active_record/apns/notification.rb +40 -0
- data/lib/rpush/client/active_record/apns2/app.rb +11 -0
- data/lib/rpush/client/active_record/apns2/notification.rb +10 -0
- data/lib/rpush/client/active_record/apnsp8/app.rb +11 -0
- data/lib/rpush/client/active_record/apnsp8/notification.rb +10 -0
- data/lib/rpush/client/active_record/app.rb +13 -0
- data/lib/rpush/client/active_record/gcm/app.rb +11 -0
- data/lib/rpush/client/active_record/gcm/notification.rb +11 -0
- data/lib/rpush/client/active_record/notification.rb +42 -0
- data/lib/rpush/client/active_record/pushy/app.rb +11 -0
- data/lib/rpush/client/active_record/pushy/notification.rb +11 -0
- data/lib/rpush/client/active_record/wns/app.rb +11 -0
- data/lib/rpush/client/active_record/wns/badge_notification.rb +15 -0
- data/lib/rpush/client/active_record/wns/notification.rb +11 -0
- data/lib/rpush/client/active_record/wns/raw_notification.rb +13 -0
- data/lib/rpush/client/active_record/wpns/app.rb +11 -0
- data/lib/rpush/client/active_record/wpns/notification.rb +11 -0
- data/lib/rpush/client/active_record.rb +33 -0
- data/lib/rpush/client/redis/adm/app.rb +14 -0
- data/lib/rpush/client/redis/adm/notification.rb +11 -0
- data/lib/rpush/client/redis/apns/app.rb +11 -0
- data/lib/rpush/client/redis/apns/feedback.rb +20 -0
- data/lib/rpush/client/redis/apns/notification.rb +11 -0
- data/lib/rpush/client/redis/apns2/app.rb +11 -0
- data/lib/rpush/client/redis/apns2/notification.rb +11 -0
- data/lib/rpush/client/redis/apnsp8/app.rb +11 -0
- data/lib/rpush/client/redis/apnsp8/notification.rb +11 -0
- data/lib/rpush/client/redis/app.rb +29 -0
- data/lib/rpush/client/redis/gcm/app.rb +11 -0
- data/lib/rpush/client/redis/gcm/notification.rb +11 -0
- data/lib/rpush/client/redis/notification.rb +74 -0
- data/lib/rpush/client/redis/pushy/app.rb +16 -0
- data/lib/rpush/client/redis/pushy/notification.rb +18 -0
- data/lib/rpush/client/redis/wns/app.rb +14 -0
- data/lib/rpush/client/redis/wns/badge_notification.rb +15 -0
- data/lib/rpush/client/redis/wns/notification.rb +11 -0
- data/lib/rpush/client/redis/wns/raw_notification.rb +11 -0
- data/lib/rpush/client/redis/wpns/app.rb +11 -0
- data/lib/rpush/client/redis/wpns/notification.rb +11 -0
- data/lib/rpush/client/redis.rb +56 -0
- data/lib/rpush/configuration.rb +115 -0
- data/lib/rpush/daemon/adm/delivery.rb +226 -0
- data/lib/rpush/daemon/adm.rb +9 -0
- data/lib/rpush/daemon/apns/delivery.rb +43 -0
- data/lib/rpush/daemon/apns/feedback_receiver.rb +90 -0
- data/lib/rpush/daemon/apns.rb +17 -0
- data/lib/rpush/daemon/apns2/delivery.rb +127 -0
- data/lib/rpush/daemon/apns2.rb +10 -0
- data/lib/rpush/daemon/apnsp8/delivery.rb +166 -0
- data/lib/rpush/daemon/apnsp8/token.rb +43 -0
- data/lib/rpush/daemon/apnsp8.rb +10 -0
- data/lib/rpush/daemon/app_runner.rb +190 -0
- data/lib/rpush/daemon/batch.rb +138 -0
- data/lib/rpush/daemon/constants.rb +59 -0
- data/lib/rpush/daemon/delivery.rb +46 -0
- data/lib/rpush/daemon/delivery_error.rb +27 -0
- data/lib/rpush/daemon/dispatcher/apns_http2.rb +51 -0
- data/lib/rpush/daemon/dispatcher/apns_tcp.rb +152 -0
- data/lib/rpush/daemon/dispatcher/apnsp8_http2.rb +33 -0
- data/lib/rpush/daemon/dispatcher/http.rb +21 -0
- data/lib/rpush/daemon/dispatcher/tcp.rb +22 -0
- data/lib/rpush/daemon/dispatcher_loop.rb +73 -0
- data/lib/rpush/daemon/errors.rb +18 -0
- data/lib/rpush/daemon/feeder.rb +69 -0
- data/lib/rpush/daemon/gcm/delivery.rb +241 -0
- data/lib/rpush/daemon/gcm.rb +9 -0
- data/lib/rpush/daemon/interruptible_sleep.rb +24 -0
- data/lib/rpush/daemon/loggable.rb +33 -0
- data/lib/rpush/daemon/proc_title.rb +17 -0
- data/lib/rpush/daemon/pushy/delivery.rb +90 -0
- data/lib/rpush/daemon/pushy.rb +9 -0
- data/lib/rpush/daemon/queue_payload.rb +12 -0
- data/lib/rpush/daemon/retry_header_parser.rb +23 -0
- data/lib/rpush/daemon/retryable_error.rb +22 -0
- data/lib/rpush/daemon/ring_buffer.rb +16 -0
- data/lib/rpush/daemon/rpc/client.rb +27 -0
- data/lib/rpush/daemon/rpc/server.rb +82 -0
- data/lib/rpush/daemon/rpc.rb +9 -0
- data/lib/rpush/daemon/service_config_methods.rb +51 -0
- data/lib/rpush/daemon/signal_handler.rb +75 -0
- data/lib/rpush/daemon/store/active_record/reconnectable.rb +80 -0
- data/lib/rpush/daemon/store/active_record.rb +214 -0
- data/lib/rpush/daemon/store/interface.rb +20 -0
- data/lib/rpush/daemon/store/redis.rb +166 -0
- data/lib/rpush/daemon/string_helpers.rb +15 -0
- data/lib/rpush/daemon/synchronizer.rb +62 -0
- data/lib/rpush/daemon/tcp_connection.rb +190 -0
- data/lib/rpush/daemon/wns/badge_request.rb +32 -0
- data/lib/rpush/daemon/wns/delivery.rb +178 -0
- data/lib/rpush/daemon/wns/post_request.rb +33 -0
- data/lib/rpush/daemon/wns/raw_request.rb +22 -0
- data/lib/rpush/daemon/wns/toast_request.rb +54 -0
- data/lib/rpush/daemon/wns.rb +9 -0
- data/lib/rpush/daemon/wpns/delivery.rb +132 -0
- data/lib/rpush/daemon/wpns.rb +9 -0
- data/lib/rpush/daemon.rb +179 -0
- data/lib/rpush/deprecatable.rb +24 -0
- data/lib/rpush/deprecation.rb +26 -0
- data/lib/rpush/embed.rb +41 -0
- data/lib/rpush/logger.rb +92 -0
- data/lib/rpush/multi_json_helper.rb +16 -0
- data/lib/rpush/plugin.rb +44 -0
- data/lib/rpush/push.rb +11 -0
- data/lib/rpush/reflectable.rb +13 -0
- data/lib/rpush/reflection_collection.rb +44 -0
- data/lib/rpush/reflection_public_methods.rb +11 -0
- data/lib/rpush/version.rb +14 -0
- data/lib/rpush.rb +43 -0
- data/lib/tasks/quality.rake +35 -0
- data/lib/tasks/test.rake +69 -0
- data/spec/.rubocop.yml +4 -0
- data/spec/functional/adm_spec.rb +50 -0
- data/spec/functional/apns2_spec.rb +232 -0
- data/spec/functional/apns_spec.rb +162 -0
- data/spec/functional/cli_spec.rb +36 -0
- data/spec/functional/embed_spec.rb +49 -0
- data/spec/functional/gcm_spec.rb +46 -0
- data/spec/functional/new_app_spec.rb +44 -0
- data/spec/functional/pushy_spec.rb +22 -0
- data/spec/functional/retry_spec.rb +42 -0
- data/spec/functional/synchronization_spec.rb +97 -0
- data/spec/functional/wpns_spec.rb +71 -0
- data/spec/functional_spec_helper.rb +32 -0
- data/spec/spec_helper.rb +69 -0
- data/spec/support/active_record_setup.rb +73 -0
- data/spec/support/cert_with_password.pem +90 -0
- data/spec/support/cert_without_password.pem +59 -0
- data/spec/support/config/database.yml +44 -0
- data/spec/support/simplecov_helper.rb +24 -0
- data/spec/support/simplecov_quality_formatter.rb +12 -0
- data/spec/tmp/.gitkeep +0 -0
- data/spec/unit/apns_feedback_spec.rb +28 -0
- data/spec/unit/client/active_record/adm/app_spec.rb +58 -0
- data/spec/unit/client/active_record/adm/notification_spec.rb +43 -0
- data/spec/unit/client/active_record/apns/app_spec.rb +29 -0
- data/spec/unit/client/active_record/apns/feedback_spec.rb +9 -0
- data/spec/unit/client/active_record/apns/notification_spec.rb +324 -0
- data/spec/unit/client/active_record/app_spec.rb +30 -0
- data/spec/unit/client/active_record/gcm/app_spec.rb +4 -0
- data/spec/unit/client/active_record/gcm/notification_spec.rb +67 -0
- data/spec/unit/client/active_record/notification_spec.rb +21 -0
- data/spec/unit/client/active_record/pushy/app_spec.rb +17 -0
- data/spec/unit/client/active_record/pushy/notification_spec.rb +65 -0
- data/spec/unit/client/active_record/wns/badge_notification_spec.rb +15 -0
- data/spec/unit/client/active_record/wns/raw_notification_spec.rb +26 -0
- data/spec/unit/client/active_record/wpns/app_spec.rb +4 -0
- data/spec/unit/client/active_record/wpns/notification_spec.rb +21 -0
- data/spec/unit/configuration_spec.rb +46 -0
- data/spec/unit/daemon/adm/delivery_spec.rb +253 -0
- data/spec/unit/daemon/apns/certificate_expired_error_spec.rb +11 -0
- data/spec/unit/daemon/apns/delivery_spec.rb +108 -0
- data/spec/unit/daemon/apns/feedback_receiver_spec.rb +119 -0
- data/spec/unit/daemon/app_runner_spec.rb +188 -0
- data/spec/unit/daemon/batch_spec.rb +169 -0
- data/spec/unit/daemon/delivery_error_spec.rb +13 -0
- data/spec/unit/daemon/delivery_spec.rb +51 -0
- data/spec/unit/daemon/dispatcher/http_spec.rb +34 -0
- data/spec/unit/daemon/dispatcher/tcp_spec.rb +32 -0
- data/spec/unit/daemon/dispatcher_loop_spec.rb +53 -0
- data/spec/unit/daemon/feeder_spec.rb +96 -0
- data/spec/unit/daemon/gcm/delivery_spec.rb +387 -0
- data/spec/unit/daemon/proc_title_spec.rb +11 -0
- data/spec/unit/daemon/pushy/delivery_spec.rb +159 -0
- data/spec/unit/daemon/retryable_error_spec.rb +14 -0
- data/spec/unit/daemon/service_config_methods_spec.rb +36 -0
- data/spec/unit/daemon/signal_handler_spec.rb +99 -0
- data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +165 -0
- data/spec/unit/daemon/store/active_record_spec.rb +357 -0
- data/spec/unit/daemon/store/redis_spec.rb +365 -0
- data/spec/unit/daemon/tcp_connection_spec.rb +292 -0
- data/spec/unit/daemon/wns/delivery_spec.rb +176 -0
- data/spec/unit/daemon/wns/post_request_spec.rb +117 -0
- data/spec/unit/daemon/wpns/delivery_spec.rb +167 -0
- data/spec/unit/daemon_spec.rb +138 -0
- data/spec/unit/deprecatable_spec.rb +32 -0
- data/spec/unit/deprecation_spec.rb +15 -0
- data/spec/unit/embed_spec.rb +47 -0
- data/spec/unit/logger_spec.rb +127 -0
- data/spec/unit/notification_shared.rb +53 -0
- data/spec/unit/plugin_spec.rb +36 -0
- data/spec/unit/push_spec.rb +34 -0
- data/spec/unit/reflectable_spec.rb +27 -0
- data/spec/unit/reflection_collection_spec.rb +26 -0
- data/spec/unit/rpush_spec.rb +8 -0
- data/spec/unit_spec_helper.rb +26 -0
- metadata +709 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
require 'unit_spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Rpush::Daemon::SignalHandler do
|
|
4
|
+
def signal_handler(sig)
|
|
5
|
+
Process.kill(sig, Process.pid)
|
|
6
|
+
sleep 0.1
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def with_handler_start_stop
|
|
10
|
+
Rpush::Daemon::SignalHandler.start
|
|
11
|
+
yield
|
|
12
|
+
ensure
|
|
13
|
+
Rpush::Daemon::SignalHandler.stop
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
describe 'shutdown signals' do
|
|
17
|
+
unless Rpush.jruby? # These tests do not work on JRuby.
|
|
18
|
+
it "shuts down when signaled signaled SIGINT" do
|
|
19
|
+
with_handler_start_stop do
|
|
20
|
+
expect(Rpush::Daemon).to receive(:shutdown)
|
|
21
|
+
signal_handler('SIGINT')
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "shuts down when signaled signaled SIGTERM" do
|
|
26
|
+
with_handler_start_stop do
|
|
27
|
+
expect(Rpush::Daemon).to receive(:shutdown)
|
|
28
|
+
signal_handler('SIGTERM')
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe 'config.embedded = true' do
|
|
35
|
+
before { Rpush.config.embedded = true }
|
|
36
|
+
|
|
37
|
+
it 'does not trap signals' do
|
|
38
|
+
expect(Signal).not_to receive(:trap)
|
|
39
|
+
Rpush::Daemon::SignalHandler.start
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
describe 'HUP' do
|
|
44
|
+
before do
|
|
45
|
+
allow(Rpush::Daemon::Synchronizer).to receive(:sync)
|
|
46
|
+
allow(Rpush::Daemon::Feeder).to receive(:wakeup)
|
|
47
|
+
allow(Rpush::Daemon).to receive_messages(store: double(reopen_log: nil))
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'syncs' do
|
|
51
|
+
with_handler_start_stop do
|
|
52
|
+
expect(Rpush::Daemon::Synchronizer).to receive(:sync)
|
|
53
|
+
signal_handler('HUP')
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it 'wakes up the Feeder' do
|
|
58
|
+
with_handler_start_stop do
|
|
59
|
+
expect(Rpush::Daemon::Feeder).to receive(:wakeup)
|
|
60
|
+
signal_handler('HUP')
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
describe 'USR2' do
|
|
66
|
+
it 'instructs the AppRunner to print debug information' do
|
|
67
|
+
with_handler_start_stop do
|
|
68
|
+
expect(Rpush::Daemon::AppRunner).to receive(:debug)
|
|
69
|
+
signal_handler('USR2')
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
describe 'error handing' do
|
|
75
|
+
let(:error) { StandardError.new('test') }
|
|
76
|
+
|
|
77
|
+
before do
|
|
78
|
+
allow(Rpush).to receive_messages(logger: double(error: nil, info: nil, reopen: nil))
|
|
79
|
+
allow(Rpush::Daemon).to receive_messages(store: double(reopen_log: nil))
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it 'logs errors received when handling a signal' do
|
|
83
|
+
allow(Rpush::Daemon::Synchronizer).to receive(:sync).and_raise(error)
|
|
84
|
+
expect(Rpush.logger).to receive(:error).with(error)
|
|
85
|
+
with_handler_start_stop do
|
|
86
|
+
signal_handler('HUP')
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it 'does not interrupt processing of further errors' do
|
|
91
|
+
allow(Rpush::Daemon::Synchronizer).to receive(:sync).and_raise(error)
|
|
92
|
+
expect(Rpush::Daemon::AppRunner).to receive(:debug)
|
|
93
|
+
with_handler_start_stop do
|
|
94
|
+
signal_handler('HUP')
|
|
95
|
+
signal_handler('USR2')
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,165 @@
|
|
|
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
|
+
fail @error if @calls <= @max_calls
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
let(:adapter_error_class) do
|
|
25
|
+
case SPEC_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
|
+
fail "Please update #{__FILE__} for adapter #{SPEC_ADAPTER}"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
let(:error) { adapter_error_class.new("db down!") }
|
|
46
|
+
let(:timeout) { ActiveRecord::ConnectionTimeoutError.new("db lazy!") }
|
|
47
|
+
let(:test_doubles) { [TestDouble.new(error, 1), TestDouble.new(timeout, 1)] }
|
|
48
|
+
|
|
49
|
+
before do
|
|
50
|
+
@logger = double("Logger", info: nil, error: nil, warn: nil)
|
|
51
|
+
allow(Rpush).to receive(:logger).and_return(@logger)
|
|
52
|
+
|
|
53
|
+
allow(ActiveRecord::Base).to receive(:clear_all_connections!)
|
|
54
|
+
allow(ActiveRecord::Base).to receive(:establish_connection)
|
|
55
|
+
test_doubles.each { |td| allow(td).to receive(:sleep) }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "should log the error raised" do
|
|
59
|
+
expect(Rpush.logger).to receive(:error).with(error)
|
|
60
|
+
test_doubles.each(&:perform)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "should log that the database is being reconnected" do
|
|
64
|
+
expect(Rpush.logger).to receive(:warn).with("Lost connection to database, reconnecting...")
|
|
65
|
+
test_doubles.each(&:perform)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "should log the reconnection attempt" do
|
|
69
|
+
expect(Rpush.logger).to receive(:warn).with("Attempt 1")
|
|
70
|
+
test_doubles.each(&:perform)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "should clear all connections" do
|
|
74
|
+
expect(ActiveRecord::Base).to receive(:clear_all_connections!)
|
|
75
|
+
test_doubles.each(&:perform)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "should establish a new connection" do
|
|
79
|
+
expect(ActiveRecord::Base).to receive(:establish_connection)
|
|
80
|
+
test_doubles.each(&:perform)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "should test out the new connection by performing a count" do
|
|
84
|
+
expect(Rpush::Client::ActiveRecord::Notification).to receive(:count).twice
|
|
85
|
+
test_doubles.each(&:perform)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
context "should reconnect on" do
|
|
89
|
+
[
|
|
90
|
+
::ActiveRecord::ConnectionNotEstablished,
|
|
91
|
+
::ActiveRecord::ConnectionTimeoutError,
|
|
92
|
+
::ActiveRecord::JDBCError,
|
|
93
|
+
::ActiveRecord::StatementInvalid,
|
|
94
|
+
Mysql::Error,
|
|
95
|
+
Mysql2::Error,
|
|
96
|
+
PG::Error,
|
|
97
|
+
PGError,
|
|
98
|
+
SQLite3::Exception
|
|
99
|
+
].each do |error_class|
|
|
100
|
+
let(:error) { error_class.new }
|
|
101
|
+
|
|
102
|
+
it error_class.name do
|
|
103
|
+
expect(ActiveRecord::Base).to receive(:establish_connection)
|
|
104
|
+
test_doubles.each(&:perform)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
context "should not reconnect on" do
|
|
110
|
+
let(:error) { ActiveRecord::ActiveRecordError.new }
|
|
111
|
+
|
|
112
|
+
it "ActiveRecord::ActiveRecordError" do
|
|
113
|
+
expect(ActiveRecord::Base).not_to receive(:establish_connection)
|
|
114
|
+
expect { test_doubles.each(&:perform) }.to raise_error(ActiveRecord::ActiveRecordError)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
context "when the reconnection attempt is not successful" do
|
|
119
|
+
before do
|
|
120
|
+
class << Rpush::Client::ActiveRecord::Notification
|
|
121
|
+
def count
|
|
122
|
+
@count_calls += 1
|
|
123
|
+
return if @count_calls == 2
|
|
124
|
+
fail @error
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
Rpush::Client::ActiveRecord::Notification.instance_variable_set("@count_calls", 0)
|
|
128
|
+
Rpush::Client::ActiveRecord::Notification.instance_variable_set("@error", error)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
describe "error behaviour" do
|
|
132
|
+
it "should log the 2nd attempt" do
|
|
133
|
+
expect(Rpush.logger).to receive(:warn).with("Attempt 2")
|
|
134
|
+
test_doubles[0].perform
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it "should log errors raised when the reconnection is not successful" do
|
|
138
|
+
expect(Rpush.logger).to receive(:error).with(error)
|
|
139
|
+
test_doubles[0].perform
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it "should sleep to avoid thrashing when the database is down" do
|
|
143
|
+
expect(test_doubles[0]).to receive(:sleep).with(2)
|
|
144
|
+
test_doubles[0].perform
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
describe "timeout behaviour" do
|
|
149
|
+
it "should log the 2nd attempt" do
|
|
150
|
+
expect(Rpush.logger).to receive(:warn).with("Attempt 2")
|
|
151
|
+
test_doubles[1].perform
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
it "should log errors raised when the reconnection is not successful" do
|
|
155
|
+
expect(Rpush.logger).to receive(:error).with(error)
|
|
156
|
+
test_doubles[1].perform
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
it "should sleep to avoid thrashing when the database is down" do
|
|
160
|
+
expect(test_doubles[1]).to receive(:sleep).with(2)
|
|
161
|
+
test_doubles[1].perform
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end if active_record?
|
|
@@ -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::Client::ActiveRecord::Apns::App.create!(name: 'my_app', environment: 'development', certificate: TEST_CERT) }
|
|
6
|
+
let(:notification) { Rpush::Client::ActiveRecord::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, internal_logger: nil) }
|
|
10
|
+
|
|
11
|
+
before do
|
|
12
|
+
allow(Rpush).to receive_messages(logger: logger)
|
|
13
|
+
allow(Time).to receive_messages(now: time)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'updates an notification' do
|
|
17
|
+
expect(notification).to receive(:save!)
|
|
18
|
+
store.update_notification(notification)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'updates an app' do
|
|
22
|
+
expect(app).to receive(:save!)
|
|
23
|
+
store.update_app(app)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'finds an app by ID' do
|
|
27
|
+
expect(store.app(app.id)).to eq(app)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'finds all apps' do
|
|
31
|
+
app
|
|
32
|
+
expect(store.all_apps).to eq([app])
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'translates an Integer notification ID' do
|
|
36
|
+
expect(store.translate_integer_notification_id(notification.id)).to eq(notification.id)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'returns the pending notification count' do
|
|
40
|
+
notification
|
|
41
|
+
expect(store.pending_delivery_count).to eq(1)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'can release a connection' do
|
|
45
|
+
expect(ActiveRecord::Base.connection).to receive(:close)
|
|
46
|
+
store.release_connection
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'logs errors raised when trying to release the connection' do
|
|
50
|
+
e = StandardError.new
|
|
51
|
+
allow(ActiveRecord::Base.connection).to receive(:close).and_raise(e)
|
|
52
|
+
expect(Rpush.logger).to receive(:error).with(e)
|
|
53
|
+
store.release_connection
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
describe 'deliverable_notifications' do
|
|
57
|
+
it 'checks for new notifications with the ability to reconnect the database' do
|
|
58
|
+
expect(store).to receive(:with_database_reconnect_and_retry)
|
|
59
|
+
store.deliverable_notifications(Rpush.config.batch_size)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it 'loads notifications in batches' do
|
|
63
|
+
Rpush.config.batch_size = 5000
|
|
64
|
+
relation = double.as_null_object
|
|
65
|
+
expect(relation).to receive(:limit).with(5000)
|
|
66
|
+
allow(relation).to receive_messages(pluck: [])
|
|
67
|
+
allow(store).to receive_messages(ready_for_delivery: relation)
|
|
68
|
+
store.deliverable_notifications(Rpush.config.batch_size)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it 'loads an undelivered notification without deliver_after set' do
|
|
72
|
+
notification.update_attributes!(delivered: false, deliver_after: nil)
|
|
73
|
+
expect(store.deliverable_notifications(Rpush.config.batch_size)).to eq [notification]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it 'loads an notification with a deliver_after time in the past' do
|
|
77
|
+
notification.update_attributes!(delivered: false, deliver_after: 1.hour.ago)
|
|
78
|
+
expect(store.deliverable_notifications(Rpush.config.batch_size)).to eq [notification]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it 'does not load an notification with a deliver_after time in the future' do
|
|
82
|
+
notification.update_attributes!(delivered: false, deliver_after: 1.hour.from_now)
|
|
83
|
+
expect(store.deliverable_notifications(Rpush.config.batch_size)).to be_empty
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it 'does not load a previously delivered notification' do
|
|
87
|
+
notification.update_attributes!(delivered: true, delivered_at: time)
|
|
88
|
+
expect(store.deliverable_notifications(Rpush.config.batch_size)).to be_empty
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "does not enqueue a notification that has previously failed delivery" do
|
|
92
|
+
notification.update_attributes!(delivered: false, failed: true)
|
|
93
|
+
expect(store.deliverable_notifications(Rpush.config.batch_size)).to 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
|
+
expect(notification).to 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
|
+
expect(notification).not_to 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
|
+
expect(notification.deliver_after).to eq deliver_after
|
|
128
|
+
expect(notification.retries).to 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
|
+
expect(notification).to 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
|
+
expect(notification).not_to 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
|
+
expect(notification.delivered_at).to eq time
|
|
175
|
+
expect(notification.delivered).to be_truthy
|
|
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
|
+
expect(notification.delivered).to eq(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
|
+
expect(notification).to 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
|
+
expect(notification).not_to 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
|
+
expect(notification.failed_at).to eq time
|
|
240
|
+
expect(notification.delivered_at).to be_nil
|
|
241
|
+
expect(notification.delivered).to eq(false)
|
|
242
|
+
expect(notification.failed).to be_truthy
|
|
243
|
+
expect(notification.error_code).to eq 123
|
|
244
|
+
expect(notification.error_description).to 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
|
+
expect(notification.delivered).to be_falsey
|
|
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
|
+
expect(Rpush::Client::ActiveRecord::Apns::Feedback).to receive(:create!).with(
|
|
285
|
+
failed_at: time, device_token: 'ab' * 32, app_id: app.id)
|
|
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) { %w(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
|
+
expect(new_notification.device_token).to eq 'ab' * 32
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
it 'sets the given data' do
|
|
303
|
+
new_notification = store.create_gcm_notification(*args)
|
|
304
|
+
expect(new_notification.data['data']).to be_truthy
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
it 'sets the given registration IDs' do
|
|
308
|
+
new_notification = store.create_gcm_notification(*args)
|
|
309
|
+
expect(new_notification.registration_ids).to eq registration_ids
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
it 'sets the deliver_after timestamp' do
|
|
313
|
+
new_notification = store.create_gcm_notification(*args)
|
|
314
|
+
expect(new_notification.deliver_after.to_s).to eq deliver_after.to_s
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
it 'saves the new notification' do
|
|
318
|
+
new_notification = store.create_gcm_notification(*args)
|
|
319
|
+
expect(new_notification.new_record?).to be_falsey
|
|
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) { %w(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
|
+
expect(new_notification.app_id).to eq app.id
|
|
333
|
+
expect(new_notification.collapse_key).to eq 'ckey'
|
|
334
|
+
expect(new_notification.delay_while_idle).to be_truthy
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
it 'sets the given data' do
|
|
338
|
+
new_notification = store.create_adm_notification(*args)
|
|
339
|
+
expect(new_notification.data['data']).to be_truthy
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
it 'sets the given registration IDs' do
|
|
343
|
+
new_notification = store.create_adm_notification(*args)
|
|
344
|
+
expect(new_notification.registration_ids).to eq registration_ids
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
it 'sets the deliver_after timestamp' do
|
|
348
|
+
new_notification = store.create_adm_notification(*args)
|
|
349
|
+
expect(new_notification.deliver_after.to_s).to eq deliver_after.to_s
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
it 'saves the new notification' do
|
|
353
|
+
new_notification = store.create_adm_notification(*args)
|
|
354
|
+
expect(new_notification.new_record?).to be_falsey
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
end if active_record?
|