resque-cluster 0.1.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile +3 -1
  3. data/Gemfile.lock +47 -44
  4. data/lib/resque/cluster.rb +1 -0
  5. data/lib/resque/cluster/config.rb +169 -0
  6. data/lib/resque/cluster/config/file.rb +40 -0
  7. data/lib/resque/cluster/config/verifier.rb +27 -0
  8. data/lib/resque/cluster/member.rb +17 -27
  9. data/lib/resque/cluster/version.rb +1 -1
  10. data/lib/resque/pool/patches.rb +5 -0
  11. data/resque-cluster.gemspec +1 -1
  12. data/spec/integration/cluster_spec.rb +41 -11
  13. data/spec/integration/config/bad_global_config.yml +3 -0
  14. data/spec/integration/config/global_rebalance_config3.yml +5 -0
  15. data/spec/integration/config/rebalance_config.yml +4 -0
  16. data/spec/integration/spec_helper.rb +3 -0
  17. data/spec/integration/test_member_manager.rb +50 -19
  18. data/spec/unit/config/verifier_spec.rb +86 -0
  19. data/spec/unit/config_spec.rb +194 -0
  20. data/spec/unit/member_spec.rb +5 -32
  21. data/spec/unit/resque_pool_patches_spec.rb +3 -3
  22. data/spec/unit/spec_helper.rb +5 -1
  23. data/spec/unit/support/broken_yaml_config.yml +1 -0
  24. data/spec/unit/support/empty_config.yml +1 -0
  25. data/spec/unit/support/missing_global_maximum.yml +3 -0
  26. data/spec/unit/support/missing_local_maximum.yml +3 -0
  27. data/spec/unit/support/non_hash_config.yml +0 -0
  28. data/spec/unit/support/separate_missing_global.yml +2 -0
  29. data/spec/unit/support/separate_missing_local.yml +2 -0
  30. data/spec/unit/support/single_old_missing_global.yml +9 -0
  31. data/spec/unit/support/single_old_missing_local.yml +9 -0
  32. data/spec/unit/support/valid_config.yml +13 -0
  33. data/spec/unit/support/valid_global_config.yml +1 -0
  34. data/spec/unit/support/valid_local_config.yml +1 -0
  35. data/spec/unit/support/valid_single_new_config.yml +13 -0
  36. data/spec/unit/support/valid_single_old_config.yml +10 -0
  37. metadata +72 -30
@@ -1,5 +1,5 @@
1
1
  module Resque
2
2
  class Cluster
3
- VERSION = '0.1.1.1'
3
+ VERSION = '0.2.0'
4
4
  end
5
5
  end
@@ -22,6 +22,11 @@ module Resque
22
22
  original_maintain_worker_count.bind(self).call
23
23
  end
24
24
 
25
+ def quit
26
+ log "Quiting ..."
27
+ Process.kill(:TERM, Process.pid)
28
+ end
29
+
25
30
  def cluster_update
26
31
  Resque::Cluster.member.perform if Resque::Cluster.member
27
32
  end
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.require_paths = ["lib"]
20
20
 
21
21
  s.add_dependency 'resque-pool', '~> 0.5.0'
22
- s.add_dependency 'gru', '0.1.1'
22
+ s.add_dependency 'gru', '0.1.3'
23
23
 
24
24
  s.add_development_dependency 'pry', '> 0.0'
25
25
  s.add_development_dependency 'awesome_print', '> 0.0'
@@ -2,9 +2,13 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
3
  LOCAL_CONFIG = "spec/integration/config/local_config.yml"
4
4
  GLOBAL_CONFIG = "spec/integration/config/global_config.yml"
5
+ BAD_GLOBAL_CONFIG = "spec/integration/config/bad_global_config.yml"
6
+ BAD_LOCAL_CONFIG = "spec/integration/config/bad_local_config.yml"
5
7
  LOCAL_CONFIG2 = "spec/integration/config/local_config2.yml"
6
8
  GLOBAL_CONFIG2 = "spec/integration/config/global_config2.yml"
7
9
  GLOBAL_REBALANCE_CONFIG2 = "spec/integration/config/global_rebalance_config2.yml"
10
+ GLOBAL_REBALANCE_CONFIG3 = "spec/integration/config/global_rebalance_config3.yml"
11
+ REBALANCE_CONFIG = "spec/integration/config/rebalance_config.yml"
8
12
  GLOBAL_CONFIG3 = "spec/integration/config/global_config3.yml"
