rapns 2.0.5 → 3.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. data/lib/generators/rapns_generator.rb +1 -0
  2. data/lib/generators/templates/add_gcm.rb +86 -0
  3. data/lib/generators/templates/create_rapns_notifications.rb +1 -1
  4. data/lib/rapns/apns/app.rb +8 -0
  5. data/lib/rapns/apns/binary_notification_validator.rb +12 -0
  6. data/lib/rapns/apns/device_token_format_validator.rb +12 -0
  7. data/lib/rapns/apns/feedback.rb +14 -0
  8. data/lib/rapns/apns/notification.rb +84 -0
  9. data/lib/rapns/app.rb +5 -6
  10. data/lib/rapns/{config.rb → configuration.rb} +5 -5
  11. data/lib/rapns/daemon/apns/app_runner.rb +36 -0
  12. data/lib/rapns/daemon/apns/connection.rb +113 -0
  13. data/lib/rapns/daemon/apns/delivery.rb +63 -0
  14. data/lib/rapns/daemon/apns/delivery_handler.rb +21 -0
  15. data/lib/rapns/daemon/apns/disconnection_error.rb +20 -0
  16. data/lib/rapns/daemon/apns/feedback_receiver.rb +74 -0
  17. data/lib/rapns/daemon/app_runner.rb +76 -77
  18. data/lib/rapns/daemon/database_reconnectable.rb +3 -3
  19. data/lib/rapns/daemon/delivery.rb +43 -0
  20. data/lib/rapns/daemon/delivery_error.rb +6 -2
  21. data/lib/rapns/daemon/delivery_handler.rb +13 -79
  22. data/lib/rapns/daemon/delivery_queue_18.rb +2 -2
  23. data/lib/rapns/daemon/delivery_queue_19.rb +3 -3
  24. data/lib/rapns/daemon/feeder.rb +5 -5
  25. data/lib/rapns/daemon/gcm/app_runner.rb +13 -0
  26. data/lib/rapns/daemon/gcm/delivery.rb +206 -0
  27. data/lib/rapns/daemon/gcm/delivery_handler.rb +20 -0
  28. data/lib/rapns/daemon.rb +31 -20
  29. data/lib/rapns/gcm/app.rb +7 -0
  30. data/lib/rapns/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +11 -0
  31. data/lib/rapns/gcm/notification.rb +31 -0
  32. data/lib/rapns/gcm/payload_size_validator.rb +13 -0
  33. data/lib/rapns/multi_json_helper.rb +16 -0
  34. data/lib/rapns/notification.rb +28 -95
  35. data/lib/rapns/version.rb +1 -1
  36. data/lib/rapns.rb +14 -4
  37. data/lib/tasks/cane.rake +19 -0
  38. data/lib/tasks/test.rake +34 -0
  39. data/spec/acceptance/gcm_upgrade_spec.rb +34 -0
  40. data/spec/acceptance_spec_helper.rb +85 -0
  41. data/spec/support/simplecov_helper.rb +13 -0
  42. data/spec/support/simplecov_quality_formatter.rb +8 -0
  43. data/spec/unit/apns/app_spec.rb +15 -0
  44. data/spec/unit/apns/feedback_spec.rb +12 -0
  45. data/spec/{rapns → unit/apns}/notification_spec.rb +44 -72
  46. data/spec/unit/app_spec.rb +18 -0
  47. data/spec/unit/daemon/apns/app_runner_spec.rb +37 -0
  48. data/spec/{rapns/daemon → unit/daemon/apns}/connection_spec.rb +9 -9
  49. data/spec/unit/daemon/apns/delivery_handler_spec.rb +48 -0
  50. data/spec/unit/daemon/apns/delivery_spec.rb +154 -0
  51. data/spec/{rapns/daemon → unit/daemon/apns}/feedback_receiver_spec.rb +14 -14
  52. data/spec/unit/daemon/app_runner_shared.rb +66 -0
  53. data/spec/unit/daemon/app_runner_spec.rb +78 -0
  54. data/spec/{rapns → unit}/daemon/database_reconnectable_spec.rb +4 -5
  55. data/spec/{rapns → unit}/daemon/delivery_error_spec.rb +2 -2
  56. data/spec/unit/daemon/delivery_handler_shared.rb +19 -0
  57. data/spec/{rapns → unit}/daemon/delivery_queue_spec.rb +1 -1
  58. data/spec/{rapns → unit}/daemon/feeder_spec.rb +33 -33
  59. data/spec/unit/daemon/gcm/app_runner_spec.rb +15 -0
  60. data/spec/unit/daemon/gcm/delivery_handler_spec.rb +36 -0
  61. data/spec/unit/daemon/gcm/delivery_spec.rb +236 -0
  62. data/spec/{rapns → unit}/daemon/interruptible_sleep_spec.rb +1 -1
  63. data/spec/{rapns → unit}/daemon/logger_spec.rb +1 -1
  64. data/spec/{rapns → unit}/daemon_spec.rb +1 -1
  65. data/spec/unit/gcm/app_spec.rb +5 -0
  66. data/spec/unit/gcm/notification_spec.rb +55 -0
  67. data/spec/unit/notification_shared.rb +38 -0
  68. data/spec/unit/notification_spec.rb +6 -0
  69. data/spec/{rapns/app_spec.rb → unit_spec_helper.rb} +76 -16
  70. metadata +107 -45
  71. data/lib/rapns/binary_notification_validator.rb +0 -10
  72. data/lib/rapns/daemon/connection.rb +0 -114
  73. data/lib/rapns/daemon/delivery_handler_pool.rb +0 -18
  74. data/lib/rapns/daemon/disconnection_error.rb +0 -14
  75. data/lib/rapns/daemon/feedback_receiver.rb +0 -82
  76. data/lib/rapns/device_token_format_validator.rb +0 -10
  77. data/lib/rapns/feedback.rb +0 -12
  78. data/spec/rapns/daemon/app_runner_spec.rb +0 -193
  79. data/spec/rapns/daemon/delivery_handler_pool_spec.rb +0 -17
  80. data/spec/rapns/daemon/delivery_handler_spec.rb +0 -206
  81. data/spec/rapns/feedback_spec.rb +0 -12
  82. data/spec/spec_helper.rb +0 -78
