rpush 2.0.0.beta1 → 2.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rpush/daemon.rb +3 -3
  3. data/lib/rpush/daemon/adm/delivery.rb +6 -6
  4. data/lib/rpush/daemon/app_runner.rb +52 -66
  5. data/lib/rpush/daemon/dispatcher_loop.rb +27 -22
  6. data/lib/rpush/daemon/feeder.rb +1 -1
  7. data/lib/rpush/daemon/proc_title.rb +2 -2
  8. data/lib/rpush/daemon/retryable_error.rb +2 -0
  9. data/lib/rpush/daemon/signal_handler.rb +1 -1
  10. data/lib/rpush/daemon/store/active_record/reconnectable.rb +6 -2
  11. data/lib/rpush/daemon/string_helpers.rb +15 -0
  12. data/lib/rpush/daemon/synchronizer.rb +44 -0
  13. data/lib/rpush/embed.rb +1 -1
  14. data/lib/rpush/push.rb +1 -1
  15. data/lib/rpush/version.rb +1 -1
  16. data/spec/functional/synchronization_spec.rb +46 -0
  17. data/spec/spec_helper.rb +2 -2
  18. data/spec/unit/client/active_record/apns/notification_spec.rb +2 -2
  19. data/spec/unit/daemon/adm/delivery_spec.rb +2 -2
  20. data/spec/unit/daemon/app_runner_spec.rb +32 -98
  21. data/spec/unit/daemon/dispatcher_loop_spec.rb +0 -13
  22. data/spec/unit/daemon/feeder_spec.rb +2 -2
  23. data/spec/unit/daemon/signal_handler_spec.rb +3 -3
  24. data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +1 -0
  25. data/spec/unit/daemon_spec.rb +3 -2
  26. data/spec/unit/embed_spec.rb +2 -2
  27. data/spec/unit/logger_spec.rb +7 -5
  28. data/spec/unit/push_spec.rb +4 -3
  29. metadata +6 -8
  30. data/lib/rpush/daemon/dispatcher_loop_collection.rb +0 -33
  31. data/lib/rpush/daemon/too_many_requests_error.rb +0 -20
  32. data/spec/unit/daemon/dispatcher_loop_collection_spec.rb +0 -37
  33. data/spec/unit/daemon/too_many_requests_error_spec.rb +0 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8d5bf4a64ca72a0c58b62a78da3e4315d06601cc
4
- data.tar.gz: 2472ea351a2cc76e46668951eeabcfaf43b94b1f
3
+ metadata.gz: 09d2df386688b4c0ac01e8aab5e4c725257a3263
4
+ data.tar.gz: a7233c67417785808daf0e8d0f637d17b31a33b9
5
5
  SHA512:
6
- metadata.gz: 1e34c0d97f7a604e0cc47b9c2e3548055207d87cd7a09c0800632c123cc4d7f4f62c7dbc7d014fa302c797ae822e2df2ae788d50b9670c24b003dbe00e7af767
7
- data.tar.gz: 39e8509bb7fcfc76cb0347d55b4eee1a74f2083248ea3c71ccfbfff5e08a4ca8a3cfe6d8f6d1b7f788955f8af8fea016c469e741a5306c0c547fef5afd031029
6
+ metadata.gz: 4e19a18011c6e4e06b84deb49b82172ebb580a71ba8aef64e7ba6dce9edfc8aa42273bc131643a1f174aa9a30429b9797617e0661f7005eb657f9d9f1daf0b76
7
+ data.tar.gz: 47c703cdd66bb3fd80327d991604d0ee573db02b4324023484a90ac5ce7793cd022906ea3b1f424c97390edf7e59e13649c41efc9f2de9cadf68859fbc937f01
@@ -8,18 +8,18 @@ require 'rpush/daemon/errors'
8
8
  require 'rpush/daemon/constants'
9
9
  require 'rpush/daemon/reflectable'
10
10
  require 'rpush/daemon/loggable'
11
+ require 'rpush/daemon/string_helpers'
11
12
  require 'rpush/daemon/interruptible_sleep'
12
13
  require 'rpush/daemon/delivery_error'
13
14
  require 'rpush/daemon/retryable_error'
14
- require 'rpush/daemon/too_many_requests_error'
15
15
  require 'rpush/daemon/delivery'
16
16
  require 'rpush/daemon/feeder'
17
17
  require 'rpush/daemon/batch'
18
18
  require 'rpush/daemon/queue_payload'
19
+ require 'rpush/daemon/synchronizer'
19
20
  require 'rpush/daemon/app_runner'
20
21
  require 'rpush/daemon/tcp_connection'
21
22
  require 'rpush/daemon/dispatcher_loop'
22
- require 'rpush/daemon/dispatcher_loop_collection'
23
23
  require 'rpush/daemon/dispatcher/http'
24
24
  require 'rpush/daemon/dispatcher/tcp'
25
25
  require 'rpush/daemon/dispatcher/apns_tcp'
@@ -55,7 +55,7 @@ module Rpush
55
55
  Process.daemon if daemonize?
56
56
  initialize_store
57
57
  write_pid_file
58
- AppRunner.sync
58
+ Synchronizer.sync
59
59
 
60
60
  # No further store connections will be made from this thread.
61
61
  store.release_connection
@@ -36,10 +36,10 @@ module Rpush
36
36
  end
37
37
  mark_delivered
38
38
  end
39
+ rescue Rpush::RateLimitError => error
40
+ handle_rate_limited(error)
39
41
  rescue Rpush::RetryableError => error
40
42
  handle_retryable(error)
41
- rescue Rpush::TooManyRequestsError => error
42
- handle_too_many_requests(error)
43
43
  rescue StandardError => error
44
44
  mark_failed(error)
45
45
  raise
@@ -58,7 +58,7 @@ module Rpush
58
58
  when 401
59
59
  unauthorized(response)
60
60
  when 429
61
- too_many_requests(response)
61
+ rate_limited(response)
62
62
  when 500
63
63
  internal_server_error(current_registration_id)
64
64
  when 503
@@ -94,7 +94,7 @@ module Rpush
94
94
  end
95
95
  end
96
96
 
97
- def handle_too_many_requests(error)
97
+ def handle_rate_limited(error)
98
98
  if @sent_registration_ids.empty?
99
99
  # none sent yet, just resend after the specified retry-after response.header
100
100
  retry_delivery(@notification, error.response)
@@ -129,9 +129,9 @@ module Rpush
129
129
  fail Rpush::RetryableError.new(response.code.to_i, @notification.id, 'ADM responded with an Unauthorized Error.', response)