9
13
  GLOBAL_CONFIG4 = "spec/integration/config/global_config4.yml"
10
14
 
@@ -49,6 +53,32 @@ RSpec.describe "Resque test-cluster" do
49
53
 
50
54
  end
51
55
 
56
+ context "Cluster with bad configs" do
57
+ before :all do
58
+ @d = TestMemberManager.new(LOCAL_CONFIG2, BAD_GLOBAL_CONFIG)
59
+ @e = TestMemberManager.new(BAD_LOCAL_CONFIG, GLOBAL_REBALANCE_CONFIG2)
60
+ @f = TestMemberManager.new("", "")
61
+ @d.start
62
+ @e.start
63
+ @f.start
64
+ sleep(5) # rebalance time
65
+ end
66
+
67
+ it 'expects counts to be correct after workers get spun up' do
68
+ expect(TestMemberManager.counts).to eq({})
69
+ expect(@d.is_running?).to eq(false)
70
+ expect(@e.is_running?).to eq(false)
71
+ expect(@f.is_running?).to eq(false)
72
+ expect(TestMemberManager.resque_cluster_members.count).to eq(0)
73
+ end
74
+
75
+ after :all do
76
+ TestMemberManager.stop_all
77
+ end
78
+
79
+ end
80
+
81
+
52
82
  context "Cluster with Rebalancing" do
53
83
  before :all do
54
84
  @d = TestMemberManager.new(LOCAL_CONFIG2, GLOBAL_REBALANCE_CONFIG2)
@@ -106,29 +136,29 @@ RSpec.describe "Resque test-cluster" do
106
136
  end
107
137
  end
108
138
 
109
- context "Multiple Configs in the same cluster" do
139
+ context "Rebalance after global maximums change" do
110
140
  before :all do
111
141
  @a = TestMemberManager.new(LOCAL_CONFIG, GLOBAL_CONFIG)
112
- @b = TestMemberManager.new(LOCAL_CONFIG2, GLOBAL_CONFIG)
113
- @c = TestMemberManager.new(LOCAL_CONFIG, GLOBAL_REBALANCE_CONFIG2)
142
+ @b = TestMemberManager.new(LOCAL_CONFIG, GLOBAL_CONFIG)
143
+ @c = TestMemberManager.new(LOCAL_CONFIG, GLOBAL_REBALANCE_CONFIG3)
114
144
  @a.start
115
145
  @b.start
116
146
  sleep(3) # rebalance time
117
147
  end
118
148
 
119
- it 'expects to have each cluster member only running workers in it\'s config' do
120
- expect(TestMemberManager.counts).to eq({"tar"=>3, "par"=>1, "par,tar,var"=>1})
121
- expect(@a.counts).to eq({"tar"=>3, "par"=>1, "par,tar,var"=>1})
122
- expect(@b.counts).to be_empty
149
+ it 'expects to have each cluster member only running workers in its config' do
150
+ expect(TestMemberManager.counts).to eq('par' => 2, 'tar' => 6, 'par,tar,var' => 1)
151
+ expect(@a.counts).to eq('par' => 1, 'tar' => 3, 'par,tar,var' => 1)
152
+ expect(@b.counts).to eq('par' => 1, 'tar' => 3)
123
153
  end
124
154
 
125
155
  it 'expects the cluster to redistribute correctly after global config change' do
126
156
  @c.start
127
157
  sleep(8) # rebalance time
128
- expect(TestMemberManager.counts).to eq({"star"=>4})
129
- expect(@a.counts).to be_empty
130
- expect(@b.counts).to eq({"star"=>4})
131
- expect(@c.counts).to be_empty
158
+ expect(TestMemberManager.counts).to eq('par' => 3, 'tar' => 6, 'par,tar,var' => 2)
159
+ expect(@a.counts).to eq('par' => 1, 'tar' => 2, 'par,tar,var' => 1)
160
+ expect(@b.counts).to eq('par' => 1, 'tar' => 2)
161
+ expect(@c.counts).to eq('par' => 1, 'tar' => 2, 'par,tar,var' => 1)
132
162
  end
133
163
 
134
164
  after :all do
@@ -0,0 +1,3 @@
1
+ par= 2
2
+ ? tar: 8
3
+ "par,tar,var"+ 1
@@ -0,0 +1,5 @@
1
+ rebalance_cluster: true
2
+ global_maximums:
3
+ par: 4
4
+ tar: 6
5
+ "par,tar,var": 2
@@ -0,0 +1,4 @@
1
+ par: 4
2
+ tar: 4
3
+ "par,tar,var": 2
4
+ star: 8
@@ -1,3 +1,6 @@
1
1
  require 'pry'