@@ -1,10 +0,0 @@
1
- module Rapns
2
- class BinaryNotificationValidator < ActiveModel::Validator
3
-
4
- def validate(record)
5
- if record.payload_size > 256
6
- record.errors[:base] << "APN notification cannot be larger than 256 bytes. Try condensing your alert and device attributes."
7
- end
8
- end
9
- end
10
- end
@@ -1,114 +0,0 @@
1
- module Rapns
2
- module Daemon
3
- class ConnectionError < StandardError; end
4
-
5
- class Connection
6
- attr_accessor :last_write
7
-
8
- def self.idle_period
9
- 30.minutes
10
- end
11
-
12
- def initialize(name, host, port, certificate, password)
13
- @name = name
14
- @host = host
15
- @port = port
16
- @certificate = certificate
17
- @password = password
18
- written
19
- end
20
-
21
- def connect
22
- @ssl_context = setup_ssl_context
23
- @tcp_socket, @ssl_socket = connect_socket
24
- end
25
-
26
- def close
27
- begin
28
- @ssl_socket.close if @ssl_socket
29
- @tcp_socket.close if @tcp_socket
30
- rescue IOError
31
- end
32
- end
33
-
34
- def read(num_bytes)
35
- @ssl_socket.read(num_bytes)
36
- end
37
-
38
- def select(timeout)
39
- IO.select([@ssl_socket], nil, nil, timeout)
40
- end
41
-
42
- def write(data)
43
- reconnect_idle if idle_period_exceeded?
44
-
45
- retry_count = 0
46
-
47
- begin
48
- write_data(data)
49
- rescue Errno::EPIPE, Errno::ETIMEDOUT, OpenSSL::SSL::SSLError => e
50
- retry_count += 1;
51
-
52
- if retry_count == 1
53
- Rapns::Daemon.logger.error("[#{@name}] Lost connection to #{@host}:#{@port} (#{e.class.name}), reconnecting...")
54
- end
55
-
56
- if retry_count <= 3
57
- reconnect
58
- sleep 1
59
- retry
60
- else
61
- raise ConnectionError, "#{@name} tried #{retry_count-1} times to reconnect but failed (#{e.class.name})."
62
- end
63
- end
64
- end
65
-
66
- def reconnect
67
- close
68
- @tcp_socket, @ssl_socket = connect_socket
69
- end
70
-
71
- protected
72
-
73
- def reconnect_idle
74
- Rapns::Daemon.logger.info("[#{@name}] Idle period exceeded, reconnecting...")
75
- reconnect
76
- end
77
-
78
- def idle_period_exceeded?
79
- Time.now - last_write > self.class.idle_period
80
- end
81
-
82
- def write_data(data)
83
- @ssl_socket.write(data)
84
- @ssl_socket.flush
85
- written
86
- end
87
-
88
- def written
89
- self.last_write = Time.now
90
- end
91
-
92
- def setup_ssl_context
93
- ssl_context = OpenSSL::SSL::SSLContext.new
94
- ssl_context.key = OpenSSL::PKey::RSA.new(@certificate, @password)
95
- ssl_context.cert = OpenSSL::X509::Certificate.new(@certificate)
96
- ssl_context
97
- end
98
-
99
- def connect_socket
100
- tcp_socket = TCPSocket.new(@host, @port)
101
- tcp_socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1)
102
- tcp_socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
103
- ssl_socket = OpenSSL::SSL::SSLSocket.new(tcp_socket, @ssl_context)
104
- ssl_socket.sync = true
105
- ssl_socket.connect
106
- Rapns::Daemon.logger.info("[#{@name}] Connected to #{@host}:#{@port}")
107
- [tcp_socket, ssl_socket]
108
- rescue StandardError => e
109
- Rapns::Daemon.logger.error("[#{@name}] Error connecting to #{@host}:#{@port} : #{e}")
110
- raise
111
- end
112
- end
113
- end
114
- end
@@ -1,18 +0,0 @@
1
- module Rapns
2
- module Daemon
3
- class DeliveryHandlerPool
4
- def initialize
5
- @handlers = []
6
- end
7
-
8
- def <<(handler)
9
- @handlers << handler
10
- handler.start
11
- end
12
-
13
- def drain
14
- @handlers.pop.stop while !@handlers.empty?
15
- end
16
- end
17
- end
18
- end
@@ -1,14 +0,0 @@
1
- module Rapns
2
- class DisconnectionError < StandardError
3
- attr_reader :code, :description
4
-
5
- def initialize
6
- @code = nil
7
- @description = "APNs disconnected without returning an error."
8
- end
9
-
10
- def message
11
- "The APNs disconnected without returning an error. This may indicate you are using an invalid certificate for the host."
12
- end
13
- end
14
- end
@@ -1,82 +0,0 @@
1
- module Rapns
2
- module Daemon
3
- class FeedbackReceiver
4
- include InterruptibleSleep
5
- include DatabaseReconnectable
6
-
7
- FEEDBACK_TUPLE_BYTES = 38
8
-
9
- def initialize(name, host, port, poll, certificate, password)
10
- @name = name
11
- @host = host
12
- @port = port
13
- @poll = poll
14
- @certificate = certificate
15
- @password = password
16
- end
17
-
18
- def start
19
- @thread = Thread.new do
20
- loop do
21
- begin
22
- break if @stop
23
- check_for_feedback
24
- rescue OpenSSL::SSL::SSLError
25
- # stop the thread if there is an SSL error. Other errors might be recoverable,
26
- # and retrying later might make sense (for example, a network outage)
27
- @stop = true
28
- break
29
- rescue
30
- # error will be logged in check_for_feedback
31
- end
32
- interruptible_sleep @poll
33
- end
34
- end
35
- end
36
-
37
- def stop
38
- @stop = true
39
- interrupt_sleep
40
- @thread.join if @thread
41
- end
42
-
43
- def check_for_feedback
44
- connection = nil
45
- begin
46
- connection = Connection.new("FeedbackReceiver:#{@name}", @host, @port, @certificate, @password)
47
- connection.connect
48
-
49
- while tuple = connection.read(FEEDBACK_TUPLE_BYTES)
50
- timestamp, device_token = parse_tuple(tuple)
51
- create_feedback(timestamp, device_token)
52
- end
53
- rescue StandardError => e
54
- Rapns::Daemon.logger.error(e)
55
- raise
56
- ensure
57
- connection.close if connection
58
- end
59
- end
60
-
61
- protected
62
-
63
- def parse_tuple(tuple)
64
- failed_at, _, device_token = tuple.unpack("N1n1H*")
65
- [Time.at(failed_at).utc, device_token]
66
- end
67
-
68
- def create_feedback(failed_at, device_token)
69
- formatted_failed_at = failed_at.strftime("%Y-%m-%d %H:%M:%S UTC")
70
- with_database_reconnect_and_retry do
71
- Rapns::Daemon.logger.info("[FeedbackReceiver:#{@name}] Delivery failed at #{formatted_failed_at} for #{device_token}")
72
- feedback = Rapns::Feedback.create!(:failed_at => failed_at, :device_token => device_token, :app => @name)
73
- begin
74
- Rapns.configuration.feedback_callback.call(feedback) if Rapns.configuration.feedback_callback
75
- rescue Exception => e
76
- Rapns::Daemon.logger.error(e)
77
- end
78
- end
79
- end
80
- end
81
- end
82
- end
@@ -1,10 +0,0 @@
1
- module Rapns
2
- class DeviceTokenFormatValidator < ActiveModel::Validator
3
-
4
- def validate(record)
5
- if record.device_token !~ /^[a-z0-9]{64}$/
6
- record.errors[:device_token] << "is invalid"
7
- end
8
- end
9
- end
10
- end
@@ -1,12 +0,0 @@
1
- module Rapns
2
- class Feedback < ActiveRecord::Base
3
- self.table_name = 'rapns_feedback'
4
-
5
- attr_accessible :device_token, :failed_at, :app
6
-
7
- validates :device_token, :presence => true
8
- validates :failed_at, :presence => true
9
-
10
- validates_with Rapns::DeviceTokenFormatValidator
11
- end
12
- end
@@ -1,193 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Rapns::Daemon::AppRunner do
4
- let(:app) { stub(:key => 'app', :certificate => 'cert', :password => '', :connections => 1) }
5
- let(:queue) { stub(:notifications_processed? => true, :push => nil) }
6
- let(:receiver) { stub(:start => nil, :stop => nil) }
7
- let(:handler) { stub(:start => nil, :stop => nil) }
8
- let(:push_config) { stub(:host => 'gateway.push.apple.com', :port => 2195) }
9
- let(:feedback_config) { stub(:host => 'feedback.push.apple.com', :port => 2196, :poll => 60) }
10
- let(:runner) { Rapns::Daemon::AppRunner.new(app, push_config.host, push_config.port,
11
- feedback_config.host, feedback_config.port, feedback_config.poll) }
12
-
13
- before do
14
- Rapns::Daemon::DeliveryQueue.stub(:new => queue)
15
- Rapns::Daemon::FeedbackReceiver.stub(:new => receiver)
16
- Rapns::Daemon::DeliveryHandler.stub(:new => handler)
17
- end
18
-
19
- after { Rapns::Daemon::AppRunner.all.clear }
20
-
21
- describe 'start' do
22
- it 'starts a feedback receiver' do
23
- Rapns::Daemon::FeedbackReceiver.should_receive(:new).with(app.key, feedback_config.host, feedback_config.port, feedback_config.poll, app.certificate, app.password)
24
- receiver.should_receive(:start)
25
- runner.start
26
- end
27
-
28
- it 'starts a delivery handler for each connection' do
29
- Rapns::Daemon::DeliveryHandler.should_receive(:new).with(queue, app.key, push_config.host,
30
- push_config.port, app.certificate, app.password)
31
- handler.should_receive(:start)
32
- runner.start
33
- end
34
- end
35
-
36
- describe 'deliver' do
37
- let(:notification) { stub }
38
-
39
- it 'enqueues the notification' do
40
- queue.should_receive(:push).with(notification)
41
- runner.deliver(notification)
42
- end
43
- end
44
-
45
- describe 'stop' do
46
- before { runner.start }
47
-
48
- it 'stops the delivery handlers' do
49
- handler.should_receive(:stop)
50
- runner.stop
51
- end
52
-
53
- it 'stops the feedback receiver' do
54
- receiver.should_receive(:stop)
55
- runner.stop
56
- end
57
- end
58
-
59
- describe 'ready?' do
60
- it 'is ready if all notifications have been processed' do
61
- queue.stub(:notifications_processed? => true)
62
- runner.ready?.should be_true
63
- end
64
-
65
- it 'is not ready if not all notifications have been processed' do
66
- queue.stub(:notifications_processed? => false)
67
- runner.ready?.should be_false
68
- end
69
- end
70
-
71
- describe 'sync' do
72
- let(:new_app) { stub(:key => 'app', :certificate => 'cert', :password => '', :connections => 1) }
73
- before { runner.start }
74
-
75
- it 'reduces the number of handlers if needed' do
76
- handler.should_receive(:stop)
77
- new_app.stub(:connections => app.connections - 1)
78
- runner.sync(new_app)
79
- end
80
-
81
- it 'increases the number of handlers if needed' do
82
- new_handler = stub
83
- Rapns::Daemon::DeliveryHandler.should_receive(:new).and_return(new_handler)
84
- new_handler.should_receive(:start)
85
- new_app.stub(:connections => app.connections + 1)
86
- runner.sync(new_app)
87
- end
88
- end
89
- end
90
-
91
- describe Rapns::Daemon::AppRunner, 'stop' do
92
- let(:runner) { stub }
93
- before { Rapns::Daemon::AppRunner.all['app'] = runner }
94
- after { Rapns::Daemon::AppRunner.all.clear }
95
-
96
- it 'stops all runners' do
97
- runner.should_receive(:stop)
98
- Rapns::Daemon::AppRunner.stop
99
- end
100
- end
101
-
102
- describe Rapns::Daemon::AppRunner, 'deliver' do
103
- let(:runner) { stub }
104
- let(:notification) { stub(:app => 'app') }
105
- let(:logger) { stub(:error => nil) }
106
-
107
- before do
108
- Rapns::Daemon.stub(:logger => logger)
109
- Rapns::Daemon::AppRunner.all['app'] = runner
110
- end
111
-
112
- after { Rapns::Daemon::AppRunner.all.clear }
113
-
114
- it 'delivers the notification' do
115
- runner.should_receive(:deliver).with(notification)
116
- Rapns::Daemon::AppRunner.deliver(notification)
117
- end
118
-
119
- it 'logs an error if there is no runner to deliver the notification' do
120
- notification.stub(:app => 'unknonw', :id => 123)
121
- logger.should_receive(:error).with("No such app '#{notification.app}' for notification #{notification.id}.")
122
- Rapns::Daemon::AppRunner.deliver(notification)
123
- end
124
- end
125
-
126
- describe Rapns::Daemon::AppRunner, 'ready' do
127
- let(:runner1) { stub(:ready? => true) }
128
- let(:runner2) { stub(:ready? => false) }
129
-
130
- before do
131
- Rapns::Daemon::AppRunner.all['app1'] = runner1
132
- Rapns::Daemon::AppRunner.all['app2'] = runner2
133
- end
134
-
135
- after { Rapns::Daemon::AppRunner.all.clear }
136
-
137
- it 'returns apps that are ready for more notifications' do
138
- Rapns::Daemon::AppRunner.ready.should == ['app1']
139
- end
140
- end
141
-
142
- describe Rapns::Daemon::AppRunner, 'sync' do
143
- let(:app) { stub(:key => 'app', :environment => 'development') }
144
- let(:new_app) { stub(:key => 'new_app', :environment => 'development') }
145
- let(:runner) { stub(:sync => nil, :stop => nil) }
146
- let(:new_runner) { stub }
147
- let(:logger) { stub(:error => nil) }
148
- let(:config) { stub(:feedback_poll => 60) }
149
-
150
- before do
151
- Rapns::Daemon.stub(:config => config, :logger => logger)
152
- Rapns::Daemon::AppRunner.all['app'] = runner
153
- Rapns::App.stub(:all => [app])
154
- end
155
-
156
- after { Rapns::Daemon::AppRunner.all.clear }
157
-
158
- it 'loads all apps' do
159
- Rapns::App.should_receive(:all)
160
- Rapns::Daemon::AppRunner.sync
161
- end
162
-
163
- it 'instructs existing runners to sync' do
164
- runner.should_receive(:sync).with(app)
165
- Rapns::Daemon::AppRunner.sync
166
- end
167
-
168
- it 'starts a runner for a new app with a production certificate' do
169
- new_app.stub(:environment => 'production')
170
- Rapns::App.stub(:all => [new_app])
171
- new_runner = stub
172
- Rapns::Daemon::AppRunner.should_receive(:new).with(new_app, 'gateway.push.apple.com', 2195,
173
- 'feedback.push.apple.com', 2196, config.feedback_poll).and_return(new_runner)
174
- new_runner.should_receive(:start)
175
- Rapns::Daemon::AppRunner.sync
176
- end
177
-
178
- it 'starts a runner for a new app with a development certificate' do
179
- new_app.stub(:environment => 'development')
180
- Rapns::App.stub(:all => [new_app])
181
- new_runner = stub
182
- Rapns::Daemon::AppRunner.should_receive(:new).with(new_app, 'gateway.sandbox.push.apple.com', 2195,
183
- 'feedback.sandbox.push.apple.com', 2196, config.feedback_poll).and_return(new_runner)
184
- new_runner.should_receive(:start)
185
- Rapns::Daemon::AppRunner.sync
186
- end
187
-
188
- it 'deletes old apps' do
189
- Rapns::App.stub(:all => [])
190
- runner.should_receive(:stop)
191
- Rapns::Daemon::AppRunner.sync
192
- end
193
- end
@@ -1,17 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe Rapns::Daemon::DeliveryHandlerPool do
4
- let(:pool) { Rapns::Daemon::DeliveryHandlerPool.new }
5
- let(:handler) { stub(:start => nil, :stop => nil) }
6
-
7
- it 'starts the handler when added to the pool' do
8
- handler.should_receive(:start)
9
- pool << handler
10
- end
11
-
12
- it 'stops each handler when drained' do
13
- pool << handler
14
- handler.should_receive(:stop)
15
- pool.drain
16
- end
17
- end
@@ -1,206 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe Rapns::Daemon::DeliveryHandler do
4
- let(:queue) { Rapns::Daemon::DeliveryQueue.new }
5
- let(:name) { 'my_app:0' }
6
- let(:host) { 'localhost' }
7
- let(:port) { 2195 }
8
- let(:certificate) { stub }
9
- let(:password) { stub }
10
- let(:delivery_handler) { Rapns::Daemon::DeliveryHandler.new(queue, name, host, port, certificate, password) }
11
- let(:connection) { stub(:select => false, :write => nil, :reconnect => nil, :close => nil, :connect => nil) }
12
- let(:logger) { stub(:error => nil, :info => nil) }
13
- let(:notification) { stub.as_null_object }
14
- let(:config) { stub(:check_for_errors => true) }
15
- let(:delivery_queues) { [] }
16
-
17
- before do
18
- Rapns::Daemon::Connection.stub(:new => connection)
19
- Rapns::Daemon.stub(:delivery_queues => delivery_queues, :logger => logger, :config => config)
20
- queue.push(notification)
21
- end
22
-
23
- it "instantiates a new connection" do
24
- Rapns::Daemon::Connection.should_receive(:new).with("DeliveryHandler:#{name}", host, port, certificate, password)
25
- delivery_handler
26
- end
27
-
28
- it "connects the socket when started" do
29
- connection.should_receive(:connect)
30
- delivery_handler.start
31
- delivery_handler.stop
32
- end
33
-
34
- it "instructs the queue to wakeup the thread when told to stop" do
35
- thread = stub
36
- Thread.stub(:new => thread)
37
- queue.should_receive(:wakeup).with(thread)
38
- delivery_handler.start
39
- delivery_handler.stop
40
- end
41
-
42
- it "sends the binary version of the notification" do
43
- notification.stub(:to_binary => "hi mom")
44
- connection.should_receive(:write).with("hi mom")
45
- delivery_handler.send(:handle_next_notification)
46
- end
47
-
48
- it "logs the notification delivery" do
49
- notification.stub(:id => 666, :device_token => 'abc123')
50
- logger.should_receive(:info).with("[DeliveryHandler:my_app:0] 666 sent to abc123")
51
- delivery_handler.send(:handle_next_notification)
52
- end
53
-
54
- it "marks the notification as delivered" do
55
- notification.should_receive(:delivered=).with(true)
56
- delivery_handler.send(:handle_next_notification)
57
- end
58
-
59
- it "sets the time the notification was delivered" do
60
- now = Time.now
61
- Time.stub(:now).and_return(now)
62
- notification.should_receive(:delivered_at=).with(now)
63
- delivery_handler.send(:handle_next_notification)
64
- end
65
-
66
- it "does not trigger validations when saving the notification" do
67
- notification.should_receive(:save!).with(:validate => false)
68
- delivery_handler.send(:handle_next_notification)
69
- end
70
-
71
- it "updates notification with the ability to reconnect the database" do
72
- delivery_handler.should_receive(:with_database_reconnect_and_retry)
73
- delivery_handler.send(:handle_next_notification)
74
- end
75
-
76
- it "logs if an error is raised when updating the notification" do
77
- e = StandardError.new("bork!")
78
- notification.stub(:save!).and_raise(e)
79
- Rapns::Daemon.logger.should_receive(:error).with(e)
80
- delivery_handler.send(:handle_next_notification)
81
- end
82
-
83
- it "notifies the delivery queue the notification has been processed" do
84
- queue.should_receive(:notification_processed)
85
- delivery_handler.send(:handle_next_notification)
86
- end
87
-
88
- it 'does not check for errors if check_for_errors config option is false' do
89
- config.stub(:check_for_errors => false)
90
- delivery_handler.should_not_receive(:check_for_error)
91
- delivery_handler.send(:handle_next_notification)
92
- end
93
-
94
- describe "when being stopped" do
95
- before { queue.pop }
96
-
97
- it "closes the connection when a DeliveryQueue::WakeupError is raised" do
98
- connection.should_receive(:close)
99
- queue.stub(:pop).and_raise(Rapns::Daemon::DeliveryQueue::WakeupError)
100
- delivery_handler.send(:handle_next_notification)
101
- end
102
-
103
- it "does not attempt to deliver a notification when a DeliveryQueue::::WakeupError is raised" do
104
- queue.stub(:pop).and_raise(Rapns::Daemon::DeliveryQueue::WakeupError)
105
- delivery_handler.should_not_receive(:deliver)
106
- delivery_handler.send(:handle_next_notification)
107
- end
108
- end
109
-
110
- describe "when delivery fails" do
111
- before { connection.stub(:select => true, :read => [8, 4, 69].pack("ccN")) }
112
-
113
- it "updates notification with the ability to reconnect the database" do
114
- delivery_handler.should_receive(:with_database_reconnect_and_retry)
115
- delivery_handler.send(:handle_next_notification)
116
- end
117
-
118
- it "sets the notification as not delivered" do
119
- notification.should_receive(:delivered=).with(false)
120
- delivery_handler.send(:handle_next_notification)
121
- end
122
-
123
- it "sets the notification delivered_at timestamp to nil" do
124
- notification.should_receive(:delivered_at=).with(nil)
125
- delivery_handler.send(:handle_next_notification)
126
- end
127
-
128
- it "sets the notification as failed" do
129
- notification.should_receive(:failed=).with(true)
130
- delivery_handler.send(:handle_next_notification)
131
- end
132
-
133
- it "sets the notification failed_at timestamp" do
134
- now = Time.now
135
- Time.stub(:now).and_return(now)
136
- notification.should_receive(:failed_at=).with(now)
137
- delivery_handler.send(:handle_next_notification)
138
- end
139
-
140
- it "sets the notification error code" do
141
- notification.should_receive(:error_code=).with(4)
142
- delivery_handler.send(:handle_next_notification)
143
- end
144
-
145
- it "logs the delivery error" do
146
- error = Rapns::DeliveryError.new(4, 12, "Missing payload")
147
- Rapns::DeliveryError.stub(:new => error)
148
- logger.should_receive(:error).with(error)
149
- delivery_handler.send(:handle_next_notification)
150
- end
151
-
152
- it "sets the notification error description" do
153
- notification.should_receive(:error_description=).with("Missing payload")
154
- delivery_handler.send(:handle_next_notification)
155
- end
156
-
157
- it "skips validation when saving the notification" do
158
- notification.should_receive(:save!).with(:validate => false)
159
- delivery_handler.send(:handle_next_notification)
160
- end
161
-
162
- it "reads 6 bytes from the socket" do
163
- connection.should_receive(:read).with(6).and_return(nil)
164
- delivery_handler.send(:handle_next_notification)
165
- end
166
-
167
- it "does not attempt to read from the socket if the socket was not selected for reading after the timeout" do
168
- connection.stub(:select => nil)
169
- connection.should_not_receive(:read)
170
- delivery_handler.send(:handle_next_notification)
171
- end
172
-
173
- it "reconnects the socket" do
174
- connection.should_receive(:reconnect)
175
- delivery_handler.send(:handle_next_notification)
176
- end
177
-
178
- it "logs that the connection is being reconnected" do
179
- Rapns::Daemon.logger.should_receive(:error).with("[DeliveryHandler:my_app:0] Error received, reconnecting...")
180
- delivery_handler.send(:handle_next_notification)
181
- end
182
-
183
- context "when the APNs disconnects without returning an error" do
184
- before do
185
- connection.stub(:read => nil)
186
- end
187
-
188
- it 'raises a DisconnectError error if the connection is closed without an error being returned' do
189
- error = Rapns::DisconnectionError.new
190
- Rapns::DisconnectionError.should_receive(:new).and_return(error)
191
- Rapns::Daemon.logger.should_receive(:error).with(error)
192
- delivery_handler.send(:handle_next_notification)
193
- end
194
-
195
- it 'does not set the error code on the notification' do
196
- notification.should_receive(:error_code=).with(nil)
197
- delivery_handler.send(:handle_next_notification)
198
- end
199
-
200
- it 'sets the error descriptipon on the notification' do
201
- notification.should_receive(:error_description=).with("APNs disconnected without returning an error.")
202
- delivery_handler.send(:handle_next_notification)
203
- end
204
- end
205
- end
206
- end
@@ -1,12 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe Rapns::Feedback do
4
- it { should validate_presence_of(:device_token) }
5
- it { should validate_presence_of(:failed_at) }
6
-
7
- it "should validate the format of the device_token" do
8
- feedback = Rapns::Feedback.new(:device_token => "{$%^&*()}")
9
- feedback.valid?.should be_false
10
- feedback.errors[:device_token].include?("is invalid").should be_true
11
- end
12
- end