130
130
  end
131
131
 
132
- def too_many_requests(response)
132
+ def rate_limited(response)
133
133
  # raise error so the current notification stops sending messages to remaining reg ids
134
- fail Rpush::TooManyRequestsError.new(response.code.to_i, @notification.id, 'Exceeded maximum allowable rate of messages.', response)
134
+ fail Rpush::RateLimitError.new(response.code.to_i, @notification.id, 'Exceeded maximum allowable rate of messages.', response)
135
135
  end
136
136
 
137
137
  def internal_server_error(current_registration_id)
@@ -4,64 +4,73 @@ module Rpush
4
4
  extend Reflectable
5
5
  include Reflectable
6
6
  include Loggable
7
-
8
- class << self
9
- attr_reader :runners
10
- end
7
+ include StringHelpers
11
8
 
12
9
  @runners = {}
13
10
 
14
11
  def self.enqueue(notifications)
15
12
  notifications.group_by(&:app_id).each do |app_id, group|
16
- sync_app_with_id(app_id) unless runners[app_id]
17
- runners[app_id].enqueue(group) if runners[app_id]
13
+ start_app_with_id(app_id) unless @runners[app_id]
14
+ @runners[app_id].enqueue(group) if @runners[app_id]
18
15
  end
16
+
19
17
  ProcTitle.update
20
18
  end
21
19
 
22
- def self.sync
23
- apps = Rpush::Daemon.store.all_apps
24
- apps.each { |app| sync_app(app) }
25
- removed = runners.keys - apps.map(&:id)
26
- removed.each { |app_id| runners.delete(app_id).stop }
27
- ProcTitle.update
20
+ def self.start_app_with_id(app_id)
21
+ start_app(Rpush::Daemon.store.app(app_id))
28
22
  end
29
23
 
30
- def self.sync_app(app)
31
- if runners[app.id]
32
- runners[app.id].sync(app)
33
- else
34
- runner = new(app)
35
- begin
36
- runners[app.id] = runner
37
- runner.start
38
- rescue StandardError => e
39
- Rpush.logger.error("[#{app.name}] Exception raised during startup. Notifications will not be delivered for this app.")
40
- Rpush.logger.error(e)
41
- reflect(:error, e)
42
- end
43
- end
24
+ def self.start_app(app)
25
+ @runners[app.id] = new(app)
26
+ @runners[app.id].start
27
+ rescue StandardError => e
28
+ @runners.delete(app.id)
29
+ Rpush.logger.error("[#{app.name}] Exception raised during startup. Notifications will not be delivered for this app.")
30
+ Rpush.logger.error(e)
31
+ reflect(:error, e)
44
32
  end
45
33
 
46
- def self.sync_app_with_id(app_id)
47
- sync_app(Rpush::Daemon.store.app(app_id))
34
+ def self.stop_app(app_id)
35
+ @runners.delete(app_id).stop
36
+ end
37
+
38
+ def self.app_running?(app)
39
+ @runners.key?(app.id)
40
+ end
41
+
42
+ def self.app_ids
43
+ @runners.keys
48
44
  end
49
45
 
50
46
  def self.stop
51
- runners.values.map(&:stop)
52
- runners.clear
47
+ @runners.values.map(&:stop)
48
+ @runners.clear
49
+ end
50
+
51
+ def self.total_dispatchers
52
+ @runners.values.sum(&:num_dispatcher_loops)
53
53
  end
54
54
 
55
- def self.num_dispatchers
56
- runners.values.sum(&:num_dispatcher_loops)
55
+ def self.total_queued
56
+ @runners.values.sum(&:queue_size)
57
57
  end
58
58
 
59
- def self.num_queued
60
- runners.values.sum(&:queue_size)
59
+ def self.num_dispatchers_for_app(app)
60
+ runner = @runners[app.id]
61
+ runner ? runner.num_dispatcher_loops : 0
62
+ end
63
+
64
+ def self.decrement_dispatchers(app, num)
65
+ @runners[app.id].decrement_dispatchers(num)
66
+ end
67
+
68
+ def self.increment_dispatchers(app, num)
69
+ @runners[app.id].increment_dispatchers(num)
61
70
  end
62
71
 
63
72
  def self.debug
64
- runners.values.map(&:debug)
73
+ @runners.values.map(&:debug)
65
74
  end
66
75
 
67
76
  attr_reader :app
@@ -69,12 +78,12 @@ module Rpush
69
78
  def initialize(app)
70
79
  @app = app
71
80
  @loops = []
81
+ @dispatcher_loops = []
72
82
  end
73
83
 
74
84
  def start
75
- app.connections.times { dispatcher_loops.push(new_dispatcher_loop) }
85
+ app.connections.times { @dispatcher_loops.push(new_dispatcher_loop) }
76
86
  start_loops
77
- log_info("Started, #{dispatchers_str}.")
78
87
  end
79
88
 
80
89
  def stop
@@ -103,31 +112,18 @@ module Rpush
103
112
  end
104
113
  end
105
114
 
106
- def sync(app)
107
- @app = app
108
- diff = dispatcher_loops.size - app.connections
109
- return if diff == 0
110
- if diff > 0
111
- decrement_dispatchers(diff)
112
- log_info("Stopped #{dispatchers_str(diff)}. #{dispatchers_str} running.")
113
- else
114
- increment_dispatchers(diff.abs)
115
- log_info("Started #{dispatchers_str(diff)}. #{dispatchers_str} running.")
116
- end
117
- end
118
-
119
115
  def decrement_dispatchers(num)
120
- num.times { dispatcher_loops.pop }
116
+ num.times { @dispatcher_loops.pop.stop }
121
117
  end
122
118
 
123
119
  def increment_dispatchers(num)
124
- num.times { dispatcher_loops.push(new_dispatcher_loop) }
120
+ num.times { @dispatcher_loops.push(new_dispatcher_loop) }
125
121
  end
126
122
 
127
123
  def debug
128
124
  dispatcher_details = {}
129
125
 