2
2
  require 'rspec'
3
+ require 'sys/proctable'
4
+ require 'resque'
5
+
3
6
  require File.expand_path(File.dirname(__FILE__) + '/test_member_manager')
@@ -1,4 +1,7 @@
1
1
  class TestMemberManager
2
+ include Sys
3
+
4
+ attr_accessor :pid
2
5
 
3
6
  def initialize(local_config_path, global_config_path, cluster_name = "test-cluster", environment = "test")
4
7
  @local_config_path = local_config_path
@@ -12,53 +15,71 @@ class TestMemberManager
12
15
  def start
13
16
  ENV['GRU_HOSTNAME'] = hostname
14
17
  @pid = spawn("bundle exec spec/integration/bin/resque-cluster_member_test -c #{@local_config_path} -E #{@environment}#{@cluster_name.nil? ? "" : " -C "+@cluster_name} -G #{@global_config_path}")
18
+ count = 0
15
19
 
16
- while ( @pool_master_pid.nil? ) do
20
+ while ( @pool_master_pid.nil? && count <= 100 ) do
17
21
  sleep(0.1)
18
- child_process = @pid #`pgrep -P #{@pid}`.strip
19
- pool = `ps -p #{child_process} -hf | grep 'resque-pool-master\\[resque-cluster\\]: managing \\[' | awk '{print $1}'`.strip.to_i
20
- @pool_master_pid = pool > 0 ? pool : nil
22
+
23
+ if (ProcTable.ps(@pid) &&
24
+ ProcTable.ps(@pid).cmdline =~ /resque-pool-master\[resque-cluster\]:\smanaging\s\[/)
25
+ pool_pid = ProcTable.ps(@pid).pid
26
+ end
27
+
28
+ @pool_master_pid = pool_pid ? pool_pid : nil
29
+ count += 1
21
30
  end
22
- puts "Pool Master pid is ---------- #{@pool_master_pid}"
31
+
32
+ puts "Pool Master pid is ---------- #{@pool_master_pid}" if @pool_master_pid
23
33
  end
24
34
 
25
35
  def stop
26
36
  puts "************************************************ About to kill : Pool Master pid ---------- #{@pool_master_pid}"
27
- Process.kill(:TERM, @pool_master_pid)
37
+ Process.kill(:QUIT, @pool_master_pid)
28
38
  while ( @pool_master_pid ) do
29
- pool = `ps -p #{@pool_master_pid} -hf | grep 'resque-pool-master\\[resque-cluster\\]: managing \\[' | awk '{print $1}'`.strip.to_i
30
- @pool_master_pid = pool > 0 ? pool : nil
39
+ if (ProcTable.ps(@pool_master_pid) &&
40
+ ! (ProcTable.ps(@pool_master_pid).cmdline =~ /resque-pool-master\[resque-cluster\]:\smanaging\s\[/))
41
+ @pool_master_pid = nil
42
+ end
31
43
  end
32
44
  @pid = nil
33
- sleep(5)
45
+ sleep(3)
46
+ end
47
+
48
+ def is_running?
49
+ (ProcTable.ps(@pid).instance_of? (Struct::ProcTableStruct)) &&
50
+ ! (ProcTable.ps(@pid).cmdline =~ /resque-pool-master\[resque-cluster\]:\smanaging\s\[/).nil?
34
51
  end
35
52
 
36
53
  def kill
37
54
  puts "************************************************ About to kill -9 : Pool Master pid ---------- #{@pool_master_pid}"
38
- `kill -9 #{@pool_master_pid}`
55
+ Process.kill(:TERM, @pool_master_pid)
39
56
  while ( @pool_master_pid ) do
40
- pool = `ps -p #{@pool_master_pid} -hf | grep 'resque-pool-master\\[resque-cluster\\]: managing \\[' | awk '{print $1}'`.strip.to_i
41
- @pool_master_pid = pool > 0 ? pool : nil
57
+ if (ProcTable.ps(@pool_master_pid) &&
58
+ ! (ProcTable.ps(@pool_master_pid).cmdline =~ /resque-pool-master\[resque-cluster\]:\smanaging\s\[/))
59
+ @pool_master_pid = nil
60
+ end
42
61
  end
43
62
  @pid = nil
44
- sleep(5)
63
+ sleep(3)
45
64
  end
46
65
 
47
66
  def counts
48
67
  return {} unless @pool_master_pid
49
- local_workers = `ps --ppid #{@pool_master_pid} -fh | awk '{print $8}'`.split
68
+ local_workers = ProcTable.ps.select{|p| p.ppid == @pool_master_pid}.map(&:cmdline)
50
69
  TestMemberManager.worker_counts(local_workers)
51
70
  end
52
71
 
53
72
  def self.counts
54
- all_workers = `ps -ef | grep "resque-" | grep "Waiting for" | grep -v ps| awk '{print $11}'`.split
73
+ pool_pids = resque_cluster_members.map(&:pid)
74
+ all_workers = ProcTable.ps.select{|p| pool_pids.include? p.ppid}.map(&:cmdline)
55
75
  worker_counts(all_workers)
56
76
  end
57
77
 
58
78
  def self.worker_counts(worker_array)
59
79
  final_counts = Hash.new(0)
60
80
 
61
- worker_array.each do |worker|
81
+ worker_array.each do |worker_cmdline|
82
+ worker = parse_worker_name(worker_cmdline)
62
83
  final_counts[worker] += 1
63
84
  end
64
85
 
@@ -69,13 +90,23 @@ class TestMemberManager
69
90
  @hostname ||= "#{Socket.gethostname}-#{member_count+1}"
70
91
  end
71
92
 
93
+ def self.parse_worker_name(worker_cmdline)
94
+ worker_cmdline.gsub(/resque(\d|\.|-)*:\sWaiting\sfor\s/, '')
95
+ end
96
+
72
97
  def self.stop_all
73
- pools = `ps -ef | grep 'resque-pool-master\\[resque-cluster\\]: managing \\[' | awk '{print $2}'`.split
74
- `kill #{pools.join(' ')}` unless pools.empty?
98
+ TestMemberManager.resque_cluster_members.each do |member|
99
+ Process.kill(:TERM, member.pid)
100
+ end
75
101
  sleep(3)
76
102
  end
77
103
 
78
104
  def member_count
79
- `ps -ef | grep resque-pool-master | grep -v grep|wc -l`.strip.to_i
105
+ TestMemberManager.resque_cluster_members.count
106
+ end
107
+
108
+ def self.resque_cluster_members
109
+ ProcTable.ps.select{|p| p.cmdline =~ /resque-pool-master/}
80
110
  end
111
+
81
112
  end
@@ -0,0 +1,86 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ module Resque
4
+ RSpec.describe Cluster::Config::Verifier do
5
+ let(:valid_local_config) { Cluster::Config::File.new(support_dir + 'valid_local_config.yml') }
6
+ let(:valid_global_config) { Cluster::Config::File.new(support_dir + 'valid_global_config.yml') }
7
+ let(:broken_yaml_config) { Cluster::Config::File.new(support_dir + 'broken_yaml_config.yml') }
8
+ let(:non_hash_config) { Cluster::Config::File.new(support_dir + 'non_hash_config.yml') }
9
+ let(:empty_config) { Cluster::Config::File.new(support_dir + 'empty_config.yml') }
10
+ let(:missing_config) { Cluster::Config::File.new(support_dir + 'missing') }
11
+
12
+ subject { Cluster::Config::Verifier.new(configs) }
13
+
14
+ shared_examples_for 'valid config' do
15
+ it 'verifies' do
16
+ expect(subject).to be_verified
17
+ end
18
+ end
19
+
20
+ shared_examples_for 'invalid config' do
21
+ it "doesn't verify" do
22
+ expect(subject).not_to be_verified
23
+ end
24
+ end
25
+
26
+ context 'with valid configs' do
27
+ let(:configs) { [valid_local_config, valid_global_config] }
28
+
29
+ it_behaves_like 'valid config'
30
+ end
31
+
32
+ context 'with a single config file' do
33
+ let(:configs) { [valid_local_config] }
34
+
35
+ it_behaves_like 'valid config'
36
+ end
37
+
38
+ context 'with a missing config' do
39
+ let(:configs) { [missing_config] }
40
+
41
+ it_behaves_like 'invalid config'
42
+
43
+ it 'reports the error' do
44
+ subject.verified?
45
+
46
+ expect(subject.configs.flat_map { |config| config.errors.to_a }).to include("Configuration file doesn't exist")
47
+ end
48
+ end
49
+
50
+ context 'with a non-hash config' do
51
+ let(:configs) { [non_hash_config] }
52
+
53
+ it_behaves_like 'invalid config'
54
+
55
+ it 'reports the error' do
56
+ subject.verified?
57
+
58
+ expect(subject.configs.flat_map { |config| config.errors.to_a }).to include("Parsed config as invalid type: expected Hash, got FalseClass")
59
+ end
60
+ end
61
+
62
+ context 'with an empty config' do
63
+ let(:configs) { [empty_config] }
64
+
65
+ it_behaves_like 'invalid config'
66
+
67
+ it 'reports the error' do
68
+ subject.verified?
69
+
70
+ expect(subject.configs.flat_map { |config| config.errors.to_a }).to include("Config file is empty")
71
+ end
72
+ end
73
+
74
+ context 'with broken YAML' do
75
+ let(:configs) { [broken_yaml_config] }
76
+
77
+ it_behaves_like 'invalid config'
78
+
79
+ it 'reports the error' do
80
+ subject.verified?
81
+
82
+ expect(subject.configs.flat_map { |config| config.errors.to_a }).to include('(<unknown>): did not find expected comment or line break while scanning a block scalar at line 1 column 1')
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,194 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ RSpec.describe Resque::Cluster::Config do
4
+ let(:redis) { Resque.redis }
5
+
6
+ before do
7
+ config.verified?
8
+
9
+ Resque::Cluster.config = {
10
+ cluster_name: 'unit-test-cluster',
11
+ environment: 'unit-test'
12
+ }
13
+ end
14
+
15
+ shared_examples_for 'a valid config' do
16
+ it 'is verified' do
17
+ expect(config).to be_verified
18
+ end
19
+
20
+ it "gru_format should return a correct hash" do
21
+ expect(config.gru_format).to eql(correct_hash)
22
+ end
23
+ end
24
+
25
+ describe 'with a single config file' do
26
+ let(:config) { Resque::Cluster::Config.new(config_path) }
27
+ let(:correct_hash) do
28
+ {
29
+ cluster_maximums: { 'foo' => 2, 'bar' => 50, "foo,bar,baz" => 1 },
30
+ host_maximums: { 'foo' => 1, 'bar' => 9, "foo,bar,baz" => 1 },
31
+ client_settings: redis.client.options,
32
+ rebalance_flag: true,
33
+ presume_host_dead_after: 60,
34
+ max_workers_per_host: 10,
35
+ cluster_name: "unit-test-cluster",
36
+ environment_name: "unit-test",
37
+ manage_worker_heartbeats: true,
38
+ version_hash: `git rev-parse --verify HEAD`.strip
39
+ }
40
+ end
41
+
42
+ context 'old style' do
43
+ let(:config_path) { support_dir + 'valid_single_old_config.yml' }
44
+
45
+ it_behaves_like 'a valid config'
46
+
47
+ context 'with a missing local maximum' do
48
+ let(:config) { Resque::Cluster::Config.new(config_path) }
49
+ let(:config_path) { support_dir + 'single_old_missing_local.yml' }
50
+
51
+ it 'should not be verified' do
52
+ expect(config).not_to be_verified
53
+ end
54
+
55
+ it 'should not be verified' do
56
+ expect(config.errors).to contain_exactly("Every worker configuration must contain a local and a global maximum.")
57
+ end
58
+ end
59
+
60
+ context 'with a missing global maximum' do
61
+ let(:config) { Resque::Cluster::Config.new(config_path) }
62
+ let(:config_path) { support_dir + 'single_old_missing_global.yml' }
63
+
64
+ it 'should not be verified' do
65
+ expect(config).not_to be_verified
66
+ end
67
+
68
+ it 'should not be verified' do
69
+ expect(config.errors).to contain_exactly("Every worker configuration must contain a local and a global maximum.")
70
+ end
71
+ end
72
+ end
73
+
74
+ context 'new style' do
75
+ let(:config_path) { support_dir + 'valid_single_new_config.yml' }
76
+
77
+ it_behaves_like 'a valid config'
78
+
79
+ context 'with a missing local maximum' do
80
+ let(:config) { Resque::Cluster::Config.new(config_path) }
81
+ let(:config_path) { support_dir + 'missing_local_maximum.yml' }
82
+
83
+ it 'should not be verified' do
84
+ expect(config).not_to be_verified
85
+ end
86
+
87
+ it 'should not be verified' do
88
+ expect(config.errors).to contain_exactly("Every worker configuration must contain a local and a global maximum.")
89
+ end
90
+ end
91
+
92
+ context 'with a missing global maximum' do
93
+ let(:config) { Resque::Cluster::Config.new(config_path) }
94
+ let(:config_path) { support_dir + 'missing_global_maximum.yml' }
95
+
96
+ it 'should not be verified' do
97
+ expect(config).not_to be_verified
98
+ end
99
+
100
+ it 'should not be verified' do
101
+ expect(config.errors).to contain_exactly("Every worker configuration must contain a local and a global maximum.")
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ describe 'separate config files' do
108
+ context "with valid config files" do
109
+ let(:config) { Resque::Cluster::Config.new(local_config_path, global_config_path) }
110
+ let(:local_config_path) { File.expand_path(File.dirname(__FILE__) + '/../local_config.yml') }
111
+ let(:global_config_path) { File.expand_path(File.dirname(__FILE__) + '/../global_config.yml') }
112
+
113
+ let(:correct_hash) do
114
+ {
115
+ cluster_maximums: { 'foo' => 2, 'bar' => 50, "foo,bar,baz" => 1 },
116
+ host_maximums: { 'foo' => 1, 'bar' => 9, "foo,bar,baz" => 1 },
117
+ client_settings: redis.client.options,
118
+ rebalance_flag: false,
119
+ presume_host_dead_after: 120,
120
+ max_workers_per_host: nil,
121
+ cluster_name: "unit-test-cluster",
122
+ environment_name: "unit-test",
123
+ manage_worker_heartbeats: true,
124
+ version_hash: `git rev-parse --verify HEAD`.strip
125
+ }
126
+ end
127
+
128
+ it_behaves_like 'a valid config'
129
+
130
+ it "config should have no warnings or errors" do
131
+ expect(config.errors.count).to eql(0)
132
+ expect(config.warnings.count).to eql(0)
133
+ end
134
+
135
+ it "git_version_hash should be set" do
136
+ expect(config.version_git_hash).to eql(`git rev-parse --verify HEAD`.strip)
137
+ end
138
+ end
139
+
140
+ context 'with a missing local maximum' do
141
+ let(:config) { Resque::Cluster::Config.new(local_config_path, global_config_path) }
142
+ let(:local_config_path) { support_dir + 'separate_missing_local.yml' }
143
+ let(:global_config_path) { File.expand_path(File.dirname(__FILE__) + '/../global_config.yml') }
144
+
145
+ it 'should not be verified' do
146
+ expect(config).not_to be_verified
147
+ end
148
+
149
+ it 'should not be verified' do
150
+ expect(config.errors).to contain_exactly("Every worker configuration must contain a local and a global maximum.")
151
+ end
152
+ end
153
+
154
+ context 'with a missing global maximum' do
155
+ let(:config) { Resque::Cluster::Config.new(local_config_path, global_config_path) }
156
+ let(:local_config_path) { File.expand_path(File.dirname(__FILE__) + '/../local_config.yml') }
157
+ let(:global_config_path) { support_dir + 'separate_missing_global.yml' }
158
+
159
+ it 'should not be verified' do
160
+ expect(config).not_to be_verified
161
+ end
162
+
163
+ it 'should not be verified' do
164
+ expect(config.errors).to contain_exactly("Every worker configuration must contain a local and a global maximum.")
165
+ end
166
+ end
167
+
168
+ context "with invalid config file" do
169
+ let(:local_config_path) { File.expand_path(File.dirname(__FILE__) + '/../local_configuration.yml') }
170
+ let(:global_config_path) { File.expand_path(File.dirname(__FILE__) + '/../../README.rdoc') }
171
+ let(:config) { Resque::Cluster::Config.new(local_config_path, global_config_path) }
172
+ let(:correct_hash) { {} }
173
+
174
+ it "should not be verified" do
175
+ expect(config.verified?).to eql(false)
176
+ end
177
+
178
+ it "config should have no warnings but 2 errors" do
179
+ expect(config.errors.count).to eql(1)
180
+ expect(config.warnings.count).to eql(0)
181
+ expect(config.errors).to contain_exactly(
182
+ "#{local_config_path}: Configuration file doesn't exist")
183
+ end
184
+
185
+ it "gru_format should return a an empty hash" do
186
+ expect(config.gru_format).to eql(correct_hash)
187
+ end
188
+
189
+ it "git_version_hash should not be set" do
190
+ expect(config.version_git_hash).to be_nil
191
+ end
192
+ end
193
+ end
194
+ end