knapsack_pro 3.4.2 → 3.6.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/lib/knapsack_pro/allocator.rb +19 -3
- data/lib/knapsack_pro/client/api/v1/build_distributions.rb +19 -8
- data/lib/knapsack_pro/hooks/queue.rb +20 -0
- data/lib/knapsack_pro/queue_allocator.rb +0 -1
- data/lib/knapsack_pro/runners/queue/cucumber_runner.rb +2 -0
- data/lib/knapsack_pro/runners/queue/minitest_runner.rb +2 -0
- data/lib/knapsack_pro/runners/queue/rspec_runner.rb +2 -0
- data/lib/knapsack_pro/version.rb +1 -1
- data/spec/fixtures/vcr_cassettes/api/v1/build_distributions/subset/invalid_test_suite_token.yml +25 -16
- data/spec/fixtures/vcr_cassettes/api/v1/build_distributions/subset/success.yml +27 -18
- data/spec/integration/api/build_distributions_subset_spec.rb +1 -0
- data/spec/knapsack_pro/allocator_spec.rb +140 -57
- data/spec/knapsack_pro/client/api/v1/build_distributions_spec.rb +46 -15
- data/spec/knapsack_pro/hooks/queue_spec.rb +40 -0
- data/spec/knapsack_pro/queue_allocator_spec.rb +2 -5
- data/spec/knapsack_pro/runners/queue/cucumber_runner_spec.rb +2 -0
- data/spec/knapsack_pro/runners/queue/minitest_runner_spec.rb +1 -0
- data/spec/knapsack_pro/runners/queue/rspec_runner_spec.rb +2 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba65a6353675fce4c06920c5dca6b7dcc90aee4a978db714f28bf9305f5208e3
|
4
|
+
data.tar.gz: 345aa8cf66464faeeb05cc3adcd2fcf904862aa02ea3f6a0109ca83f37c7c332
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c12d293d3ac2ca00f33e5802f4c6cf0c8f9ef08dd76d5217892efcb14a8bc090b6f5a118514c79a7066baf8449c3b2406e7b6d4a867b4b479a9bda841d057dbf
|
7
|
+
data.tar.gz: 757a63fd917a7fc685659fe6d64d01b19800fa3cc81b4f66d7e9fd08ed9631b28c727544577bae13faae3e54c3b1b3fb3624b2d69e6b74287887c3cb1fb95c44
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,21 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
### 3.6.0
|
4
|
+
|
5
|
+
* Add an attempt to read from the cache for Regular Mode API
|
6
|
+
|
7
|
+
https://github.com/KnapsackPro/knapsack_pro-ruby/pull/182
|
8
|
+
|
9
|
+
https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v3.5.0...v3.6.0
|
10
|
+
|
11
|
+
### 3.5.0
|
12
|
+
|
13
|
+
* Add the `KnapsackPro::Hooks::Queue.before_subset_queue` hook in Queue Mode
|
14
|
+
|
15
|
+
https://github.com/KnapsackPro/knapsack_pro-ruby/pull/183
|
16
|
+
|
17
|
+
https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v3.4.2...v3.5.0
|
18
|
+
|
3
19
|
### 3.4.2
|
4
20
|
|
5
21
|
* Fix: Load `rspec/core` in Regular Mode when using RSpec split by test examples feature
|
@@ -9,8 +9,18 @@ module KnapsackPro
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def test_file_paths
|
12
|
-
|
12
|
+
action = build_action(cache_read_attempt: true)
|
13
|
+
connection = KnapsackPro::Client::Connection.new(action)
|
13
14
|
response = connection.call
|
15
|
+
|
16
|
+
# when a cache miss because the test suite split was not cached yet
|
17
|
+
if connection.success? && connection.api_code == KnapsackPro::Client::API::V1::BuildDistributions::TEST_SUITE_SPLIT_CACHE_MISS_CODE
|
18
|
+
# make an attempt to initalize a new test suite split on the API side
|
19
|
+
action = build_action(cache_read_attempt: false)
|
20
|
+
connection = KnapsackPro::Client::Connection.new(action)
|
21
|
+
response = connection.call
|
22
|
+
end
|
23
|
+
|
14
24
|
if connection.success?
|
15
25
|
raise ArgumentError.new(response) if connection.errors?
|
16
26
|
prepare_test_files(response)
|
@@ -47,13 +57,19 @@ module KnapsackPro
|
|
47
57
|
KnapsackPro::Crypto::BranchEncryptor.call(repository_adapter.branch)
|
48
58
|
end
|
49
59
|
|
50
|
-
def build_action
|
60
|
+
def build_action(cache_read_attempt:)
|
61
|
+
test_files =
|
62
|
+
unless cache_read_attempt
|
63
|
+
encrypted_test_files
|
64
|
+
end
|
65
|
+
|
51
66
|
KnapsackPro::Client::API::V1::BuildDistributions.subset(
|
67
|
+
cache_read_attempt: cache_read_attempt,
|
52
68
|
commit_hash: repository_adapter.commit_hash,
|
53
69
|
branch: encrypted_branch,
|
54
70
|
node_total: ci_node_total,
|
55
71
|
node_index: ci_node_index,
|
56
|
-
test_files:
|
72
|
+
test_files: test_files,
|
57
73
|
)
|
58
74
|
end
|
59
75
|
|
@@ -3,19 +3,30 @@ module KnapsackPro
|
|
3
3
|
module API
|
4
4
|
module V1
|
5
5
|
class BuildDistributions < Base
|
6
|
+
TEST_SUITE_SPLIT_CACHE_MISS_CODE = 'TEST_SUITE_SPLIT_CACHE_MISS'
|
7
|
+
|
6
8
|
class << self
|
7
9
|
def subset(args)
|
10
|
+
request_hash = {
|
11
|
+
:fixed_test_suite_split => KnapsackPro::Config::Env.fixed_test_suite_split,
|
12
|
+
:cache_read_attempt => args.fetch(:cache_read_attempt),
|
13
|
+
:commit_hash => args.fetch(:commit_hash),
|
14
|
+
:branch => args.fetch(:branch),
|
15
|
+
:node_total => args.fetch(:node_total),
|
16
|
+
:node_index => args.fetch(:node_index),
|
17
|
+
:ci_build_id => KnapsackPro::Config::Env.ci_node_build_id,
|
18
|
+
}
|
19
|
+
|
20
|
+
unless request_hash[:cache_read_attempt]
|
21
|
+
request_hash.merge!({
|
22
|
+
:test_files => args.fetch(:test_files)
|
23
|
+
})
|
24
|
+
end
|
25
|
+
|
8
26
|
action_class.new(
|
9
27
|
endpoint_path: '/v1/build_distributions/subset',
|
10
28
|
http_method: :post,
|
11
|
-
request_hash:
|
12
|
-
:fixed_test_suite_split => KnapsackPro::Config::Env.fixed_test_suite_split,
|
13
|
-
:commit_hash => args.fetch(:commit_hash),
|
14
|
-
:branch => args.fetch(:branch),
|
15
|
-
:node_total => args.fetch(:node_total),
|
16
|
-
:node_index => args.fetch(:node_index),
|
17
|
-
:test_files => args.fetch(:test_files)
|
18
|
-
}
|
29
|
+
request_hash: request_hash
|
19
30
|
)
|
20
31
|
end
|
21
32
|
|
@@ -3,6 +3,7 @@ module KnapsackPro
|
|
3
3
|
class Queue
|
4
4
|
class << self
|
5
5
|
attr_reader :before_queue_store,
|
6
|
+
:before_subset_queue_store,
|
6
7
|
:after_subset_queue_store,
|
7
8
|
:after_queue_store
|
8
9
|
|
@@ -10,6 +11,10 @@ module KnapsackPro
|
|
10
11
|
@before_queue_store = nil
|
11
12
|
end
|
12
13
|
|
14
|
+
def reset_before_subset_queue
|
15
|
+
@before_subset_queue_store = nil
|
16
|
+
end
|
17
|
+
|
13
18
|
def reset_after_subset_queue
|
14
19
|
@after_subset_queue_store = nil
|
15
20
|
end
|
@@ -23,6 +28,11 @@ module KnapsackPro
|
|
23
28
|
@before_queue_store << block
|
24
29
|
end
|
25
30
|
|
31
|
+
def before_subset_queue(&block)
|
32
|
+
@before_subset_queue_store ||= []
|
33
|
+
@before_subset_queue_store << block
|
34
|
+
end
|
35
|
+
|
26
36
|
def after_subset_queue(&block)
|
27
37
|
@after_subset_queue_store ||= []
|
28
38
|
@after_subset_queue_store << block
|
@@ -42,6 +52,16 @@ module KnapsackPro
|
|
42
52
|
end
|
43
53
|
end
|
44
54
|
|
55
|
+
def call_before_subset_queue
|
56
|
+
return unless before_subset_queue_store
|
57
|
+
before_subset_queue_store.each do |block|
|
58
|
+
block.call(
|
59
|
+
KnapsackPro::Config::Env.queue_id,
|
60
|
+
KnapsackPro::Config::Env.subset_queue_id
|
61
|
+
)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
45
65
|
def call_after_subset_queue
|
46
66
|
return unless after_subset_queue_store
|
47
67
|
after_subset_queue_store.each do |block|
|
@@ -62,7 +62,6 @@ module KnapsackPro
|
|
62
62
|
end
|
63
63
|
|
64
64
|
def build_action(can_initialize_queue, attempt_connect_to_queue:)
|
65
|
-
# read test files from disk only when needed because it can be slow operation
|
66
65
|
test_files =
|
67
66
|
if can_initialize_queue && !attempt_connect_to_queue
|
68
67
|
encrypted_test_files
|
@@ -60,6 +60,8 @@ module KnapsackPro
|
|
60
60
|
KnapsackPro.tracker.reset!
|
61
61
|
KnapsackPro.tracker.set_prerun_tests(test_file_paths)
|
62
62
|
|
63
|
+
KnapsackPro::Hooks::Queue.call_before_subset_queue
|
64
|
+
|
63
65
|
all_test_file_paths += test_file_paths
|
64
66
|
|
65
67
|
result_exitstatus = cucumber_run(runner, test_file_paths, args)
|
@@ -67,6 +67,8 @@ module KnapsackPro
|
|
67
67
|
KnapsackPro.tracker.reset!
|
68
68
|
KnapsackPro.tracker.set_prerun_tests(test_file_paths)
|
69
69
|
|
70
|
+
KnapsackPro::Hooks::Queue.call_before_subset_queue
|
71
|
+
|
70
72
|
all_test_file_paths += test_file_paths
|
71
73
|
|
72
74
|
result = minitest_run(runner, test_file_paths, args)
|
data/lib/knapsack_pro/version.rb
CHANGED
data/spec/fixtures/vcr_cassettes/api/v1/build_distributions/subset/invalid_test_suite_token.yml
CHANGED
@@ -5,12 +5,18 @@ http_interactions:
|
|
5
5
|
uri: http://api.knapsackpro.test:3000/v1/build_distributions/subset
|
6
6
|
body:
|
7
7
|
encoding: UTF-8
|
8
|
-
string: '{"commit_hash":"abcdefg","branch":"master","node_total":"2","node_index":"1","
|
8
|
+
string: '{"fixed_test_suite_split":true,"cache_read_attempt":true,"commit_hash":"abcdefg","branch":"master","node_total":"2","node_index":"1","ci_build_id":"missing-build-id"}'
|
9
9
|
headers:
|
10
10
|
Content-Type:
|
11
11
|
- application/json
|
12
12
|
Accept:
|
13
13
|
- application/json
|
14
|
+
Knapsack-Pro-Client-Name:
|
15
|
+
- knapsack_pro-ruby
|
16
|
+
Knapsack-Pro-Client-Version:
|
17
|
+
- 3.5.0
|
18
|
+
Knapsack-Pro-Test-Suite-Token:
|
19
|
+
- fake
|
14
20
|
Accept-Encoding:
|
15
21
|
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
16
22
|
User-Agent:
|
@@ -18,33 +24,36 @@ http_interactions:
|
|
18
24
|
response:
|
19
25
|
status:
|
20
26
|
code: 403
|
21
|
-
message:
|
27
|
+
message: Forbidden
|
22
28
|
headers:
|
23
29
|
X-Frame-Options:
|
24
30
|
- SAMEORIGIN
|
25
31
|
X-Xss-Protection:
|
26
|
-
-
|
32
|
+
- '0'
|
27
33
|
X-Content-Type-Options:
|
28
34
|
- nosniff
|
35
|
+
X-Download-Options:
|
36
|
+
- noopen
|
37
|
+
X-Permitted-Cross-Domain-Policies:
|
38
|
+
- none
|
39
|
+
Referrer-Policy:
|
40
|
+
- strict-origin-when-cross-origin
|
29
41
|
Content-Type:
|
30
42
|
- application/json; charset=utf-8
|
31
43
|
Cache-Control:
|
32
44
|
- no-cache
|
33
45
|
X-Request-Id:
|
34
|
-
-
|
46
|
+
- a5ffe97f-4558-426b-9475-166eb89ab7cb
|
35
47
|
X-Runtime:
|
36
|
-
- '0.
|
37
|
-
Server:
|
38
|
-
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
-
|
43
|
-
Connection:
|
44
|
-
- Keep-Alive
|
48
|
+
- '0.231483'
|
49
|
+
Server-Timing:
|
50
|
+
- sql.active_record;dur=24.31, start_processing.action_controller;dur=0.00,
|
51
|
+
instantiation.active_record;dur=0.02, halted_callback.action_controller;dur=0.00,
|
52
|
+
process_action.action_controller;dur=15.10
|
53
|
+
Transfer-Encoding:
|
54
|
+
- chunked
|
45
55
|
body:
|
46
56
|
encoding: UTF-8
|
47
57
|
string: '{"errors":["invalid test suite token"]}'
|
48
|
-
|
49
|
-
|
50
|
-
recorded_with: VCR 2.9.3
|
58
|
+
recorded_at: Thu, 08 Dec 2022 23:27:10 GMT
|
59
|
+
recorded_with: VCR 6.1.0
|
@@ -5,12 +5,18 @@ http_interactions:
|
|
5
5
|
uri: http://api.knapsackpro.test:3000/v1/build_distributions/subset
|
6
6
|
body:
|
7
7
|
encoding: UTF-8
|
8
|
-
string: '{"commit_hash":"abcdefg","branch":"master","node_total":"2","node_index":"1","
|
8
|
+
string: '{"fixed_test_suite_split":true,"cache_read_attempt":true,"commit_hash":"abcdefg","branch":"master","node_total":"2","node_index":"1","ci_build_id":"missing-build-id"}'
|
9
9
|
headers:
|
10
10
|
Content-Type:
|
11
11
|
- application/json
|
12
12
|
Accept:
|
13
13
|
- application/json
|
14
|
+
Knapsack-Pro-Client-Name:
|
15
|
+
- knapsack_pro-ruby
|
16
|
+
Knapsack-Pro-Client-Version:
|
17
|
+
- 3.5.0
|
18
|
+
Knapsack-Pro-Test-Suite-Token:
|
19
|
+
- 3fa64859337f6e56409d49f865d13fd7
|
14
20
|
Accept-Encoding:
|
15
21
|
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
16
22
|
User-Agent:
|
@@ -18,35 +24,38 @@ http_interactions:
|
|
18
24
|
response:
|
19
25
|
status:
|
20
26
|
code: 200
|
21
|
-
message:
|
27
|
+
message: OK
|
22
28
|
headers:
|
23
29
|
X-Frame-Options:
|
24
30
|
- SAMEORIGIN
|
25
31
|
X-Xss-Protection:
|
26
|
-
-
|
32
|
+
- '0'
|
27
33
|
X-Content-Type-Options:
|
28
34
|
- nosniff
|
35
|
+
X-Download-Options:
|
36
|
+
- noopen
|
37
|
+
X-Permitted-Cross-Domain-Policies:
|
38
|
+
- none
|
39
|
+
Referrer-Policy:
|
40
|
+
- strict-origin-when-cross-origin
|
29
41
|
Content-Type:
|
30
42
|
- application/json; charset=utf-8
|
31
43
|
Etag:
|
32
|
-
- W/"
|
44
|
+
- W/"1f643e3528e8ff45d9d2d67ab7e32be1"
|
33
45
|
Cache-Control:
|
34
46
|
- max-age=0, private, must-revalidate
|
35
47
|
X-Request-Id:
|
36
|
-
-
|
48
|
+
- 9b378323-c960-4868-894e-5ae0cdab8840
|
37
49
|
X-Runtime:
|
38
|
-
- '0.
|
39
|
-
Server:
|
40
|
-
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
-
|
45
|
-
Connection:
|
46
|
-
- Keep-Alive
|
50
|
+
- '0.273775'
|
51
|
+
Server-Timing:
|
52
|
+
- sql.active_record;dur=55.90, start_processing.action_controller;dur=0.00,
|
53
|
+
unpermitted_parameters.action_controller;dur=0.01, instantiation.active_record;dur=10.69,
|
54
|
+
process_action.action_controller;dur=92.24
|
55
|
+
Transfer-Encoding:
|
56
|
+
- chunked
|
47
57
|
body:
|
48
58
|
encoding: UTF-8
|
49
|
-
string: '{"
|
50
|
-
|
51
|
-
|
52
|
-
recorded_with: VCR 2.9.3
|
59
|
+
string: '{"code":"TEST_SUITE_SPLIT_CACHE_MISS"}'
|
60
|
+
recorded_at: Thu, 08 Dec 2022 23:27:07 GMT
|
61
|
+
recorded_with: VCR 6.1.0
|
@@ -17,66 +17,11 @@ describe KnapsackPro::Allocator do
|
|
17
17
|
|
18
18
|
describe '#test_file_paths' do
|
19
19
|
let(:response) { double }
|
20
|
+
let(:api_code) { nil }
|
20
21
|
|
21
22
|
subject { allocator.test_file_paths }
|
22
23
|
|
23
|
-
|
24
|
-
encrypted_test_files = double
|
25
|
-
expect(KnapsackPro::Crypto::Encryptor).to receive(:call).with(fast_and_slow_test_files_to_run).and_return(encrypted_test_files)
|
26
|
-
|
27
|
-
encrypted_branch = double
|
28
|
-
expect(KnapsackPro::Crypto::BranchEncryptor).to receive(:call).with(repository_adapter.branch).and_return(encrypted_branch)
|
29
|
-
|
30
|
-
action = double
|
31
|
-
expect(KnapsackPro::Client::API::V1::BuildDistributions).to receive(:subset).with({
|
32
|
-
commit_hash: repository_adapter.commit_hash,
|
33
|
-
branch: encrypted_branch,
|
34
|
-
node_total: ci_node_total,
|
35
|
-
node_index: ci_node_index,
|
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(fast_and_slow_test_files_to_run, 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
|
-
|
24
|
+
shared_examples_for 'when connection to API failed (fallback mode)' do
|
80
25
|
context 'when fallback mode is disabled' do
|
81
26
|
before do
|
82
27
|
expect(KnapsackPro::Config::Env).to receive(:fallback_mode_enabled?).and_return(false)
|
@@ -126,5 +71,143 @@ describe KnapsackPro::Allocator do
|
|
126
71
|
it { should eq ['c_spec.rb', 'd_spec.rb'] }
|
127
72
|
end
|
128
73
|
end
|
74
|
+
|
75
|
+
before do
|
76
|
+
encrypted_branch = double
|
77
|
+
expect(KnapsackPro::Crypto::BranchEncryptor).to receive(:call).with(repository_adapter.branch).and_return(encrypted_branch)
|
78
|
+
|
79
|
+
action = double
|
80
|
+
expect(KnapsackPro::Client::API::V1::BuildDistributions).to receive(:subset).with({
|
81
|
+
cache_read_attempt: true,
|
82
|
+
commit_hash: repository_adapter.commit_hash,
|
83
|
+
branch: encrypted_branch,
|
84
|
+
node_total: ci_node_total,
|
85
|
+
node_index: ci_node_index,
|
86
|
+
test_files: nil, # when `cache_read_attempt=true`, then expect `test_files` is `nil` to make the request fast due to a small payload
|
87
|
+
}).and_return(action)
|
88
|
+
|
89
|
+
connection = instance_double(KnapsackPro::Client::Connection,
|
90
|
+
call: response,
|
91
|
+
success?: success?,
|
92
|
+
errors?: errors?,
|
93
|
+
api_code: api_code)
|
94
|
+
expect(KnapsackPro::Client::Connection).to receive(:new).with(action).and_return(connection)
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'when successful request to API' do
|
98
|
+
let(:success?) { true }
|
99
|
+
|
100
|
+
context 'when response has errors' do
|
101
|
+
let(:errors?) { true }
|
102
|
+
|
103
|
+
it do
|
104
|
+
expect { subject }.to raise_error(ArgumentError)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'when response has no errors' do
|
109
|
+
let(:errors?) { false }
|
110
|
+
|
111
|
+
context 'when the response returns test files (successful attempt to read from the cache - the cached test suite split exists on the API side)' do
|
112
|
+
let(:response) do
|
113
|
+
{
|
114
|
+
'test_files' => [
|
115
|
+
{ 'path' => 'a_spec.rb' },
|
116
|
+
{ 'path' => 'b_spec.rb' },
|
117
|
+
]
|
118
|
+
}
|
119
|
+
end
|
120
|
+
|
121
|
+
before do
|
122
|
+
expect(KnapsackPro::Crypto::Decryptor).to receive(:call).with(fast_and_slow_test_files_to_run, response['test_files']).and_call_original
|
123
|
+
end
|
124
|
+
|
125
|
+
it { should eq ['a_spec.rb', 'b_spec.rb'] }
|
126
|
+
end
|
127
|
+
|
128
|
+
context 'when the response has the API code=TEST_SUITE_SPLIT_CACHE_MISS' do
|
129
|
+
let(:response) do
|
130
|
+
{ 'code' => 'TEST_SUITE_SPLIT_CACHE_MISS' }
|
131
|
+
end
|
132
|
+
let(:api_code) { 'TEST_SUITE_SPLIT_CACHE_MISS' }
|
133
|
+
|
134
|
+
before do
|
135
|
+
encrypted_branch = double
|
136
|
+
expect(KnapsackPro::Crypto::BranchEncryptor).to receive(:call).with(repository_adapter.branch).and_return(encrypted_branch)
|
137
|
+
|
138
|
+
encrypted_test_files = double
|
139
|
+
expect(KnapsackPro::Crypto::Encryptor).to receive(:call).with(fast_and_slow_test_files_to_run).and_return(encrypted_test_files)
|
140
|
+
|
141
|
+
# 2nd request is not an attempt to read from the cache.
|
142
|
+
# Try to initalize a new test suite split by sending a list of test files from the disk.
|
143
|
+
action = double
|
144
|
+
expect(KnapsackPro::Client::API::V1::BuildDistributions).to receive(:subset).with({
|
145
|
+
cache_read_attempt: false,
|
146
|
+
commit_hash: repository_adapter.commit_hash,
|
147
|
+
branch: encrypted_branch,
|
148
|
+
node_total: ci_node_total,
|
149
|
+
node_index: ci_node_index,
|
150
|
+
test_files: encrypted_test_files,
|
151
|
+
}).and_return(action)
|
152
|
+
|
153
|
+
connection = instance_double(KnapsackPro::Client::Connection,
|
154
|
+
call: response2,
|
155
|
+
success?: response2_success?,
|
156
|
+
errors?: response2_errors?,
|
157
|
+
api_code: nil)
|
158
|
+
expect(KnapsackPro::Client::Connection).to receive(:new).with(action).and_return(connection)
|
159
|
+
end
|
160
|
+
|
161
|
+
context 'when successful 2nd request to API' do
|
162
|
+
let(:response2_success?) { true }
|
163
|
+
|
164
|
+
context 'when 2nd response has errors' do
|
165
|
+
let(:response2_errors?) { true }
|
166
|
+
let(:response2) { nil }
|
167
|
+
|
168
|
+
it do
|
169
|
+
expect { subject }.to raise_error(ArgumentError)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
context 'when 2nd response has no errors' do
|
174
|
+
let(:response2_errors?) { false }
|
175
|
+
|
176
|
+
context 'when 2nd response returns test files' do
|
177
|
+
let(:response2) do
|
178
|
+
{
|
179
|
+
'test_files' => [
|
180
|
+
{ 'path' => 'a_spec.rb' },
|
181
|
+
{ 'path' => 'b_spec.rb' },
|
182
|
+
]
|
183
|
+
}
|
184
|
+
end
|
185
|
+
|
186
|
+
before do
|
187
|
+
expect(KnapsackPro::Crypto::Decryptor).to receive(:call).with(fast_and_slow_test_files_to_run, response2['test_files']).and_call_original
|
188
|
+
end
|
189
|
+
|
190
|
+
it { should eq ['a_spec.rb', 'b_spec.rb'] }
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
context 'when not successful 2nd request to API' do
|
196
|
+
let(:response2_success?) { false }
|
197
|
+
let(:response2_errors?) { false }
|
198
|
+
let(:response2) { nil }
|
199
|
+
|
200
|
+
it_behaves_like 'when connection to API failed (fallback mode)'
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
context 'when not successful request to API' do
|
207
|
+
let(:success?) { false }
|
208
|
+
let(:errors?) { false }
|
209
|
+
|
210
|
+
it_behaves_like 'when connection to API failed (fallback mode)'
|
211
|
+
end
|
129
212
|
end
|
130
213
|
end
|
@@ -5,10 +5,12 @@ describe KnapsackPro::Client::API::V1::BuildDistributions do
|
|
5
5
|
let(:branch) { double }
|
6
6
|
let(:node_total) { double }
|
7
7
|
let(:node_index) { double }
|
8
|
+
let(:ci_build_id) { double }
|
8
9
|
let(:test_files) { double }
|
9
10
|
|
10
11
|
subject do
|
11
12
|
described_class.subset(
|
13
|
+
cache_read_attempt: cache_read_attempt,
|
12
14
|
commit_hash: commit_hash,
|
13
15
|
branch: branch,
|
14
16
|
node_total: node_total,
|
@@ -19,23 +21,52 @@ describe KnapsackPro::Client::API::V1::BuildDistributions do
|
|
19
21
|
|
20
22
|
before do
|
21
23
|
expect(KnapsackPro::Config::Env).to receive(:fixed_test_suite_split).and_return(fixed_test_suite_split)
|
24
|
+
expect(KnapsackPro::Config::Env).to receive(:ci_node_build_id).and_return(ci_build_id)
|
22
25
|
end
|
23
26
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
27
|
+
context 'when cache_read_attempt=true' do
|
28
|
+
let(:cache_read_attempt) { true }
|
29
|
+
|
30
|
+
it 'does not send test_files among other params' do
|
31
|
+
action = double
|
32
|
+
expect(KnapsackPro::Client::API::Action).to receive(:new).with({
|
33
|
+
endpoint_path: '/v1/build_distributions/subset',
|
34
|
+
http_method: :post,
|
35
|
+
request_hash: {
|
36
|
+
fixed_test_suite_split: fixed_test_suite_split,
|
37
|
+
cache_read_attempt: cache_read_attempt,
|
38
|
+
commit_hash: commit_hash,
|
39
|
+
branch: branch,
|
40
|
+
node_total: node_total,
|
41
|
+
node_index: node_index,
|
42
|
+
ci_build_id: ci_build_id,
|
43
|
+
}
|
44
|
+
}).and_return(action)
|
45
|
+
expect(subject).to eq action
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'when cache_read_attempt=false' do
|
50
|
+
let(:cache_read_attempt) { false }
|
51
|
+
|
52
|
+
it 'sends test_files among other params' do
|
53
|
+
action = double
|
54
|
+
expect(KnapsackPro::Client::API::Action).to receive(:new).with({
|
55
|
+
endpoint_path: '/v1/build_distributions/subset',
|
56
|
+
http_method: :post,
|
57
|
+
request_hash: {
|
58
|
+
fixed_test_suite_split: fixed_test_suite_split,
|
59
|
+
cache_read_attempt: cache_read_attempt,
|
60
|
+
commit_hash: commit_hash,
|
61
|
+
branch: branch,
|
62
|
+
node_total: node_total,
|
63
|
+
node_index: node_index,
|
64
|
+
ci_build_id: ci_build_id,
|
65
|
+
test_files: test_files
|
66
|
+
}
|
67
|
+
}).and_return(action)
|
68
|
+
expect(subject).to eq action
|
69
|
+
end
|
39
70
|
end
|
40
71
|
end
|
41
72
|
|
@@ -37,6 +37,46 @@ describe KnapsackPro::Hooks::Queue do
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
+
describe '.call_before_subset_queue' do
|
41
|
+
subject { described_class.call_before_subset_queue }
|
42
|
+
|
43
|
+
context 'when callback is not set' do
|
44
|
+
before do
|
45
|
+
described_class.reset_before_subset_queue
|
46
|
+
end
|
47
|
+
|
48
|
+
it { should be_nil }
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'when callback is set multiple times' do
|
52
|
+
let(:queue_id) { double }
|
53
|
+
let(:subset_queue_id) { double }
|
54
|
+
|
55
|
+
before do
|
56
|
+
expect(KnapsackPro::Config::Env).to receive(:queue_id).twice.and_return(queue_id)
|
57
|
+
expect(KnapsackPro::Config::Env).to receive(:subset_queue_id).twice.and_return(subset_queue_id)
|
58
|
+
|
59
|
+
$expected_called_blocks = []
|
60
|
+
|
61
|
+
described_class.before_subset_queue do |q_id, subset_q_id|
|
62
|
+
$expected_called_blocks << [:block_1_called, q_id, subset_q_id]
|
63
|
+
end
|
64
|
+
described_class.before_subset_queue do |q_id, subset_q_id|
|
65
|
+
$expected_called_blocks << [:block_2_called, q_id, subset_q_id]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'each block is called' do
|
70
|
+
subject
|
71
|
+
|
72
|
+
expect($expected_called_blocks).to eq([
|
73
|
+
[:block_1_called, queue_id, subset_queue_id],
|
74
|
+
[:block_2_called, queue_id, subset_queue_id],
|
75
|
+
])
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
40
80
|
describe '.call_after_subset_queue' do
|
41
81
|
subject { described_class.call_after_subset_queue }
|
42
82
|
|
@@ -147,10 +147,7 @@ describe KnapsackPro::QueueAllocator do
|
|
147
147
|
it { should eq ['a_spec.rb', 'b_spec.rb'] }
|
148
148
|
end
|
149
149
|
|
150
|
-
context 'when response has code=ATTEMPT_CONNECT_TO_QUEUE_FAILED' do
|
151
|
-
let(:response) do
|
152
|
-
{ 'code' => 'ATTEMPT_CONNECT_TO_QUEUE_FAILED' }
|
153
|
-
end
|
150
|
+
context 'when the response has the API code=ATTEMPT_CONNECT_TO_QUEUE_FAILED' do
|
154
151
|
let(:api_code) { 'ATTEMPT_CONNECT_TO_QUEUE_FAILED' }
|
155
152
|
|
156
153
|
before do
|
@@ -197,7 +194,7 @@ describe KnapsackPro::QueueAllocator do
|
|
197
194
|
context 'when 2nd response has no errors' do
|
198
195
|
let(:response2_errors?) { false }
|
199
196
|
|
200
|
-
context 'when 2nd response returns test files (
|
197
|
+
context 'when 2nd response returns test files (successfully initialized a new queue or connected to an existing queue on the API side)' do
|
201
198
|
let(:response2) do
|
202
199
|
{
|
203
200
|
'test_files' => [
|
@@ -115,6 +115,8 @@ describe KnapsackPro::Runners::Queue::CucumberRunner do
|
|
115
115
|
expect(tracker).to receive(:reset!)
|
116
116
|
expect(tracker).to receive(:set_prerun_tests).with(test_file_paths)
|
117
117
|
|
118
|
+
expect(KnapsackPro::Hooks::Queue).to receive(:call_before_subset_queue)
|
119
|
+
|
118
120
|
# .cucumber_run
|
119
121
|
expect(Kernel).to receive(:system).with('bundle exec cucumber --retry 5 --no-strict-flaky --require fake-features-dir -- "features/a.feature" "features/b.feature"')
|
120
122
|
|
@@ -125,6 +125,7 @@ describe KnapsackPro::Runners::Queue::MinitestRunner do
|
|
125
125
|
|
126
126
|
expect(Minitest::Runnable).to receive(:reset)
|
127
127
|
|
128
|
+
expect(KnapsackPro::Hooks::Queue).to receive(:call_before_subset_queue)
|
128
129
|
|
129
130
|
expect(KnapsackPro::Hooks::Queue).to receive(:call_after_subset_queue)
|
130
131
|
|
@@ -225,6 +225,8 @@ describe KnapsackPro::Runners::Queue::RSpecRunner do
|
|
225
225
|
|
226
226
|
expect(described_class).to receive(:rspec_clear_examples)
|
227
227
|
|
228
|
+
expect(KnapsackPro::Hooks::Queue).to receive(:call_before_subset_queue)
|
229
|
+
|
228
230
|
expect(KnapsackPro::Hooks::Queue).to receive(:call_after_subset_queue)
|
229
231
|
|
230
232
|
expect(KnapsackPro::Report).to receive(:save_subset_queue_to_file)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: knapsack_pro
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ArturT
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-12-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|