130
- dispatcher_loops.loops.each_with_index do |dispatcher_loop, i|
126
+ @dispatcher_loops.each_with_index do |dispatcher_loop, i|
131
127
  dispatcher_details[i] = {
132
128
  started_at: dispatcher_loop.started_at.iso8601,
133
129
  dispatched: dispatcher_loop.dispatch_count,
@@ -144,7 +140,7 @@ module Rpush
144
140
  end
145
141
 
146
142
  def num_dispatcher_loops
147
- dispatcher_loops.size
143
+ @dispatcher_loops.size
148
144
  end
149
145
 
150
146
  private
@@ -160,8 +156,8 @@ module Rpush
160
156
  end
161
157
 
162
158
  def stop_dispatcher_loops
163
- dispatcher_loops.stop
164
- @dispatcher_loops = nil
159
+ @dispatcher_loops.map(&:stop)
160
+ @dispatcher_loops.clear
165
161
  end
166
162
 
167
163
  def new_dispatcher_loop
@@ -179,16 +175,6 @@ module Rpush
179
175
  def queue
180
176
  @queue ||= Queue.new
181
177
  end
182
-
183
- def dispatcher_loops
184
- @dispatcher_loops ||= Rpush::Daemon::DispatcherLoopCollection.new
185
- end
186
-
187
- def dispatchers_str(count = num_dispatcher_loops)
188
- count = count.abs
189
- str = count == 1 ? 'dispatcher' : 'dispatchers'
190
- "#{count} #{str}"
191
- end
192
178
  end
193
179
  end
194
180
  end
@@ -6,7 +6,7 @@ module Rpush
6
6
 
7
7
  attr_reader :started_at, :dispatch_count
8
8
 
9
- WAKEUP = :wakeup
9
+ STOP = :stop
10
10
 
11
11
  def initialize(queue, dispatcher)
12
12
  @queue = queue
@@ -23,8 +23,17 @@ module Rpush
23
23
 
24
24
  @thread = Thread.new do
25
25
  loop do
26
- dispatch
27
- break if @stop
26
+ payload = @queue.pop
27
+ if stop_payload?(payload)
28
+ break if should_stop?(payload)
29
+
30
+ # Intended for another dispatcher loop.
31
+ @queue.push(payload)
32
+ Thread.pass
33
+ sleep 0.1
34
+ else
35
+ dispatch(payload)
36
+ end
28
37
  end
29
38
 
30
39
  Rpush::Daemon.store.release_connection
@@ -32,31 +41,27 @@ module Rpush
32
41
  end
33
42
 
34
43
  def stop
35
- @stop = true
36
- end
37
-
38
- def wakeup
39
- @queue.push(WAKEUP) if @thread
40
- end
41
-
42
- def wait
44
+ @queue.push([STOP, object_id]) if @thread
43
45
  @thread.join if @thread
44
46
  @dispatcher.cleanup
45
47
  end
46
48
 
47
- protected
49
+ private
48
50
 
49
- def dispatch
50
- payload = @queue.pop
51
- return if payload == WAKEUP
51
+ def stop_payload?(payload)
52
+ payload.is_a?(Array) && payload.first == STOP
53
+ end
52
54
 
53
- begin
54
- @dispatch_count += 1
55
- @dispatcher.dispatch(payload)
56
- rescue StandardError => e
57
- log_error(e)
58
- reflect(:error, e)
59
- end
55
+ def should_stop?(payload)
56
+ payload.last == object_id
57
+ end
58
+
59
+ def dispatch(payload)
60
+ @dispatch_count += 1
61
+ @dispatcher.dispatch(payload)
62
+ rescue StandardError => e
63
+ log_error(e)
64
+ reflect(:error, e)
60
65
  end
61
66
  end
62
67
  end
@@ -37,7 +37,7 @@ module Rpush
37
37
  end
38
38
 
39
39
  def self.enqueue_notifications
40
- batch_size = Rpush.config.batch_size - Rpush::Daemon::AppRunner.num_queued
40
+ batch_size = Rpush.config.batch_size - Rpush::Daemon::AppRunner.total_queued
41
41
  return if batch_size <= 0
42
42
  notifications = Rpush::Daemon.store.deliverable_notifications(batch_size)
43
43
  Rpush::Daemon::AppRunner.enqueue(notifications)
@@ -6,9 +6,9 @@ module Rpush
6
6
  end
7
7
 
8
8
  def self.proc_title
9
- total_dispatchers = AppRunner.num_dispatchers
9
+ total_dispatchers = AppRunner.total_dispatchers
10
10
  dispatchers_str = total_dispatchers == 1 ? 'dispatcher' : 'dispatchers'
11
- total_queued = AppRunner.num_queued
11
+ total_queued = AppRunner.total_queued
12
12
  format("rpush | %s | %d queued | %d %s", Rpush.config.environment, total_queued, total_dispatchers, dispatchers_str)
13
13
  end
14
14
  end
@@ -17,4 +17,6 @@ module Rpush
17
17
  "Retryable error for #{@notification_id}, received error #{@code} (#{@description}) - retry after #{@response.header['retry-after']}"
18
18
  end
19
19
  end
20
+
21
+ class RateLimitError < RetryableError; end
20
22
  end
@@ -25,7 +25,7 @@ module Rpush
25
25
  loop do
26
26
  case read_io.readline.strip.to_i
27
27
  when Signal.list['HUP']
28
- AppRunner.sync
28
+ Synchronizer.sync
29
29
  Feeder.wakeup
30
30
  when Signal.list['USR2']
31
31
  AppRunner.debug
@@ -1,4 +1,7 @@
1
1
  class PGError < StandardError; end unless defined?(PGError)
2
+ module PG
3
+ class Error < StandardError; end unless defined?(::PG::Error)
4
+ end
2
5
  class Mysql; class Error < StandardError; end; end unless defined?(Mysql)
3
6
  module Mysql2; class Error < StandardError; end; end unless defined?(Mysql2)
4
7
  module ActiveRecord
@@ -17,8 +20,9 @@ module Rpush
17
20
  module Store
18
21
  class ActiveRecord
19
22
  module Reconnectable
20
- ADAPTER_ERRORS = [::ActiveRecord::StatementInvalid, PGError, Mysql::Error,
21
- Mysql2::Error, ::ActiveRecord::JDBCError, SQLite3::Exception]
23
+ ADAPTER_ERRORS = [::ActiveRecord::StatementInvalid, PGError, PG::Error,
24
+ Mysql::Error, Mysql2::Error, ::ActiveRecord::JDBCError,
25
+ SQLite3::Exception]
22
26
 
23
27
  def with_database_reconnect_and_retry
24
28
  ::ActiveRecord::Base.connection_pool.with_connection do
@@ -0,0 +1,15 @@
1
+ module Rpush
2
+ module Daemon
3
+ module StringHelpers
4
+ def pluralize(count, singular, plural = nil)
5
+ if count == 1 || count =~ /^1(\.0+)?$/
6
+ word = singular
7
+ else
8
+ word = plural || singular.pluralize
9
+ end
10
+
11
+ "#{count || 0} #{word}"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,44 @@
1
+ module Rpush
2
+ module Daemon
3
+ class Synchronizer
4
+ extend Loggable
5
+ extend StringHelpers
6
+
7
+ def self.sync
8
+ apps = Rpush::Daemon.store.all_apps
9
+ apps.each { |app| sync_app(app) }
10
+ removed = AppRunner.app_ids - apps.map(&:id)
11
+ removed.each { |app_id| AppRunner.stop_app(app_id) }
12
+
13
+ ProcTitle.update
14
+ end
15
+
16
+ def self.sync_app(app)
17
+ unless AppRunner.app_running?(app)
18
+ AppRunner.start_app(app)
19
+ log_info("[#{app.name}] Started, #{pluralize(app.connections, 'dispatcher')}.")
20
+ return
21
+ end
22
+
23
+ sync_dispatcher_count(app)
24
+ end
25
+
26
+ def self.sync_dispatcher_count(app)
27
+ num_dispatchers = AppRunner.num_dispatchers_for_app(app)
28
+ diff = num_dispatchers - app.connections
29
+ return if diff == 0
30
+
31
+ if diff > 0
32
+ AppRunner.decrement_dispatchers(app, diff)
33
+ start_stop_str = "Stopped"
34
+ else
35
+ AppRunner.increment_dispatchers(app, diff.abs)
36
+ start_stop_str = "Started"
37
+ end
38
+
39
+ num_dispatchers = AppRunner.num_dispatchers_for_app(app)
40
+ log_info("[#{app.name}] #{start_stop_str} #{pluralize(diff.abs, 'dispatcher')}. #{num_dispatchers} running.")
41
+ end
42
+ end
43
+ end
44
+ end
@@ -23,7 +23,7 @@ module Rpush
23
23
 
24
24
  def self.sync
25
25
  return unless Rpush.config.embedded
26
- Rpush::Daemon::AppRunner.sync
26
+ Rpush::Daemon::Synchronizer.sync
27
27
  end
28
28
 
29
29
  def self.debug
@@ -8,7 +8,7 @@ module Rpush
8
8
  Rpush.config.update(config)
9
9
 
10
10
  Rpush::Daemon.initialize_store
11
- Rpush::Daemon::AppRunner.sync
11
+ Rpush::Daemon::Synchronizer.sync
12
12
  Rpush::Daemon::Feeder.start
13
13
  Rpush::Daemon::AppRunner.stop
14
14
  end
@@ -1,3 +1,3 @@
1
1
  module Rpush
2
- VERSION = '2.0.0.beta1'
2
+ VERSION = '2.0.0.beta2'
3
3
  end
@@ -0,0 +1,46 @@
1
+ require 'functional_spec_helper'
2
+
3
+ describe 'Synchronization' do
4
+ let(:timeout) { 10 }
5
+ let(:app) { Rpush::Gcm::App.new }
6
+
7
+ def wait_for_num_dispatchers(num)
8
+ Timeout.timeout(timeout) do
9
+ until Rpush::Daemon::AppRunner.num_dispatchers_for_app(app) == num
10
+ sleep 0.1
11
+ end
12
+ end
13
+ end
14
+
15
+ before do
16
+ app.name = 'test'
17
+ app.auth_key = 'abc123'
18
+ app.connections = 2
19
+ app.save!
20
+
21
+ Rpush.embed
22
+ wait_for_num_dispatchers(app.connections)
23
+ end
24
+
25
+ after { Timeout.timeout(timeout) { Rpush.shutdown } }
26
+
27
+ it 'increments the number of dispatchers' do
28
+ app.connections += 1
29
+ app.save!
30
+ Rpush.sync
31
+ wait_for_num_dispatchers(app.connections)
32
+ end
33
+
34
+ it 'decrements the number of dispatchers' do
35
+ app.connections -= 1
36
+ app.save!
37
+ Rpush.sync
38
+ wait_for_num_dispatchers(app.connections)
39
+ end
40
+
41
+ it 'stops a deleted app' do
42
+ app.destroy
43
+ Rpush.sync
44
+ Rpush::Daemon::AppRunner.app_running?(app).should be_false
45
+ end
46
+ end
@@ -1,8 +1,8 @@
1
1
  ENV['RAILS_ENV'] = 'test'
2
+ client = (ENV['CLIENT'] || :active_record).to_sym
2
3
 
3
- require 'bundler'
4
+ require 'bundler/setup'
4
5
  Bundler.require(:default)
5
- client = (ENV['CLIENT'] || :active_record).to_sym
6
6
 
7
7
  unless ENV['TRAVIS'] && ENV['QUALITY'] == 'false'
8
8
  begin
@@ -156,7 +156,7 @@ describe Rpush::Client::ActiveRecord::Apns::Notification, 'to_binary' do
156
156
  notification.badge = nil
157
157
  notification.sound = nil
158
158
  notification.content_available = true
159
- bytes = notification.to_binary.bytes[-4..-1]
159
+ bytes = notification.to_binary.bytes.to_a[-4..-1]
160
160
  bytes.first.should eq 5 # priority item ID
161
161
  bytes.last.should eq Rpush::Client::ActiveRecord::Apns::Notification::APNS_PRIORITY_CONSERVE_POWER
162
162
  end
@@ -166,7 +166,7 @@ describe Rpush::Client::ActiveRecord::Apns::Notification, 'to_binary' do
166
166
  notification.badge = nil
167
167
  notification.sound = nil
168
168
  notification.content_available = true
169
- bytes = notification.to_binary.bytes[-4..-1]
169
+ bytes = notification.to_binary.bytes.to_a[-4..-1]
170
170
  bytes.first.should eq 5 # priority item ID
171
171
  bytes.last.should eq Rpush::Client::ActiveRecord::Apns::Notification::APNS_PRIORITY_IMMEDIATE
172
172
  end
@@ -134,7 +134,7 @@ describe Rpush::Daemon::Adm::Delivery do
134
134
  describe 'a 429 (Too Many Request) response' do
135
135
  let(:http) { double(shutdown: nil) }
136
136
  let(:notification) { Rpush::Adm::Notification.create!(app: app, registration_ids: %w(abc xyz), deliver_after: Time.now, collapse_key: 'sync', data: { 'message' => 'test' }) }
137
- let(:too_many_request_response) { double(code: 429, header: { 'retry-after' => 3600 }) }
137
+ let(:rate_limited_response) { double(code: 429, header: { 'retry-after' => 3600 }) }
138
138
 
139
139
  it 'should retry the entire notification respecting the Retry-After header if none sent out yet' do
140
140
  response.stub(code: 429, header: { 'retry-after' => 3600 })
@@ -167,7 +167,7 @@ describe Rpush::Daemon::Adm::Delivery do
167
167
 
168
168
  # first request to deliver message that returns too many request response
169
169
  adm_uri = URI.parse(format(Rpush::Daemon::Adm::Delivery::AMAZON_ADM_URL, 'xyz'))
170
- http.should_receive(:request).with(adm_uri, instance_of(Net::HTTP::Post)).and_return(too_many_request_response)
170
+ http.should_receive(:request).with(adm_uri, instance_of(Net::HTTP::Post)).and_return(rate_limited_response)
171
171
 
172
172
  store.should_receive(:update_notification).with do |notif|
173
173
  notif.registration_ids.include?('abc').should be_true
@@ -31,104 +31,49 @@ module Rpush
31
31
  end
32
32
  end
33
33
 
34
- describe Rpush::Daemon::AppRunner, 'stop' do
35
- let(:runner) { double }
36
- before { Rpush::Daemon::AppRunner.runners['app'] = runner }
37
- after { Rpush::Daemon::AppRunner.runners.clear }
38
-
39
- it 'stops all runners' do
40
- runner.should_receive(:stop)
41
- Rpush::Daemon::AppRunner.stop
42
- end
43
- end
44
-
45
34
  describe Rpush::Daemon::AppRunner, 'enqueue' do
46
- let(:runner) { double(enqueue: nil) }
47
- let(:notification1) { double(app_id: 1) }
48
- let(:notification2) { double(app_id: 2) }
35
+ let(:app) { double(id: 1) }
36
+ let(:notification) { double(app_id: 1) }
37
+ let(:runner) { double(Rpush::Daemon::AppRunner, enqueue: nil, start: nil, stop: nil) }
49
38
  let(:logger) { double(Rpush::Logger, error: nil, info: nil) }
50
39
 
51
40
  before do
52
41
  Rpush.stub(logger: logger)
53
42
  Rpush::Daemon::ProcTitle.stub(:update)
54
- Rpush::Daemon::AppRunner.runners[1] = runner
55
- Rpush::Daemon::AppRunner.runners[2] = runner
43
+ Rpush::Daemon::AppRunner.stub(new: runner)
44
+ Rpush::Daemon::AppRunner.start_app(app)
56
45
  end
57
46
 
58
- after { Rpush::Daemon::AppRunner.runners.clear }
47
+ after { Rpush::Daemon::AppRunner.stop }
59
48
 
60
- it 'enqueues notifications on each runner' do
61
- runner.should_receive(:enqueue).with([notification1])
62
- runner.should_receive(:enqueue).with([notification2])
63
- Rpush::Daemon::AppRunner.enqueue([notification1, notification2])
49
+ it 'enqueues notifications on the runner' do
50
+ runner.should_receive(:enqueue).with([notification])
51
+ Rpush::Daemon::AppRunner.enqueue([notification])
64
52
  end
65
53
 
66
- it 'syncs the app if a runner does not exist' do
67
- Rpush::Daemon::AppRunner.runners[3].should be_nil
54
+ it 'starts the app if a runner does not exist' do
68
55
  notification = double(app_id: 3)
69
- app = double(Rpush::App, id: 3, connections: 1, service_name: 'app_runner_spec_service', environment: 'sandbox', certificate: TEST_CERT, password: nil, name: 'test')
70
- Rpush::Daemon.store = double(app: app)
56
+ new_app = double(Rpush::App, id: 3)
57
+ Rpush::Daemon.store = double(app: new_app)
71
58
  Rpush::Daemon::AppRunner.enqueue([notification])
72
- Rpush::Daemon::AppRunner.runners[3].should_not be_nil
59
+ Rpush::Daemon::AppRunner.app_running?(new_app).should be_true
73
60
  end
74
61
  end
75
62
 
76
- describe Rpush::Daemon::AppRunner, 'sync' do
77
- let(:app) { double(Rpush::AppRunnerSpecService::App, name: 'test') }
78
- let(:new_app) { double(Rpush::AppRunnerSpecService::App, name: 'new_test') }
79
- let(:runner) { double(sync: nil, stop: nil, start: nil) }
80
- let(:logger) { double(Rpush::Logger, error: nil, warn: nil) }
81
- let(:queue) { Queue.new }
82
- let(:store) { double(all_apps: [app]) }
63
+ describe Rpush::Daemon::AppRunner, 'start_app' do
64
+ let(:app) { double(id: 1, name: 'test') }
65
+ let(:runner) { double(Rpush::Daemon::AppRunner, enqueue: nil, start: nil, stop: nil) }
66
+ let(:logger) { double(Rpush::Logger, error: nil, info: nil) }
83
67
 
84
68
  before do
85
- app.stub(id: 1)
86
- new_app.stub(id: 2)
87
- Queue.stub(new: queue)
88
- Rpush::Daemon::AppRunner.runners[app.id] = runner
89
69
  Rpush.stub(logger: logger)
90
- Rpush::Daemon.stub(store: store)
91
- Rpush::Daemon::ProcTitle.stub(:update)
92
- end
93
-
94
- after { Rpush::Daemon::AppRunner.runners.clear }
95
-
96
- it 'instructs existing runners to sync' do
97
- runner.should_receive(:sync).with(app)
98
- Rpush::Daemon::AppRunner.sync
99
- end
100
-
101
- it 'starts a runner for a new app' do
102
- store.stub(all_apps: [app, new_app])
103
- new_runner = double
104
- Rpush::Daemon::AppRunner.should_receive(:new).with(new_app).and_return(new_runner)
105
- new_runner.should_receive(:start)
106
- Rpush::Daemon::AppRunner.sync
107
- end
108
-
109
- it 'deletes old runners' do
110
- store.stub(all_apps: [])
111
- runner.should_receive(:stop)
112
- Rpush::Daemon::AppRunner.sync
113
70
  end
114
71
 
115
72
  it 'logs an error if the runner could not be started' do
116
- store.stub(all_apps: [app, new_app])
117
- new_runner = double
118
- Rpush::Daemon::AppRunner.should_receive(:new).with(new_app).and_return(new_runner)
119
- new_runner.stub(:start).and_raise(StandardError)
73
+ Rpush::Daemon::AppRunner.should_receive(:new).with(app).and_return(runner)
74
+ runner.stub(:start).and_raise(StandardError)
120
75
  Rpush.logger.should_receive(:error)
121
- Rpush::Daemon::AppRunner.sync
122
- end
123
-
124
- it 'reflects errors if the runner could not be started' do
125
- store.stub(all_apps: [app, new_app])
126
- new_runner = double
127
- Rpush::Daemon::AppRunner.should_receive(:new).with(new_app).and_return(new_runner)
128
- e = StandardError.new
129
- new_runner.stub(:start).and_raise(e)
130
- Rpush::Daemon::AppRunner.should_receive(:reflect).with(:error, e)
131
- Rpush::Daemon::AppRunner.sync
76
+ Rpush::Daemon::AppRunner.start_app(app)
132
77
  end
133
78
  end
134
79
 
@@ -137,15 +82,15 @@ describe Rpush::Daemon::AppRunner, 'debug' do
137
82
  environment: 'development', certificate: TEST_CERT, service_name: 'app_runner_spec_service')
