rpush 8.0.0 → 9.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|