knapsack_pro 0.17.0 → 0.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/README.md +151 -2
  4. data/bin/knapsack_pro +1 -0
  5. data/knapsack_pro.gemspec +1 -1
  6. data/lib/knapsack_pro.rb +8 -0
  7. data/lib/knapsack_pro/adapters/base_adapter.rb +10 -0
  8. data/lib/knapsack_pro/adapters/rspec_adapter.rb +8 -0
  9. data/lib/knapsack_pro/allocator.rb +28 -14
  10. data/lib/knapsack_pro/allocator_builder.rb +1 -29
  11. data/lib/knapsack_pro/base_allocator_builder.rb +35 -0
  12. data/lib/knapsack_pro/client/api/v1/queues.rb +27 -0
  13. data/lib/knapsack_pro/config/ci/base.rb +3 -0
  14. data/lib/knapsack_pro/config/ci/buildkite.rb +4 -0
  15. data/lib/knapsack_pro/config/ci/circle.rb +4 -1
  16. data/lib/knapsack_pro/config/ci/semaphore.rb +4 -0
  17. data/lib/knapsack_pro/config/ci/snap_ci.rb +4 -0
  18. data/lib/knapsack_pro/config/ci/travis.rb +4 -0
  19. data/lib/knapsack_pro/config/env.rb +22 -0
  20. data/lib/knapsack_pro/config/env_generator.rb +19 -0
  21. data/lib/knapsack_pro/queue_allocator.rb +52 -0
  22. data/lib/knapsack_pro/queue_allocator_builder.rb +13 -0
  23. data/lib/knapsack_pro/report.rb +35 -0
  24. data/lib/knapsack_pro/runners/queue/base_runner.rb +34 -0
  25. data/lib/knapsack_pro/runners/queue/rspec_runner.rb +42 -0
  26. data/lib/knapsack_pro/task_loader.rb +1 -1
  27. data/lib/knapsack_pro/version.rb +1 -1
  28. data/lib/tasks/queue/rspec.rake +9 -0
  29. data/lib/tasks/salt.rake +0 -2
  30. data/spec/knapsack_pro/adapters/base_adapter_spec.rb +39 -8
  31. data/spec/knapsack_pro/adapters/rspec_adapter_spec.rb +11 -0
  32. data/spec/knapsack_pro/allocator_builder_spec.rb +2 -12
  33. data/spec/knapsack_pro/base_allocator_builder_spec.rb +22 -0
  34. data/spec/knapsack_pro/client/api/v1/queues_spec.rb +42 -0
  35. data/spec/knapsack_pro/config/ci/base_spec.rb +1 -0
  36. data/spec/knapsack_pro/config/ci/buildkite_spec.rb +13 -0
  37. data/spec/knapsack_pro/config/ci/circle_spec.rb +13 -0
  38. data/spec/knapsack_pro/config/ci/semaphore_spec.rb +13 -0
  39. data/spec/knapsack_pro/config/ci/snap_ci_spec.rb +13 -0
  40. data/spec/knapsack_pro/config/ci/travis_spec.rb +13 -0
  41. data/spec/knapsack_pro/config/env_generator_spec.rb +52 -0
  42. data/spec/knapsack_pro/config/env_spec.rb +90 -0
  43. data/spec/knapsack_pro/queue_allocator_builder_spec.rb +38 -0
  44. data/spec/knapsack_pro/queue_allocator_spec.rb +85 -0
  45. data/spec/knapsack_pro/report_spec.rb +69 -0
  46. data/spec/knapsack_pro/runners/queue/base_runner_spec.rb +67 -0
  47. data/spec/knapsack_pro/runners/queue/rspec_runner_spec.rb +118 -0
  48. metadata +26 -4
@@ -112,5 +112,16 @@ describe KnapsackPro::Adapters::RSpecAdapter do
112
112
  subject.bind_save_report
113
113
  end
114
114
  end
115
+
116
+ describe '#bind_save_queue_report' do
117
+ it do
118
+ expect(config).to receive(:after).with(:suite).and_yield
119
+ expect(::RSpec).to receive(:configure).and_yield(config)
120
+
121
+ expect(KnapsackPro::Report).to receive(:save_subset_queue_to_file)
122
+
123
+ subject.bind_save_queue_report
124
+ end
125
+ end
115
126
  end
116
127
  end
@@ -22,24 +22,14 @@ describe KnapsackPro::AllocatorBuilder do
22
22
  ci_node_index = double