138
83
  end
139
84
  let(:logger) { double(Rpush::Logger, info: nil) }
140
- let(:store) { double(all_apps: [app]) }
85
+ let(:store) { double(all_apps: [app], release_connection: nil) }
141
86
 
142
87
  before do
143
88
  Rpush::Daemon.stub(config: {}, store: store)
144
89
  Rpush.stub(logger: logger)
145
- Rpush::Daemon::AppRunner.sync
90
+ Rpush::Daemon::AppRunner.start_app(app)
146
91
  end
147
92
 
148
- after { Rpush::Daemon::AppRunner.runners.clear }
93
+ after { Rpush::Daemon::AppRunner.stop_app(app.id) }
149
94
 
150
95
  it 'prints debug app states to the log' do
151
96
  Rpush.logger.should_receive(:info).with(kind_of(String))
@@ -161,18 +106,16 @@ describe Rpush::Daemon::AppRunner do
161
106
  let(:runner) { Rpush::Daemon::AppRunner.new(app) }
162
107
  let(:logger) { double(Rpush::Logger, info: nil) }
163
108
  let(:queue) { Queue.new }
164
- let(:dispatcher_loop_collection) { Rpush::Daemon::DispatcherLoopCollection.new }
165
- let(:service_loop) do double(Rpush::Daemon::AppRunnerSpecService::ServiceLoop,
166
- start: nil, stop: nil)
167
- end
109
+ let(:service_loop) { double(Rpush::Daemon::AppRunnerSpecService::ServiceLoop, start: nil, stop: nil) }
110
+ let(:dispatcher_loop) { double(Rpush::Daemon::DispatcherLoop, stop: nil, start: nil) }
168
111
  let(:store) { double(Rpush::Daemon::Store::ActiveRecord, release_connection: nil) }
