rpush 2.0.0.beta1 → 2.0.0.beta2

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.
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