23
23
  expect(KnapsackPro::Config::Env).to receive(:ci_node_index).and_return(ci_node_index)
24
24
 
25
- expect(KnapsackPro::Allocator).to receive(:new).with({
25
+ expect(KnapsackPro::Allocator).to receive(:new).with(
26
26
  test_files: test_files,
27
27
  ci_node_total: ci_node_total,
28
28
  ci_node_index: ci_node_index,
29
29
  repository_adapter: repository_adapter,
30
- }).and_return(allocator)
30
+ ).and_return(allocator)
31
31
  end
32
32
 
33
33
  it { should eq allocator }
34
34
  end
35
-
36
- describe '#test_dir' do
37
- subject { allocator_builder.test_dir }
38
-
39
- before do
40
- expect(KnapsackPro::TestFilePattern).to receive(:call).and_return('spec/**{,/*/**}/*_spec.rb')
41
- end
42
-
43
- it { should eq 'spec' }
44
- end
45
35
  end
@@ -0,0 +1,22 @@
1
+ describe KnapsackPro::BaseAllocatorBuilder do
2
+ let(:adapter_class) { KnapsackPro::Adapters::BaseAdapter }
3
+ let(:allocator_builder) { described_class.new(adapter_class) }
4
+
5
+ describe '#allocator' do
6
+ subject { allocator_builder.allocator }
7
+
8
+ it do
9
+ expect { subject }.to raise_error(NotImplementedError)
10
+ end
11
+ end
12
+
13
+ describe '#test_dir' do
14
+ subject { allocator_builder.test_dir }
15
+
16
+ before do
17
+ expect(KnapsackPro::TestFilePattern).to receive(:call).with(adapter_class).and_return('spec/**{,/*/**}/*_spec.rb')
18
+ end
19
+
20
+ it { should eq 'spec' }
21
+ end
22
+ end
@@ -0,0 +1,42 @@
1
+ describe KnapsackPro::Client::API::V1::Queues do
2
+ describe '.queue' do
3
+ let(:can_initialize_queue) { double }
4
+ let(:commit_hash) { double }
5
+ let(:branch) { double }
6
+ let(:node_total) { double }
7
+ let(:node_index) { double }
8
+ let(:test_files) { double }
9
+
10
+ subject do
11
+ described_class.queue(
12
+ can_initialize_queue: can_initialize_queue,
13
+ commit_hash: commit_hash,
14
+ branch: branch,
15
+ node_total: node_total,
16
+ node_index: node_index,
17
+ test_files: test_files
18
+ )
19
+ end
20
+
21
+ it do
22
+ node_build_id = double
23
+ expect(KnapsackPro::Config::Env).to receive(:ci_node_build_id).and_return(node_build_id)
24
+
25
+ action = double
26
+ expect(KnapsackPro::Client::API::Action).to receive(:new).with({
27
+ endpoint_path: '/v1/queues/queue',
28
+ http_method: :post,
29
+ request_hash: {
30
+ can_initialize_queue: can_initialize_queue,
31
+ commit_hash: commit_hash,
32
+ branch: branch,
33
+ node_total: node_total,
34
+ node_index: node_index,
35
+ node_build_id: node_build_id,
36
+ test_files: test_files
37
+ }
38
+ }).and_return(action)
39
+ expect(subject).to eq action
40
+ end
41
+ end
42
+ end
@@ -1,6 +1,7 @@
1
1
  describe KnapsackPro::Config::CI::Base do
2
2
  its(:node_total) { should be nil }
3
3
  its(:node_index) { should be nil }
4
+ its(:node_build_id) { should be nil }
4
5
  its(:commit_hash) { should be nil }
5
6
  its(:branch) { should be nil }
6
7
  its(:project_dir) { should be nil }
@@ -33,6 +33,19 @@ describe KnapsackPro::Config::CI::Buildkite do
33
33
  end
34
34
  end
35
35
 
36
+ describe '#node_build_id' do
37
+ subject { described_class.new.node_build_id }
38
+
39
+ context 'when environment exists' do
40
+ let(:env) { { 'BUILDKITE_BUILD_NUMBER' => 1514 } }
41
+ it { should eql 1514 }
42
+ end
43
+
44
+ context "when environment doesn't exist" do
45
+ it { should be nil }
46
+ end
47
+ end
48
+
36
49
  describe '#commit_hash' do
37
50
  subject { described_class.new.commit_hash }