169
112
 
170
113
  before do
114
+ Rpush::Daemon::DispatcherLoop.stub(new: dispatcher_loop)
171
115
  Rpush::Daemon.stub(store: store)
172
116
  Rpush::Daemon::AppRunnerSpecService::ServiceLoop.stub(new: service_loop)
173
117
  Queue.stub(new: queue)
174
118
  Rpush.stub(logger: logger)
175
- Rpush::Daemon::DispatcherLoopCollection.stub(new: dispatcher_loop_collection)
176
119
  end
177
120
 
178
121
  describe 'start' do
@@ -182,6 +125,11 @@ describe Rpush::Daemon::AppRunner do
182
125
  runner.num_dispatcher_loops.should eq 2
183
126
  end
184
127
 
128
+ it 'starts the dispatcher loop' do
129
+ dispatcher_loop.should_receive(:start)
130
+ runner.start
131
+ end
132
+
185
133
  it 'starts the loops' do
186
134
  service_loop.should_receive(:start)
187
135
  runner.start
@@ -209,7 +157,7 @@ describe Rpush::Daemon::AppRunner do
209
157
  before { runner.start }
210
158
 
211
159
  it 'stops the delivery dispatchers' do
212
- dispatcher_loop_collection.should_receive(:stop)
160
+ dispatcher_loop.should_receive(:stop)
213
161
  runner.stop
