rpush 8.0.0 → 9.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 +4 -4
- data/CHANGELOG.md +12 -6
- data/README.md +8 -65
- data/lib/generators/templates/rpush.rb +9 -16
- data/lib/rpush/client/active_model.rb +0 -4
- data/lib/rpush/client/active_record.rb +0 -3
- data/lib/rpush/client/redis.rb +0 -3
- data/lib/rpush/configuration.rb +2 -19
- data/lib/rpush/daemon/service_config_methods.rb +0 -2
- data/lib/rpush/daemon/store/active_record.rb +2 -14
- data/lib/rpush/daemon/store/interface.rb +2 -2
- data/lib/rpush/daemon/store/redis.rb +2 -11
- data/lib/rpush/daemon.rb +0 -10
- data/lib/rpush/reflection_collection.rb +1 -2
- data/lib/rpush/version.rb +1 -1
- data/lib/rpush.rb +0 -1
- data/spec/functional/cli_spec.rb +41 -15
- data/spec/functional/embed_spec.rb +57 -26
- data/spec/functional/retry_spec.rb +21 -4
- data/spec/functional/synchronization_spec.rb +1 -1
- data/spec/functional_spec_helper.rb +0 -6
- data/spec/spec_helper.rb +2 -0
- data/spec/unit/client/active_record/shared/app.rb +1 -1
- data/spec/unit/daemon/shared/store.rb +0 -42
- metadata +49 -55
- data/lib/rpush/apns_feedback.rb +0 -18
- data/lib/rpush/client/active_model/gcm/app.rb +0 -19
- data/lib/rpush/client/active_model/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +0 -14
- data/lib/rpush/client/active_model/gcm/notification.rb +0 -62
- data/lib/rpush/client/active_record/gcm/app.rb +0 -11
- data/lib/rpush/client/active_record/gcm/notification.rb +0 -11
- data/lib/rpush/client/redis/gcm/app.rb +0 -11
- data/lib/rpush/client/redis/gcm/notification.rb +0 -11
- data/lib/rpush/daemon/apns/delivery.rb +0 -43
- data/lib/rpush/daemon/apns/feedback_receiver.rb +0 -91
- data/lib/rpush/daemon/apns.rb +0 -17
- data/lib/rpush/daemon/dispatcher/apns_tcp.rb +0 -152
- data/lib/rpush/daemon/dispatcher/tcp.rb +0 -22
- data/lib/rpush/daemon/gcm/delivery.rb +0 -241
- data/lib/rpush/daemon/gcm.rb +0 -9
- data/lib/rpush/daemon/tcp_connection.rb +0 -190
- data/spec/functional/apns_spec.rb +0 -162
- data/spec/functional/gcm_priority_spec.rb +0 -40
- data/spec/functional/gcm_spec.rb +0 -46
- data/spec/functional/new_app_spec.rb +0 -44
- data/spec/unit/apns_feedback_spec.rb +0 -39
- data/spec/unit/client/active_record/gcm/app_spec.rb +0 -6
- data/spec/unit/client/active_record/gcm/notification_spec.rb +0 -14
- data/spec/unit/client/redis/gcm/app_spec.rb +0 -5
- data/spec/unit/client/redis/gcm/notification_spec.rb +0 -5
- data/spec/unit/client/shared/gcm/app.rb +0 -4
- data/spec/unit/client/shared/gcm/notification.rb +0 -77
- data/spec/unit/daemon/apns/delivery_spec.rb +0 -108
- data/spec/unit/daemon/apns/feedback_receiver_spec.rb +0 -137
- data/spec/unit/daemon/dispatcher/tcp_spec.rb +0 -32
- data/spec/unit/daemon/gcm/delivery_spec.rb +0 -387
- data/spec/unit/daemon/tcp_connection_spec.rb +0 -293
@@ -1,190 +0,0 @@
|
|
1
|
-
module Rpush
|
2
|
-
module Daemon
|
3
|
-
class TcpConnectionError < StandardError; end
|
4
|
-
|
5
|
-
class TcpConnection
|
6
|
-
include Reflectable
|
7
|
-
include Loggable
|
8
|
-
|
9
|
-
OSX_TCP_KEEPALIVE = 0x10 # Defined in <netinet/tcp.h>
|
10
|
-
KEEPALIVE_INTERVAL = 5
|
11
|
-
KEEPALIVE_IDLE = 5
|
12
|
-
KEEPALIVE_MAX_FAIL_PROBES = 1
|
13
|
-
TCP_ERRORS = [SystemCallError, OpenSSL::OpenSSLError, IOError]
|
14
|
-
|
15
|
-
attr_accessor :last_touch
|
16
|
-
attr_reader :host, :port
|
17
|
-
|
18
|
-
def self.idle_period
|
19
|
-
30.minutes
|
20
|
-
end
|
21
|
-
|
22
|
-
def initialize(app, host, port)
|
23
|
-
@app = app
|
24
|
-
@host = host
|
25
|
-
@port = port
|
26
|
-
@certificate = app.certificate
|
27
|
-
@password = app.password
|
28
|
-
@connected = false
|
29
|
-
@connection_callbacks = []
|
30
|
-
touch
|
31
|
-
end
|
32
|
-
|
33
|
-
def on_connect(&blk)
|
34
|
-
raise 'already connected' if @connected
|
35
|
-
@connection_callbacks << blk
|
36
|
-
end
|
37
|
-
|
38
|
-
def connect
|
39
|
-
@ssl_context = setup_ssl_context
|
40
|
-
@tcp_socket, @ssl_socket = connect_socket
|
41
|
-
@connected = true
|
42
|
-
|
43
|
-
@connection_callbacks.each do |blk|
|
44
|
-
begin
|
45
|
-
blk.call
|
46
|
-
rescue StandardError => e
|
47
|
-
log_error(e)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
@connection_callbacks.clear
|
52
|
-
end
|
53
|
-
|
54
|
-
def close
|
55
|
-
@ssl_socket.close if @ssl_socket
|
56
|
-
@tcp_socket.close if @tcp_socket
|
57
|
-
rescue IOError # rubocop:disable HandleExceptions
|
58
|
-
end
|
59
|
-
|
60
|
-
def read(num_bytes)
|
61
|
-
@ssl_socket.read(num_bytes) if @ssl_socket
|
62
|
-
end
|
63
|
-
|
64
|
-
def select(timeout)
|
65
|
-
IO.select([@ssl_socket], nil, nil, timeout) if @ssl_socket
|
66
|
-
end
|
67
|
-
|
68
|
-
def write(data)
|
69
|
-
connect unless @connected
|
70
|
-
reconnect_idle if idle_period_exceeded?
|
71
|
-
|
72
|
-
retry_count = 0
|
73
|
-
|
74
|
-
begin
|
75
|
-
write_data(data)
|
76
|
-
rescue *TCP_ERRORS => e
|
77
|
-
retry_count += 1
|
78
|
-
|
79
|
-
if retry_count == 1
|
80
|
-
log_error("Lost connection to #{@host}:#{@port} (#{e.class.name}, #{e.message}), reconnecting...")
|
81
|
-
reflect(:tcp_connection_lost, @app, e)
|
82
|
-
end
|
83
|
-
|
84
|
-
if retry_count <= 3
|
85
|
-
reconnect_with_rescue
|
86
|
-
sleep 1
|
87
|
-
retry
|
88
|
-
else
|
89
|
-
raise TcpConnectionError, "#{@app.name} tried #{retry_count - 1} times to reconnect but failed (#{e.class.name}, #{e.message})."
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def reconnect_with_rescue
|
95
|
-
reconnect
|
96
|
-
rescue StandardError => e
|
97
|
-
log_error(e)
|
98
|
-
end
|
99
|
-
|
100
|
-
def reconnect
|
101
|
-
close
|
102
|
-
@tcp_socket, @ssl_socket = connect_socket
|
103
|
-
end
|
104
|
-
|
105
|
-
protected
|
106
|
-
|
107
|
-
def reconnect_idle
|
108
|
-
log_info("Idle period exceeded, reconnecting...")
|
109
|
-
reconnect
|
110
|
-
end
|
111
|
-
|
112
|
-
def idle_period_exceeded?
|
113
|
-
Time.now - last_touch > self.class.idle_period
|
114
|
-
end
|
115
|
-
|
116
|
-
def write_data(data)
|
117
|
-
@ssl_socket.write(data)
|
118
|
-
@ssl_socket.flush
|
119
|
-
touch
|
120
|
-
end
|
121
|
-
|
122
|
-
def touch
|
123
|
-
self.last_touch = Time.now
|
124
|
-
end
|
125
|
-
|
126
|
-
def setup_ssl_context
|
127
|
-
ssl_context = OpenSSL::SSL::SSLContext.new
|
128
|
-
ssl_context.key = OpenSSL::PKey::RSA.new(@certificate, @password)
|
129
|
-
ssl_context.cert = OpenSSL::X509::Certificate.new(@certificate)
|
130
|
-
ssl_context
|
131
|
-
end
|
132
|
-
|
133
|
-
def connect_socket
|
134
|
-
touch
|
135
|
-
check_certificate_expiration
|
136
|
-
|
137
|
-
tcp_socket = TCPSocket.new(@host, @port)
|
138
|
-
tcp_socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)
|
139
|
-
tcp_socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true)
|
140
|
-
|
141
|
-
# Linux
|
142
|
-
if [:SOL_TCP, :TCP_KEEPIDLE, :TCP_KEEPINTVL, :TCP_KEEPCNT].all? { |c| Socket.const_defined?(c) }
|
143
|
-
tcp_socket.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPIDLE, KEEPALIVE_IDLE)
|
144
|
-
tcp_socket.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPINTVL, KEEPALIVE_INTERVAL)
|
145
|
-
tcp_socket.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT, KEEPALIVE_MAX_FAIL_PROBES)
|
146
|
-
end
|
147
|
-
|
148
|
-
# OSX
|
149
|
-
if RUBY_PLATFORM =~ /darwin/
|
150
|
-
tcp_socket.setsockopt(Socket::IPPROTO_TCP, OSX_TCP_KEEPALIVE, KEEPALIVE_IDLE)
|
151
|
-
end
|
152
|
-
|
153
|
-
ssl_socket = OpenSSL::SSL::SSLSocket.new(tcp_socket, @ssl_context)
|
154
|
-
ssl_socket.sync = true
|
155
|
-
ssl_socket.connect
|
156
|
-
[tcp_socket, ssl_socket]
|
157
|
-
rescue *TCP_ERRORS => error
|
158
|
-
if error.message =~ /certificate revoked/i
|
159
|
-
log_error('Certificate has been revoked.')
|
160
|
-
reflect(:ssl_certificate_revoked, @app, error)
|
161
|
-
end
|
162
|
-
raise TcpConnectionError, "#{error.class.name}, #{error.message}"
|
163
|
-
end
|
164
|
-
|
165
|
-
def check_certificate_expiration
|
166
|
-
cert = @ssl_context.cert
|
167
|
-
if certificate_expired?
|
168
|
-
log_error(certificate_msg('expired'))
|
169
|
-
raise Rpush::CertificateExpiredError.new(@app, cert.not_after)
|
170
|
-
elsif certificate_expires_soon?
|
171
|
-
log_warn(certificate_msg('will expire'))
|
172
|
-
reflect(:ssl_certificate_will_expire, @app, cert.not_after)
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
def certificate_msg(msg)
|
177
|
-
time = @ssl_context.cert.not_after.utc.strftime('%Y-%m-%d %H:%M:%S UTC')
|
178
|
-
"Certificate #{msg} at #{time}."
|
179
|
-
end
|
180
|
-
|
181
|
-
def certificate_expired?
|
182
|
-
@ssl_context.cert.not_after && @ssl_context.cert.not_after.utc < Time.now.utc
|
183
|
-
end
|
184
|
-
|
185
|
-
def certificate_expires_soon?
|
186
|
-
@ssl_context.cert.not_after && @ssl_context.cert.not_after.utc < (Time.now + 1.month).utc
|
187
|
-
end
|
188
|
-
end
|
189
|
-
end
|
190
|
-
end
|
@@ -1,162 +0,0 @@
|
|
1
|
-
require 'functional_spec_helper'
|
2
|
-
|
3
|
-
describe 'APNs' do
|
4
|
-
let(:app) { create_app }
|
5
|
-
let(:tcp_socket) { double(TCPSocket, setsockopt: nil, close: nil) }
|
6
|
-
let(:ssl_socket) { double(OpenSSL::SSL::SSLSocket, :sync= => nil, connect: nil, write: nil, flush: nil, read: nil, close: nil) }
|
7
|
-
let(:io_double) { double(select: nil) }
|
8
|
-
let(:delivered_ids) { [] }
|
9
|
-
let(:failed_ids) { [] }
|
10
|
-
let(:retry_ids) { [] }
|
11
|
-
|
12
|
-
before do
|
13
|
-
Rpush.config.push_poll = 0.5
|
14
|
-
stub_tcp_connection(tcp_socket, ssl_socket, io_double)
|
15
|
-
end
|
16
|
-
|
17
|
-
def create_app
|
18
|
-
app = Rpush::Apns::App.new
|
19
|
-
app.certificate = TEST_CERT
|
20
|
-
app.name = 'test'
|
21
|
-
app.environment = 'sandbox'
|
22
|
-
app.save!
|
23
|
-
app
|
24
|
-
end
|
25
|
-
|
26
|
-
def create_notification
|
27
|
-
notification = Rpush::Apns::Notification.new
|
28
|
-
notification.app = app
|
29
|
-
notification.alert = 'test'
|
30
|
-
notification.device_token = 'a' * 108
|
31
|
-
notification.save!
|
32
|
-
notification
|
33
|
-
end
|
34
|
-
|
35
|
-
def wait
|
36
|
-
sleep 0.1
|
37
|
-
end
|
38
|
-
|
39
|
-
def wait_for_notification_to_deliver(notification)
|
40
|
-
timeout { wait until delivered_ids.include?(notification.id) }
|
41
|
-
end
|
42
|
-
|
43
|
-
def wait_for_notification_to_fail(notification)
|
44
|
-
timeout { wait until failed_ids.include?(notification.id) }
|
45
|
-
end
|
46
|
-
|
47
|
-
def wait_for_notification_to_retry(notification)
|
48
|
-
timeout { wait until retry_ids.include?(notification.id) }
|
49
|
-
end
|
50
|
-
|
51
|
-
def fail_notification(notification)
|
52
|
-
allow(ssl_socket).to receive_messages(read: [8, 4, notification.id].pack('ccN'))
|
53
|
-
enable_io_select
|
54
|
-
end
|
55
|
-
|
56
|
-
def enable_io_select
|
57
|
-
called = false
|
58
|
-
allow(io_double).to receive(:select) do
|
59
|
-
if called
|
60
|
-
nil
|
61
|
-
else
|
62
|
-
called = true
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
it 'delivers a notification successfully' do
|
68
|
-
notification = create_notification
|
69
|
-
expect do
|
70
|
-
Rpush.push
|
71
|
-
notification.reload
|
72
|
-
end.to change(notification, :delivered).to(true)
|
73
|
-
end
|
74
|
-
|
75
|
-
it 'receives feedback' do
|
76
|
-
app
|
77
|
-
tuple = "N\xE3\x84\r\x00 \x83OxfU\xEB\x9F\x84aJ\x05\xAD}\x00\xAF1\xE5\xCF\xE9:\xC3\xEA\a\x8F\x1D\xA4M*N\xB0\xCE\x17"
|
78
|
-
allow(ssl_socket).to receive(:read).and_return(tuple, nil)
|
79
|
-
Rpush.apns_feedback
|
80
|
-
feedback = Rpush::Apns::Feedback.all.first
|
81
|
-
expect(feedback).not_to be_nil
|
82
|
-
expect(feedback.app_id).to eq(app.id)
|
83
|
-
expect(feedback.device_token).to eq('834f786655eb9f84614a05ad7d00af31e5cfe93ac3ea078f1da44d2a4eb0ce17')
|
84
|
-
end
|
85
|
-
|
86
|
-
describe 'delivery failures' do
|
87
|
-
before do
|
88
|
-
Rpush.reflect do |on|
|
89
|
-
on.notification_delivered do |n|
|
90
|
-
delivered_ids << n.id
|
91
|
-
end
|
92
|
-
|
93
|
-
on.notification_id_failed do |_, n_id|
|
94
|
-
failed_ids << n_id
|
95
|
-
end
|
96
|
-
|
97
|
-
on.notification_id_will_retry do |_, n_id|
|
98
|
-
retry_ids << n_id
|
99
|
-
end
|
100
|
-
|
101
|
-
on.notification_will_retry do |n|
|
102
|
-
retry_ids << n.id
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
Rpush.embed
|
107
|
-
end
|
108
|
-
|
109
|
-
after do
|
110
|
-
Rpush.reflection_stack.clear
|
111
|
-
Rpush.reflection_stack.push(Rpush::ReflectionCollection.new)
|
112
|
-
|
113
|
-
timeout { Rpush.shutdown }
|
114
|
-
end
|
115
|
-
|
116
|
-
it 'fails to deliver a notification' do
|
117
|
-
notification = create_notification
|
118
|
-
wait_for_notification_to_deliver(notification)
|
119
|
-
fail_notification(notification)
|
120
|
-
wait_for_notification_to_fail(notification)
|
121
|
-
end
|
122
|
-
|
123
|
-
describe 'with a failed connection' do
|
124
|
-
it 'retries all notifications' do
|
125
|
-
allow_any_instance_of(Rpush::Daemon::TcpConnection).to receive_messages(sleep: nil)
|
126
|
-
expect(ssl_socket).to receive(:write).at_least(1).times.and_raise(Errno::EPIPE)
|
127
|
-
notifications = 2.times.map { create_notification }
|
128
|
-
notifications.each { |n| wait_for_notification_to_retry(n) }
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
describe 'with multiple notifications' do
|
133
|
-
let(:notification1) { create_notification }
|
134
|
-
let(:notification2) { create_notification }
|
135
|
-
let(:notification3) { create_notification }
|
136
|
-
let(:notification4) { create_notification }
|
137
|
-
let(:notifications) { [notification1, notification2, notification3, notification4] }
|
138
|
-
|
139
|
-
it 'marks the correct notification as failed' do
|
140
|
-
notifications.each { |n| wait_for_notification_to_deliver(n) }
|
141
|
-
fail_notification(notification2)
|
142
|
-
wait_for_notification_to_fail(notification2)
|
143
|
-
end
|
144
|
-
|
145
|
-
it 'does not mark prior notifications as failed' do
|
146
|
-
notifications.each { |n| wait_for_notification_to_deliver(n) }
|
147
|
-
fail_notification(notification2)
|
148
|
-
wait_for_notification_to_fail(notification2)
|
149
|
-
|
150
|
-
expect(failed_ids).to_not include(notification1.id)
|
151
|
-
notification1.reload
|
152
|
-
expect(notification1.delivered).to eq(true)
|
153
|
-
end
|
154
|
-
|
155
|
-
it 'marks notifications following the failed one as retryable' do
|
156
|
-
notifications.each { |n| wait_for_notification_to_deliver(n) }
|
157
|
-
fail_notification(notification2)
|
158
|
-
[notification3, notification4].each { |n| wait_for_notification_to_retry(n) }
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
require 'functional_spec_helper'
|
2
|
-
|
3
|
-
describe 'GCM priority' do
|
4
|
-
let(:app) { Rpush::Gcm::App.new }
|
5
|
-
let(:notification) { Rpush::Gcm::Notification.new }
|
6
|
-
let(:hydrated_notification) { Rpush::Gcm::Notification.find(notification.id) }
|
7
|
-
let(:response) { double(Net::HTTPResponse, code: 200) }
|
8
|
-
let(:http) { double(Net::HTTP::Persistent, request: response, shutdown: nil) }
|
9
|
-
let(:priority) { 'normal' }
|
10
|
-
|
11
|
-
before do
|
12
|
-
app.name = 'test'
|
13
|
-
app.auth_key = 'abc123'
|
14
|
-
app.save!
|
15
|
-
|
16
|
-
notification.app_id = app.id
|
17
|
-
notification.registration_ids = ['foo']
|
18
|
-
notification.data = { message: 'test' }
|
19
|
-
notification.priority = priority
|
20
|
-
notification.save!
|
21
|
-
|
22
|
-
allow(Net::HTTP::Persistent).to receive_messages(new: http)
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'supports normal priority' do
|
26
|
-
expect(hydrated_notification.as_json['priority']).to eq('normal')
|
27
|
-
end
|
28
|
-
|
29
|
-
context 'high priority' do
|
30
|
-
let(:priority) { 'high' }
|
31
|
-
|
32
|
-
it 'supports high priority' do
|
33
|
-
expect(hydrated_notification.as_json['priority']).to eq('high')
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
it 'does not add an error when receiving expected priority' do
|
38
|
-
expect(hydrated_notification.errors.messages[:priority]).to be_empty
|
39
|
-
end
|
40
|
-
end
|
data/spec/functional/gcm_spec.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
require 'functional_spec_helper'
|
2
|
-
|
3
|
-
describe 'GCM' do
|
4
|
-
let(:app) { Rpush::Gcm::App.new }
|
5
|
-
let(:notification) { Rpush::Gcm::Notification.new }
|
6
|
-
let(:response) { double(Net::HTTPResponse, code: 200) }
|
7
|
-
let(:http) { double(Net::HTTP::Persistent, request: response, shutdown: nil) }
|
8
|
-
|
9
|
-
before do
|
10
|
-
app.name = 'test'
|
11
|
-
app.auth_key = 'abc123'
|
12
|
-
app.save!
|
13
|
-
|
14
|
-
notification.app_id = app.id
|
15
|
-
notification.registration_ids = ['foo']
|
16
|
-
notification.data = { message: 'test' }
|
17
|
-
notification.save!
|
18
|
-
|
19
|
-
allow(Net::HTTP::Persistent).to receive_messages(new: http)
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'delivers a notification successfully' do
|
23
|
-
allow(response).to receive_messages(body: JSON.dump(results: [{ message_id: notification.registration_ids.first.to_s }]))
|
24
|
-
|
25
|
-
expect do
|
26
|
-
Rpush.push
|
27
|
-
notification.reload
|
28
|
-
end.to change(notification, :delivered).to(true)
|
29
|
-
end
|
30
|
-
|
31
|
-
it 'fails to deliver a notification successfully' do
|
32
|
-
allow(response).to receive_messages(body: JSON.dump(results: [{ error: 'Err' }]))
|
33
|
-
Rpush.push
|
34
|
-
notification.reload
|
35
|
-
expect(notification.delivered).to eq(false)
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'retries notification that fail due to a SocketError' do
|
39
|
-
expect(http).to receive(:request).and_raise(SocketError.new)
|
40
|
-
expect(notification.deliver_after).to be_nil
|
41
|
-
expect do
|
42
|
-
Rpush.push
|
43
|
-
notification.reload
|
44
|
-
end.to change(notification, :deliver_after).to(kind_of(Time))
|
45
|
-
end
|
46
|
-
end
|
@@ -1,44 +0,0 @@
|
|
1
|
-
require 'functional_spec_helper'
|
2
|
-
|
3
|
-
describe 'New app loading' do
|
4
|
-
let(:timeout) { 10 }
|
5
|
-
let(:app) { create_app }
|
6
|
-
let(:tcp_socket) { double(TCPSocket, setsockopt: nil, close: nil) }
|
7
|
-
let(:ssl_socket) { double(OpenSSL::SSL::SSLSocket, :sync= => nil, connect: nil, write: nil, flush: nil, read: nil, close: nil) }
|
8
|
-
let(:io_double) { double(select: nil) }
|
9
|
-
|
10
|
-
before do
|
11
|
-
stub_tcp_connection
|
12
|
-
end
|
13
|
-
|
14
|
-
def create_app
|
15
|
-
app = Rpush::Apns::App.new
|
16
|
-
app.certificate = TEST_CERT
|
17
|
-
app.name = 'test'
|
18
|
-
app.environment = 'sandbox'
|
19
|
-
app.save!
|
20
|
-
app
|
21
|
-
end
|
22
|
-
|
23
|
-
def create_notification
|
24
|
-
notification = Rpush::Apns::Notification.new
|
25
|
-
notification.app = app
|
26
|
-
notification.alert = 'test'
|
27
|
-
notification.device_token = 'a' * 108
|
28
|
-
notification.save!
|
29
|
-
notification
|
30
|
-
end
|
31
|
-
|
32
|
-
def stub_tcp_connection
|
33
|
-
allow_any_instance_of(Rpush::Daemon::TcpConnection).to receive_messages(connect_socket: [tcp_socket, ssl_socket])
|
34
|
-
allow_any_instance_of(Rpush::Daemon::TcpConnection).to receive_messages(setup_ssl_context: double.as_null_object)
|
35
|
-
stub_const('Rpush::Daemon::TcpConnection::IO', io_double)
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'delivers a notification successfully' do
|
39
|
-
notification = create_notification
|
40
|
-
Rpush.push
|
41
|
-
notification.reload
|
42
|
-
expect(notification.delivered).to eq(true)
|
43
|
-
end
|
44
|
-
end
|
@@ -1,39 +0,0 @@
|
|
1
|
-
require 'unit_spec_helper'
|
2
|
-
|
3
|
-
describe Rpush, 'apns_feedback' do
|
4
|
-
let!(:apns_app) { Rpush::Apns::App.create!(apns_app_params) }
|
5
|
-
let(:apns_app_params) do
|
6
|
-
{
|
7
|
-
name: 'test',
|
8
|
-
environment: 'production',
|
9
|
-
certificate: TEST_CERT
|
10
|
-
}
|
11
|
-
end
|
12
|
-
let!(:gcm_app) { Rpush::Gcm::App.create!(name: 'MyApp', auth_key: 'abc123') }
|
13
|
-
|
14
|
-
let(:receiver) { double(check_for_feedback: nil) }
|
15
|
-
|
16
|
-
before do
|
17
|
-
allow(Rpush::Daemon::Apns::FeedbackReceiver).to receive(:new) { receiver }
|
18
|
-
end
|
19
|
-
|
20
|
-
it 'initializes the daemon' do
|
21
|
-
expect(Rpush::Daemon).to receive(:common_init)
|
22
|
-
Rpush.apns_feedback
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'checks feedback for each app' do
|
26
|
-
expect(Rpush::Daemon::Apns::FeedbackReceiver).to receive(:new).with(apns_app).and_return(receiver)
|
27
|
-
expect(receiver).to receive(:check_for_feedback)
|
28
|
-
Rpush.apns_feedback
|
29
|
-
end
|
30
|
-
|
31
|
-
context 'feedback disabled' do
|
32
|
-
let(:apns_app_params) { super().merge(feedback_enabled: false) }
|
33
|
-
|
34
|
-
it 'does not initialize feedback receiver' do
|
35
|
-
expect(Rpush::Daemon::Apns::FeedbackReceiver).not_to receive(:new)
|
36
|
-
Rpush.apns_feedback
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
@@ -1,14 +0,0 @@
|
|
1
|
-
require 'unit_spec_helper'
|
2
|
-
|
3
|
-
describe Rpush::Client::ActiveRecord::Gcm::Notification do
|
4
|
-
it_behaves_like 'Rpush::Client::Gcm::Notification'
|
5
|
-
it_behaves_like 'Rpush::Client::ActiveRecord::Notification'
|
6
|
-
|
7
|
-
subject(:notification) { described_class.new }
|
8
|
-
let(:app) { Rpush::Gcm::App.create!(name: 'test', auth_key: 'abc') }
|
9
|
-
|
10
|
-
it 'accepts non-booleans as a truthy value' do
|
11
|
-
notification.dry_run = 'Not a boolean'
|
12
|
-
expect(notification.as_json['dry_run']).to eq true
|
13
|
-
end
|
14
|
-
end if active_record?
|
@@ -1,77 +0,0 @@
|
|
1
|
-
require 'unit_spec_helper'
|
2
|
-
|
3
|
-
shared_examples 'Rpush::Client::Gcm::Notification' do
|
4
|
-
let(:app) { Rpush::Gcm::App.create!(name: 'test', auth_key: 'abc') }
|
5
|
-
let(:notification) { described_class.new }
|
6
|
-
|
7
|
-
it "has a 'data' payload limit of 4096 bytes" do
|
8
|
-
notification.data = { key: "a" * 4096 }
|
9
|
-
expect(notification.valid?).to be_falsey
|
10
|
-
expect(notification.errors[:base]).to eq ["Notification payload data cannot be larger than 4096 bytes."]
|
11
|
-
end
|
12
|
-
|
13
|
-
it 'limits the number of registration ids to 1000' do
|
14
|
-
notification.registration_ids = ['a'] * (1000 + 1)
|
15
|
-
expect(notification.valid?).to be_falsey
|
16
|
-
expect(notification.errors[:base]).to eq ["Number of registration_ids cannot be larger than 1000."]
|
17
|
-
end
|
18
|
-
|
19
|
-
it 'validates expiry is present if collapse_key is set' do
|
20
|
-
notification.collapse_key = 'test'
|
21
|
-
notification.expiry = nil
|
22
|
-
expect(notification.valid?).to be_falsey
|
23
|
-
expect(notification.errors[:expiry]).to eq ['must be set when using a collapse_key']
|
24
|
-
end
|
25
|
-
|
26
|
-
it 'includes time_to_live in the payload' do
|
27
|
-
notification.expiry = 100
|
28
|
-
expect(notification.as_json['time_to_live']).to eq 100
|
29
|
-
end
|
30
|
-
|
31
|
-
it 'includes content_available in the payload' do
|
32
|
-
notification.content_available = true
|
33
|
-
expect(notification.as_json['content_available']).to eq true
|
34
|
-
end
|
35
|
-
|
36
|
-
it 'includes mutable_content in the payload' do
|
37
|
-
notification.mutable_content = true
|
38
|
-
expect(notification.as_json['mutable_content']).to eq true
|
39
|
-
end
|
40
|
-
|
41
|
-
it 'sets the priority to high when set to high' do
|
42
|
-
notification.priority = 'high'
|
43
|
-
expect(notification.as_json['priority']).to eq 'high'
|
44
|
-
end
|
45
|
-
|
46
|
-
it 'sets the priority to normal when set to normal' do
|
47
|
-
notification.priority = 'normal'
|
48
|
-
expect(notification.as_json['priority']).to eq 'normal'
|
49
|
-
end
|
50
|
-
|
51
|
-
it 'validates the priority is either "normal" or "high"' do
|
52
|
-
notification.priority = 'invalid'
|
53
|
-
expect(notification.errors[:priority]).to eq ['must be one of either "normal" or "high"']
|
54
|
-
end
|
55
|
-
|
56
|
-
it 'excludes the priority if it is not defined' do
|
57
|
-
expect(notification.as_json).not_to have_key 'priority'
|
58
|
-
end
|
59
|
-
|
60
|
-
it 'includes the notification payload if defined' do
|
61
|
-
notification.notification = { key: 'any key is allowed' }
|
62
|
-
expect(notification.as_json).to have_key 'notification'
|
63
|
-
end
|
64
|
-
|
65
|
-
it 'excludes the notification payload if undefined' do
|
66
|
-
expect(notification.as_json).not_to have_key 'notification'
|
67
|
-
end
|
68
|
-
|
69
|
-
it 'includes the dry_run payload if defined' do
|
70
|
-
notification.dry_run = true
|
71
|
-
expect(notification.as_json['dry_run']).to eq true
|
72
|
-
end
|
73
|
-
|
74
|
-
it 'excludes the dry_run payload if undefined' do
|
75
|
-
expect(notification.as_json).not_to have_key 'dry_run'
|
76
|
-
end
|
77
|
-
end
|