38
51
 
@@ -33,6 +33,19 @@ describe KnapsackPro::Config::CI::Circle do
33
33
  end
34
34
  end
35
35
 
36
+ describe '#node_build_id' do
37
+ subject { described_class.new.node_build_id }
38
+
39
+ context 'when environment exists' do
40
+ let(:env) { { 'CIRCLE_BUILD_NUM' => 123 } }
41
+ it { should eql 123 }
42
+ end
43
+
44
+ context "when environment doesn't exist" do
45
+ it { should be nil }
46
+ end
47
+ end
48
+
36
49
  describe '#commit_hash' do
37
50
  subject { described_class.new.commit_hash }
38
51
 
@@ -33,6 +33,19 @@ describe KnapsackPro::Config::CI::Semaphore do
33
33
  end
34
34
  end
35
35
 
36
+ describe '#node_build_id' do
37
+ subject { described_class.new.node_build_id }
38
+
39
+ context 'when environment exists' do
40
+ let(:env) { { 'SEMAPHORE_BUILD_NUMBER' => 23 } }
41
+ it { should eql 23 }
42
+ end
43
+
44
+ context "when environment doesn't exist" do
45
+ it { should be nil }
46
+ end
47
+ end
48
+
36
49
  describe '#commit_hash' do
37
50
  subject { described_class.new.commit_hash }
38
51
 
@@ -33,6 +33,19 @@ describe KnapsackPro::Config::CI::SnapCI do
33
33
  end
34
34
  end
35
35
 
36
+ describe '#node_build_id' do
37
+ subject { described_class.new.node_build_id }
38
+
39
+ context 'when environment exists' do
40
+ let(:env) { { 'SNAP_PIPELINE_COUNTER' => 123 } }
41
+ it { should eql 123 }
42
+ end
43
+
44
+ context "when environment doesn't exist" do
45
+ it { should be nil }
46
+ end
47
+ end
48
+
36
49
  describe '#commit_hash' do
37
50
  subject { described_class.new.commit_hash }
38
51
 
@@ -19,6 +19,19 @@ describe KnapsackPro::Config::CI::Travis do
19
19
  it { should be nil }
20
20
  end
21
21
 
22
+ describe '#node_build_id' do
23
+ subject { described_class.new.node_build_id }
24
+
25
+ context 'when environment exists' do
26
+ let(:env) { { 'TRAVIS_BUILD_NUMBER' => 4 } }
27
+ it { should eql 4 }
28
+ end
29
+
30
+ context "when environment doesn't exist" do
31
+ it { should be nil }
32
+ end
33
+ end
34
+
22
35
  describe '#commit_hash' do
23
36
  subject { described_class.new.commit_hash }
24
37
 
@@ -0,0 +1,52 @@
1
+ describe KnapsackPro::Config::EnvGenerator do
2
+ describe '.set_queue_id' do
3
+ subject { described_class.set_queue_id }
4
+
5
+ context 'when queue id exists' do
6
+ before do
7
+ stub_const("ENV", { 'KNAPSACK_PRO_QUEUE_ID' => 'fake-queue-id' })
8
+ end
9
+
10
+ it do
11
+ expect { subject }.to raise_error('Queue ID already generated.')
12
+ end
13
+ end
14
+
15
+ context "when queue id doesn't exist" do
16
+ before { stub_const("ENV", {}) }
17
+
18
+ it do
19
+ subject
20
+ expect(ENV['KNAPSACK_PRO_QUEUE_ID']).not_to be_nil
21
+ end
22
+
23
+ it do
24
+ now = DateTime.new(2016, 1, 9, 0, 0, 0)
25
+
26
+ Timecop.freeze(now) do
27
+ uuid = 'fake-uuid'
28
+ expect(SecureRandom).to receive(:uuid).and_return(uuid)
29
+
30
+ subject
31
+
32
+ expect(ENV['KNAPSACK_PRO_QUEUE_ID']).to eq '1452297600_fake-uuid'
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ describe '.set_subset_queue_id' do
39
+ subject { described_class.set_subset_queue_id }
40
+
41
+ before { stub_const("ENV", {}) }
42
+
43
+ it do
44
+ uuid = 'fake-uuid'
45
+ expect(SecureRandom).to receive(:uuid).and_return(uuid)
46
+
47
+ subject
48
+
49
+ expect(ENV['KNAPSACK_PRO_SUBSET_QUEUE_ID']).to eq uuid
50
+ end
51
+ end
52
+ end
@@ -47,6 +47,29 @@ describe KnapsackPro::Config::Env do
47
47
  end