214
162
  end
215
163
 
@@ -218,18 +166,4 @@ describe Rpush::Daemon::AppRunner do
218
166
  runner.stop
219
167
  end
220
168
  end
221
-
222
- describe 'sync' do
223
- before { runner.start }
224
-
225
- it 'reduces the number of dispatchers if needed' do
226
- app.stub(connections: 0)
227
- expect { runner.sync(app) }.to change(runner, :num_dispatcher_loops).to(0)
228
- end
229
-
230
- it 'increases the number of dispatchers if needed' do
231
- app.stub(connections: 2)
232
- expect { runner.sync(app) }.to change(runner, :num_dispatcher_loops).to(2)
233
- end
234
- end
235
169
  end
@@ -4,8 +4,6 @@ describe Rpush::Daemon::DispatcherLoop do
4
4
  def run_dispatcher_loop
5
5
  dispatcher_loop.start
6
6
  dispatcher_loop.stop
7
- dispatcher_loop.wakeup
8
- dispatcher_loop.wait
9
7
  end
10
8
 
11
9
  let(:notification) { double }
@@ -37,20 +35,9 @@ describe Rpush::Daemon::DispatcherLoop do
37
35
  run_dispatcher_loop
38
36
  end
39
37
 
40
- it 'instructs the queue to wakeup the thread when told to stop' do
41
- queue.should_receive(:push).with(Rpush::Daemon::DispatcherLoop::WAKEUP).and_call_original
42
- run_dispatcher_loop
43
- end
44
-
45
38
  describe 'stop' do
46
39
  before do
47
40
  queue.clear
48
- queue.push(Rpush::Daemon::DispatcherLoop::WAKEUP)
49
- end
50
-
51
- it 'does not attempt to dispatch when a WAKEUP is dequeued' do
52
- dispatcher.should_not_receive(:dispatch)
53
- run_dispatcher_loop
54
41
  end
55
42
 
56
43
  it 'instructs the dispatcher to cleanup' do
@@ -33,14 +33,14 @@ describe Rpush::Daemon::Feeder do
33
33
  end
34
34
 
35
35
  it 'does not load more notifications if the total queue size is equal to the batch size' do
36
- Rpush::Daemon::AppRunner.stub(num_queued: Rpush.config.batch_size)
36
+ Rpush::Daemon::AppRunner.stub(total_queued: Rpush.config.batch_size)
37
37
  Rpush::Daemon.store.should_not_receive(:deliverable_notifications)
38
38
  start_and_stop
39
39
  end
40
40
 
41
41
  it 'limits the batch size if some runners are still processing notifications' do
42
42
  Rpush.config.stub(batch_size: 10)
43
- Rpush::Daemon::AppRunner.stub(num_queued: 6)
43
+ Rpush::Daemon::AppRunner.stub(total_queued: 6)
44
44
  Rpush::Daemon.store.should_receive(:deliverable_notifications).with(4)
45
45
  start_and_stop
46
46
  end
@@ -42,13 +42,13 @@ describe Rpush::Daemon::SignalHandler do
42
42
 
43
43
  describe 'HUP' do
44
44
  before do
45
- Rpush::Daemon::AppRunner.stub(:sync)
45
+ Rpush::Daemon::Synchronizer.stub(:sync)
46
46
  Rpush::Daemon::Feeder.stub(:wakeup)
47
47
  end
48
48
 
49
- it 'syncs the AppRunner' do
49
+ it 'syncs' do
50
50
  with_handler_start_stop do
51
- Rpush::Daemon::AppRunner.should_receive(:sync)
51
+ Rpush::Daemon::Synchronizer.should_receive(:sync)
52
52
  signal_handler('HUP')
53
53
  end
54
54
  end
@@ -41,6 +41,7 @@ describe Rpush::Daemon::Store::ActiveRecord::Reconnectable do
41
41
  fail "Please update #{__FILE__} for adapter #{SPEC_ADAPTER}"
42
42
  end
43
43
  end
44
+
44
45
  let(:error) { adapter_error_class.new("db down!") }
45
46
  let(:test_double) { TestDouble.new(error, 1) }
46
47
 
@@ -11,7 +11,8 @@ describe Rpush::Daemon, "when starting" do
11
11
  before do
12
12
  Rpush.stub(logger: logger)
13
13
  Rpush::Daemon::Feeder.stub(:start)
14
- Rpush::Daemon::AppRunner.stub(sync: nil, stop: nil)
14
+ Rpush::Daemon::Synchronizer.stub(sync: nil)
15
+ Rpush::Daemon::AppRunner.stub(stop: nil)
15
16
  Rpush::Daemon.stub(exit: nil, puts: nil)
16
17
  Rpush::Daemon::SignalHandler.stub(start: nil, stop: nil, handle_shutdown_signal: nil)
17
18
  Process.stub(:daemon)
@@ -87,7 +88,7 @@ describe Rpush::Daemon, "when starting" do
87
88
  end
88
89
 
89
90
  it "syncs apps" do
90
- Rpush::Daemon::AppRunner.should_receive(:sync)
91
+ Rpush::Daemon::Synchronizer.should_receive(:sync)
91
92
  Rpush::Daemon.start
92
93
  end
93
94
 
@@ -36,8 +36,8 @@ end
36
36
  describe Rpush, 'sync' do
37
37
  before { Rpush.config.embedded = true }
38
38
 
39
- it 'syncs the AppRunner' do
40
- Rpush::Daemon::AppRunner.should_receive(:sync)
39
+ it 'syncs' do
40
+ Rpush::Daemon::Synchronizer.should_receive(:sync)
41
41
  Rpush.sync
42
42
  end
43
43
  end
@@ -70,11 +70,13 @@ describe Rpush::Logger do
70
70
  logger.info("hi mom")
71
71
  end
72
72
 
73
- it "should not print out the msg if not running in the foreground" do
74
- Rpush.config.foreground = false
75
- logger = Rpush::Logger.new
76
- STDOUT.should_not_receive(:puts).with(/hi mom/)
77
- logger.info("hi mom")
73
+ unless Rpush.jruby? # These tests do not work on JRuby.
74
+ it "should not print out the msg if not running in the foreground" do
75
+ Rpush.config.foreground = false
76
+ logger = Rpush::Logger.new
77
+ STDOUT.should_not_receive(:puts).with(/hi mom/)
78
+ logger.info("hi mom")
79
+ end
78
80
  end
79
81
 
80
82
  it "should prefix log lines with the current time" do
@@ -2,7 +2,8 @@ require 'unit_spec_helper'
2
2
 
3
3
  describe Rpush, 'push' do
4
4
  before do
5
- Rpush::Daemon::AppRunner.stub(sync: nil, wait: nil)
5
+ Rpush::Daemon::Synchronizer.stub(sync: nil)
6
+ Rpush::Daemon::AppRunner.stub(wait: nil)
6
7
  Rpush::Daemon::Feeder.stub(start: nil)
7
8
  end
8
9
 
@@ -16,8 +17,8 @@ describe Rpush, 'push' do
16
17
  Rpush.push
17
18
  end
18
19
 
19
- it 'syncs the app runner' do
20
- Rpush::Daemon::AppRunner.should_receive(:sync)
20
+ it 'syncs' do
21
+ Rpush::Daemon::Synchronizer.should_receive(:sync)
21
22
  Rpush.push
22
23
  end
23
24
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rpush
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.beta1
4
+ version: 2.0.0.beta2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ian Leitch
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-13 00:00:00.000000000 Z
11
+ date: 2014-07-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: multi_json
@@ -136,7 +136,6 @@ files:
136
136
  - lib/rpush/daemon/dispatcher/http.rb
137
137
  - lib/rpush/daemon/dispatcher/tcp.rb
138
138
  - lib/rpush/daemon/dispatcher_loop.rb
139
- - lib/rpush/daemon/dispatcher_loop_collection.rb
140
139
  - lib/rpush/daemon/errors.rb
141
140
  - lib/rpush/daemon/feeder.rb
142
141
  - lib/rpush/daemon/gcm.rb
@@ -155,8 +154,9 @@ files:
155
154
  - lib/rpush/daemon/store/active_record/reconnectable.rb
156
155
  - lib/rpush/daemon/store/interface.rb
157
156
  - lib/rpush/daemon/store/redis.rb
157
+ - lib/rpush/daemon/string_helpers.rb
158
+ - lib/rpush/daemon/synchronizer.rb
158
159
  - lib/rpush/daemon/tcp_connection.rb
159
- - lib/rpush/daemon/too_many_requests_error.rb
160
160
  - lib/rpush/daemon/wpns.rb
161
161
  - lib/rpush/daemon/wpns/delivery.rb
162
162
  - lib/rpush/deprecatable.rb
@@ -176,6 +176,7 @@ files:
176
176
  - spec/functional/gcm_spec.rb