48
48
  end
49
49
 
50
+ describe '.ci_node_build_id' do
51
+ subject { described_class.ci_node_build_id }
52
+
53
+ context 'when ENV exists' do
54
+ context 'when KNAPSACK_PRO_CI_NODE_BUILD_ID has value' do
55
+ before { stub_const("ENV", { 'KNAPSACK_PRO_CI_NODE_BUILD_ID' => '7' }) }
56
+ it { should eq '7' }
57
+ end
58
+
59
+ context 'when CI environment has value' do
60
+ before do
61
+ expect(described_class).to receive(:ci_env_for).with(:node_build_id).and_return('8')
62
+ end
63
+
64
+ it { should eq '8' }
65
+ end
66
+ end
67
+
68
+ context "when ENV doesn't exist" do
69
+ it { should eq 'missing-build-id' }
70
+ end
71
+ end
72
+
50
73
  describe '.commit_hash' do
51
74
  subject { described_class.commit_hash }
52
75
 
@@ -178,6 +201,73 @@ describe KnapsackPro::Config::Env do
178
201
  end
179
202
  end
180
203
 
204
+ describe '.queue_recording_enabled' do
205
+ subject { described_class.queue_recording_enabled }
206
+
207
+ context 'when ENV exists' do
208
+ let(:queue_recording_enabled) { 'true' }
209
+ before { stub_const("ENV", { 'KNAPSACK_PRO_QUEUE_RECORDING_ENABLED' => queue_recording_enabled }) }
210
+ it { should eq queue_recording_enabled }
211
+ end
212
+
213
+ context "when ENV doesn't exist" do
214
+ it { should be_nil }
215
+ end
216
+ end
217
+
218
+ describe '.queue_recording_enabled?' do
219
+ subject { described_class.queue_recording_enabled? }
220
+
221
+ before do
222
+ expect(described_class).to receive(:queue_recording_enabled).and_return(queue_recording_enabled)
223
+ end
224
+
225
+ context 'when enabled' do
226
+ let(:queue_recording_enabled) { 'true' }
227
+
228
+ it { should be true }
229
+ end
230
+
231
+ context 'when disabled' do
232
+ let(:queue_recording_enabled) { nil }
233
+
234
+ it { should be false }
235
+ end
236
+ end
237
+
238
+ describe '.queue_id' do
239
+ subject { described_class.queue_id }
240
+
241
+ context 'when ENV exists' do
242
+ let(:queue_id) { 'fake-queue-id' }
243
+ before { stub_const("ENV", { 'KNAPSACK_PRO_QUEUE_ID' => queue_id }) }
244
+ it { should eq queue_id }
245
+ end
246
+
247
+ context "when ENV doesn't exist" do
248
+ before { stub_const("ENV", {}) }
249
+ it do
250
+ expect { subject }.to raise_error('Missing Queue ID')
251
+ end
252
+ end
253
+ end
254
+
255
+ describe '.subset_queue_id' do
256
+ subject { described_class.subset_queue_id }
257
+
258
+ context 'when ENV exists' do
259
+ let(:subset_queue_id) { 'fake-subset-queue-id' }
260
+ before { stub_const("ENV", { 'KNAPSACK_PRO_SUBSET_QUEUE_ID' => subset_queue_id }) }
261
+ it { should eq subset_queue_id }
262
+ end
263
+
264
+ context "when ENV doesn't exist" do
265
+ it do
266
+ expect { subject }.to raise_error('Missing Subset Queue ID')
267
+ end
268
+ end
269
+ end
270
+
181
271
  describe '.test_files_encrypted' do
182
272
  subject { described_class.test_files_encrypted }
183
273
 