177
177
  - spec/functional/new_app_spec.rb
178
178
  - spec/functional/retry_spec.rb
179
+ - spec/functional/synchronization_spec.rb
179
180
  - spec/functional/wpns_spec.rb
180
181
  - spec/functional_spec_helper.rb
181
182
  - spec/integration/rpush_spec.rb
@@ -212,7 +213,6 @@ files:
212
213
  - spec/unit/daemon/delivery_spec.rb
213
214
  - spec/unit/daemon/dispatcher/http_spec.rb
214
215
  - spec/unit/daemon/dispatcher/tcp_spec.rb
215
- - spec/unit/daemon/dispatcher_loop_collection_spec.rb
216
216
  - spec/unit/daemon/dispatcher_loop_spec.rb
217
217
  - spec/unit/daemon/feeder_spec.rb
218
218
  - spec/unit/daemon/gcm/delivery_spec.rb
@@ -223,7 +223,6 @@ files:
223
223
  - spec/unit/daemon/store/active_record/reconnectable_spec.rb
224
224
  - spec/unit/daemon/store/active_record_spec.rb
225
225
  - spec/unit/daemon/tcp_connection_spec.rb
226
- - spec/unit/daemon/too_many_requests_error_spec.rb
227
226
  - spec/unit/daemon/wpns/delivery_spec.rb
228
227
  - spec/unit/daemon_spec.rb
229
228
  - spec/unit/deprecatable_spec.rb
@@ -267,6 +266,7 @@ test_files:
267
266
  - spec/functional/gcm_spec.rb
268
267
  - spec/functional/new_app_spec.rb
269
268
  - spec/functional/retry_spec.rb
269
+ - spec/functional/synchronization_spec.rb
270
270
  - spec/functional/wpns_spec.rb
271
271
  - spec/functional_spec_helper.rb
272
272
  - spec/integration/rpush_spec.rb
@@ -303,7 +303,6 @@ test_files:
303
303
  - spec/unit/daemon/delivery_spec.rb
304
304
  - spec/unit/daemon/dispatcher/http_spec.rb
305
305
  - spec/unit/daemon/dispatcher/tcp_spec.rb
306
- - spec/unit/daemon/dispatcher_loop_collection_spec.rb
307
306
  - spec/unit/daemon/dispatcher_loop_spec.rb
308
307
  - spec/unit/daemon/feeder_spec.rb
309
308
  - spec/unit/daemon/gcm/delivery_spec.rb
@@ -314,7 +313,6 @@ test_files:
314
313
  - spec/unit/daemon/store/active_record/reconnectable_spec.rb
315
314
  - spec/unit/daemon/store/active_record_spec.rb
316
315
  - spec/unit/daemon/tcp_connection_spec.rb
317
- - spec/unit/daemon/too_many_requests_error_spec.rb
318
316
  - spec/unit/daemon/wpns/delivery_spec.rb
319
317
  - spec/unit/daemon_spec.rb
320
318
  - spec/unit/deprecatable_spec.rb
@@ -1,33 +0,0 @@
1
- module Rpush
2
- module Daemon
3
- class DispatcherLoopCollection
4
- attr_reader :loops
5
-
6
- def initialize
7
- @loops = []
8
- end
9
-
10
- def push(dispatcher_loop)
11
- @loops << dispatcher_loop
12
- end
13
-
14
- def pop
15
- dispatcher_loop = @loops.pop
16
- dispatcher_loop.stop
17
- dispatcher_loop.wakeup
18
- @loops.map(&:wakeup)
19
- dispatcher_loop.wait
20
- end
21
-
22
- def size
23
- @loops.size
24
- end
25
-
26
- def stop
27
- @loops.map(&:stop)
28
- @loops.map(&:wakeup)
29
- @loops.map(&:wait)
30
- end
31
- end
32
- end
33
- end
@@ -1,20 +0,0 @@
1
- module Rpush
2
- class TooManyRequestsError < StandardError
3
- attr_reader :code, :description, :response
4
-
5
- def initialize(code, notification_id, description, response)
6
- @code = code
7
- @notification_id = notification_id
8
- @description = description
9
- @response = response
10
- end
11
-
12
- def to_s
13
- message
14
- end
15
-
16
- def message
17
- "Too many requests for #{@notification_id}, received error #{@code} (#{@description}) - retry after #{@response.header['retry-after']}"
18
- end
19
- end
20
- end
@@ -1,37 +0,0 @@
1
- require 'unit_spec_helper'
2
-
3
- describe Rpush::Daemon::DispatcherLoopCollection do
4
- let(:dispatcher_loop) { double.as_null_object }
5
- let(:collection) { Rpush::Daemon::DispatcherLoopCollection.new }
6
-
7
- it 'returns the size of the collection' do
8
- collection.push(dispatcher_loop)
9
- collection.size.should eq 1
10
- end
11
-
12
- it 'pops a dispatcher loop from the collection' do
13
- collection.push(dispatcher_loop)
14
- dispatcher_loop.should_receive(:stop)
15
- dispatcher_loop.should_receive(:wakeup)
16
- dispatcher_loop.should_receive(:wait)
17
- collection.pop
18
- collection.size.should eq 0
19
- end
20
-
21
- it 'wakes up all dispatcher loops when popping a single dispatcher_loop' do
22
- collection.push(dispatcher_loop)
23
- dispatcher_loop2 = double.as_null_object
24
- collection.push(dispatcher_loop2)
25
- dispatcher_loop.should_receive(:wakeup)
26
- dispatcher_loop2.should_receive(:wakeup)
27
- collection.pop
28
- end
29
-
30
- it 'stops all dispatcher detetcloops' do
31
- collection.push(dispatcher_loop)
32
- dispatcher_loop.should_receive(:stop)
33
- dispatcher_loop.should_receive(:wakeup)
34
- dispatcher_loop.should_receive(:wait)
35
- collection.stop
36
- end
37
- end
@@ -1,14 +0,0 @@
1
- require "unit_spec_helper"
2
-
3
- describe Rpush::TooManyRequestsError do
4
- let(:response) { double(code: 429, header: { 'retry-after' => 3600 }) }
5
- let(:error) { Rpush::TooManyRequestsError.new(429, 12, "Too Many Requests", response) }
6
-
7
- it "returns an informative message" do
8
- error.to_s.should eq "Too many requests for 12, received error 429 (Too Many Requests) - retry after 3600"
9
- end
10
-
11
- it "returns the error code" do
12
- error.code.should eq 429
13
- end
14
- end