@@ -0,0 +1,38 @@
1
+ describe KnapsackPro::QueueAllocatorBuilder do
2
+ let(:adapter_class) { KnapsackPro::Adapters::BaseAdapter }
3
+ let(:allocator_builder) { described_class.new(adapter_class) }
4
+
5
+ describe '#allocator' do
6
+ let(:allocator) { double }
7
+
8
+ subject { allocator_builder.allocator }
9
+
10
+ before do
11
+ test_file_pattern = double
12
+ expect(KnapsackPro::TestFilePattern).to receive(:call).with(adapter_class).and_return(test_file_pattern)
13
+
14
+ test_files = double
15
+ expect(KnapsackPro::TestFileFinder).to receive(:call).with(test_file_pattern).and_return(test_files)
16
+
17
+ repository_adapter = double
18
+ expect(KnapsackPro::RepositoryAdapterInitiator).to receive(:call).and_return(repository_adapter)
19
+
20
+ ci_node_total = double
21
+ expect(KnapsackPro::Config::Env).to receive(:ci_node_total).and_return(ci_node_total)
22
+ ci_node_index = double
23
+ expect(KnapsackPro::Config::Env).to receive(:ci_node_index).and_return(ci_node_index)
24
+ ci_node_build_id = double
25
+ expect(KnapsackPro::Config::Env).to receive(:ci_node_build_id).and_return(ci_node_build_id)
26
+
27
+ expect(KnapsackPro::QueueAllocator).to receive(:new).with(
28
+ test_files: test_files,
29
+ ci_node_total: ci_node_total,
30
+ ci_node_index: ci_node_index,
31
+ ci_node_build_id: ci_node_build_id,
32
+ repository_adapter: repository_adapter,
33
+ ).and_return(allocator)
34
+ end
35
+
36
+ it { should eq allocator }
37
+ end
38
+ end
@@ -0,0 +1,85 @@
1
+ describe KnapsackPro::QueueAllocator do
2
+ let(:test_files) { double }
3
+ let(:ci_node_total) { double }
4
+ let(:ci_node_index) { double }
5
+ let(:ci_node_build_id) { double }
6
+ let(:repository_adapter) { instance_double(KnapsackPro::RepositoryAdapters::EnvAdapter, commit_hash: double, branch: double) }
7
+
8
+ let(:queue_allocator) do
9
+ described_class.new(
10
+ test_files: test_files,
11
+ ci_node_total: ci_node_total,
12
+ ci_node_index: ci_node_index,
13
+ ci_node_build_id: ci_node_build_id,
14
+ repository_adapter: repository_adapter
15
+ )
16
+ end
17
+
18
+ describe '#test_file_paths' do
19
+ let(:can_initialize_queue) { double }
20
+ let(:response) { double }
21
+
22
+ subject { queue_allocator.test_file_paths(can_initialize_queue) }
23
+
24
+ before do
25
+ encrypted_test_files = double
26
+ expect(KnapsackPro::Crypto::Encryptor).to receive(:call).with(test_files).and_return(encrypted_test_files)
27
+
28
+ action = double
29
+ expect(KnapsackPro::Client::API::V1::Queues).to receive(:queue).with(
30
+ can_initialize_queue: can_initialize_queue,
31
+ commit_hash: repository_adapter.commit_hash,
32
+ branch: repository_adapter.branch,
33
+ node_total: ci_node_total,
34
+ node_index: ci_node_index,
35
+ node_build_id: ci_node_build_id,
36
+ test_files: encrypted_test_files,
37
+ ).and_return(action)
38
+
39
+ connection = instance_double(KnapsackPro::Client::Connection,
40
+ call: response,
41
+ success?: success?,
42
+ errors?: errors?)
43
+ expect(KnapsackPro::Client::Connection).to receive(:new).with(action).and_return(connection)
44
+ end
45
+
46
+ context 'when successful request to API' do
47
+ let(:success?) { true }
48
+
49
+ context 'when response has errors' do
50
+ let(:errors?) { true }
51
+
52
+ it do
53
+ expect { subject }.to raise_error(ArgumentError)
54
+ end
55
+ end
56
+
57
+ context 'when response has no errors' do
58
+ let(:errors?) { false }
59
+ let(:response) do
60
+ {
61
+ 'test_files' => [
62
+ { 'path' => 'a_spec.rb' },
63
+ { 'path' => 'b_spec.rb' },
64
+ ]
65
+ }
66
+ end
67
+
68
+ before do
69
+ expect(KnapsackPro::Crypto::Decryptor).to receive(:call).with(test_files, response['test_files']).and_call_original
70
+ end
71
+
72
+ it { should eq ['a_spec.rb', 'b_spec.rb'] }
73
+ end
74
+ end
75
+
76
+ context 'when not successful request to API' do
77
+ let(:success?) { false }
78
+ let(:errors?) { false }
79
+
80
+ it do
81
+ expect { subject }.to raise_error("Couldn't connect with Knapsack Pro API. Response: #{response}")
82
+ end
83
+ end
84
+ end
85
+ end