knapsack_pro 1.20.2 → 1.21.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 +10 -0
- data/README.md +24 -12
- data/lib/knapsack_pro.rb +6 -0
- data/lib/knapsack_pro/adapters/base_adapter.rb +16 -0
- data/lib/knapsack_pro/adapters/rspec_adapter.rb +11 -9
- data/lib/knapsack_pro/allocator.rb +7 -5
- data/lib/knapsack_pro/allocator_builder.rb +2 -1
- data/lib/knapsack_pro/base_allocator_builder.rb +41 -10
- data/lib/knapsack_pro/build_distribution_fetcher.rb +57 -0
- data/lib/knapsack_pro/client/api/v1/build_distributions.rb +13 -0
- data/lib/knapsack_pro/client/connection.rb +30 -12
- data/lib/knapsack_pro/config/env.rb +4 -0
- data/lib/knapsack_pro/queue_allocator.rb +7 -5
- data/lib/knapsack_pro/queue_allocator_builder.rb +2 -1
- data/lib/knapsack_pro/runners/queue/rspec_runner.rb +7 -0
- data/lib/knapsack_pro/runners/rspec_runner.rb +5 -2
- data/lib/knapsack_pro/slow_test_file_determiner.rb +28 -0
- data/lib/knapsack_pro/slow_test_file_finder.rb +27 -0
- data/lib/knapsack_pro/test_case_detectors/rspec_test_example_detector.rb +18 -2
- data/lib/knapsack_pro/test_case_mergers/base_merger.rb +29 -0
- data/lib/knapsack_pro/test_case_mergers/rspec_merger.rb +34 -0
- data/lib/knapsack_pro/test_file_finder.rb +43 -5
- data/lib/knapsack_pro/test_files_with_test_cases_composer.rb +22 -0
- data/lib/knapsack_pro/version.rb +1 -1
- data/spec/knapsack_pro/adapters/base_adapter_spec.rb +55 -0
- data/spec/knapsack_pro/adapters/rspec_adapter_spec.rb +61 -25
- data/spec/knapsack_pro/allocator_builder_spec.rb +7 -3
- data/spec/knapsack_pro/allocator_spec.rb +7 -5
- data/spec/knapsack_pro/base_allocator_builder_spec.rb +79 -27
- data/spec/knapsack_pro/build_distribution_fetcher_spec.rb +89 -0
- data/spec/knapsack_pro/client/api/v1/build_distributions_spec.rb +31 -0
- data/spec/knapsack_pro/client/connection_spec.rb +165 -103
- data/spec/knapsack_pro/config/env_spec.rb +14 -0
- data/spec/knapsack_pro/queue_allocator_builder_spec.rb +7 -3
- data/spec/knapsack_pro/queue_allocator_spec.rb +7 -5
- data/spec/knapsack_pro/runners/rspec_runner_spec.rb +4 -4
- data/spec/knapsack_pro/slow_test_file_determiner_spec.rb +74 -0
- data/spec/knapsack_pro/slow_test_file_finder_spec.rb +43 -0
- data/spec/knapsack_pro/test_case_detectors/rspec_test_example_detector_spec.rb +81 -35
- data/spec/knapsack_pro/test_case_mergers/base_merger_spec.rb +27 -0
- data/spec/knapsack_pro/test_case_mergers/rspec_merger_spec.rb +59 -0
- data/spec/knapsack_pro/test_file_finder_spec.rb +105 -29
- data/spec/knapsack_pro/test_files_with_test_cases_composer_spec.rb +41 -0
- metadata +27 -10
@@ -8,8 +8,11 @@ describe KnapsackPro::AllocatorBuilder do
|
|
8
8
|
subject { allocator_builder.allocator }
|
9
9
|
|
10
10
|
before do
|
11
|
-
|
12
|
-
expect(allocator_builder).to receive(:
|
11
|
+
fast_and_slow_test_files_to_run = double
|
12
|
+
expect(allocator_builder).to receive(:fast_and_slow_test_files_to_run).and_return(fast_and_slow_test_files_to_run)
|
13
|
+
|
14
|
+
fallback_mode_test_files = double
|
15
|
+
expect(allocator_builder).to receive(:fallback_mode_test_files).and_return(fallback_mode_test_files)
|
13
16
|
|
14
17
|
repository_adapter = double
|
15
18
|
expect(KnapsackPro::RepositoryAdapterInitiator).to receive(:call).and_return(repository_adapter)
|
@@ -20,7 +23,8 @@ describe KnapsackPro::AllocatorBuilder do
|
|
20
23
|
expect(KnapsackPro::Config::Env).to receive(:ci_node_index).and_return(ci_node_index)
|
21
24
|
|
22
25
|
expect(KnapsackPro::Allocator).to receive(:new).with(
|
23
|
-
|
26
|
+
fast_and_slow_test_files_to_run: fast_and_slow_test_files_to_run,
|
27
|
+
fallback_mode_test_files: fallback_mode_test_files,
|
24
28
|
ci_node_total: ci_node_total,
|
25
29
|
ci_node_index: ci_node_index,
|
26
30
|
repository_adapter: repository_adapter,
|
@@ -1,12 +1,14 @@
|
|
1
1
|
describe KnapsackPro::Allocator do
|
2
|
-
let(:
|
2
|
+
let(:fast_and_slow_test_files_to_run) { double }
|
3
|
+
let(:fallback_mode_test_files) { double }
|
3
4
|
let(:ci_node_total) { double }
|
4
5
|
let(:ci_node_index) { double }
|
5
6
|
let(:repository_adapter) { instance_double(KnapsackPro::RepositoryAdapters::EnvAdapter, commit_hash: double, branch: double) }
|
6
7
|
|
7
8
|
let(:allocator) do
|
8
9
|
described_class.new(
|
9
|
-
|
10
|
+
fast_and_slow_test_files_to_run: fast_and_slow_test_files_to_run,
|
11
|
+
fallback_mode_test_files: fallback_mode_test_files,
|
10
12
|
ci_node_total: ci_node_total,
|
11
13
|
ci_node_index: ci_node_index,
|
12
14
|
repository_adapter: repository_adapter
|
@@ -20,7 +22,7 @@ describe KnapsackPro::Allocator do
|
|
20
22
|
|
21
23
|
before do
|
22
24
|
encrypted_test_files = double
|
23
|
-
expect(KnapsackPro::Crypto::Encryptor).to receive(:call).with(
|
25
|
+
expect(KnapsackPro::Crypto::Encryptor).to receive(:call).with(fast_and_slow_test_files_to_run).and_return(encrypted_test_files)
|
24
26
|
|
25
27
|
encrypted_branch = double
|
26
28
|
expect(KnapsackPro::Crypto::BranchEncryptor).to receive(:call).with(repository_adapter.branch).and_return(encrypted_branch)
|
@@ -64,7 +66,7 @@ describe KnapsackPro::Allocator do
|
|
64
66
|
end
|
65
67
|
|
66
68
|
before do
|
67
|
-
expect(KnapsackPro::Crypto::Decryptor).to receive(:call).with(
|
69
|
+
expect(KnapsackPro::Crypto::Decryptor).to receive(:call).with(fast_and_slow_test_files_to_run, response['test_files']).and_call_original
|
68
70
|
end
|
69
71
|
|
70
72
|
it { should eq ['a_spec.rb', 'b_spec.rb'] }
|
@@ -114,7 +116,7 @@ describe KnapsackPro::Allocator do
|
|
114
116
|
context 'when fallback mode started' do
|
115
117
|
before do
|
116
118
|
test_flat_distributor = instance_double(KnapsackPro::TestFlatDistributor)
|
117
|
-
expect(KnapsackPro::TestFlatDistributor).to receive(:new).with(
|
119
|
+
expect(KnapsackPro::TestFlatDistributor).to receive(:new).with(fallback_mode_test_files, ci_node_total).and_return(test_flat_distributor)
|
118
120
|
expect(test_flat_distributor).to receive(:test_files_for_node).with(ci_node_index).and_return([
|
119
121
|
{ 'path' => 'c_spec.rb' },
|
120
122
|
{ 'path' => 'd_spec.rb' },
|
@@ -100,8 +100,22 @@ describe KnapsackPro::BaseAllocatorBuilder do
|
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
103
|
-
describe '#
|
104
|
-
subject { allocator_builder.
|
103
|
+
describe '#fallback_mode_test_files' do
|
104
|
+
subject { allocator_builder.fallback_mode_test_files }
|
105
|
+
|
106
|
+
it do
|
107
|
+
test_file_pattern = double
|
108
|
+
expect(KnapsackPro::TestFilePattern).to receive(:call).with(adapter_class).and_return(test_file_pattern)
|
109
|
+
|
110
|
+
test_files = double
|
111
|
+
expect(KnapsackPro::TestFileFinder).to receive(:call).with(test_file_pattern).and_return(test_files)
|
112
|
+
|
113
|
+
expect(subject).to eq test_files
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe '#fast_and_slow_test_files_to_run' do
|
118
|
+
subject { allocator_builder.fast_and_slow_test_files_to_run }
|
105
119
|
|
106
120
|
context 'when looking for test files on disk by default' do
|
107
121
|
it do
|
@@ -115,10 +129,10 @@ describe KnapsackPro::BaseAllocatorBuilder do
|
|
115
129
|
end
|
116
130
|
end
|
117
131
|
|
118
|
-
context 'when RSpec adapter
|
132
|
+
context 'when RSpec adapter AND rspec split by test examples is enabled' do
|
119
133
|
let(:adapter_class) { KnapsackPro::Adapters::RSpecAdapter }
|
120
|
-
let(:
|
121
|
-
let(:cmd) { 'bundle exec rake knapsack_pro:rspec_test_example_detector' }
|
134
|
+
let(:test_files_to_run) { double }
|
135
|
+
let(:cmd) { 'RACK_ENV=test RAILS_ENV=test bundle exec rake knapsack_pro:rspec_test_example_detector' }
|
122
136
|
|
123
137
|
before do
|
124
138
|
expect(KnapsackPro::Config::Env).to receive(:rspec_split_by_test_examples?).and_return(true)
|
@@ -126,53 +140,91 @@ describe KnapsackPro::BaseAllocatorBuilder do
|
|
126
140
|
test_file_pattern = double
|
127
141
|
expect(KnapsackPro::TestFilePattern).to receive(:call).with(adapter_class).and_return(test_file_pattern)
|
128
142
|
|
129
|
-
expect(KnapsackPro::TestFileFinder).to receive(:call).with(test_file_pattern).and_return(
|
143
|
+
expect(KnapsackPro::TestFileFinder).to receive(:call).with(test_file_pattern).and_return(test_files_to_run)
|
144
|
+
end
|
145
|
+
|
146
|
+
context 'when RSpec version < 3.3.0' do
|
147
|
+
before do
|
148
|
+
stub_const('RSpec::Core::Version::STRING', '3.2.0')
|
149
|
+
end
|
130
150
|
|
131
|
-
|
151
|
+
it do
|
152
|
+
expect { subject }.to raise_error RuntimeError, 'RSpec >= 3.3.0 is required to split test files by test examples. Learn more: https://github.com/KnapsackPro/knapsack_pro-ruby#split-test-files-by-test-cases'
|
153
|
+
end
|
132
154
|
end
|
133
155
|
|
134
156
|
context 'when rake task to detect RSpec test examples works' do
|
157
|
+
let(:slow_test_files) { double(size: 5) }
|
135
158
|
let(:cmd_result) { true }
|
136
159
|
let(:test_file_example_paths) { double }
|
137
160
|
let(:logger) { instance_double(Logger) }
|
161
|
+
let(:test_files_with_test_cases) { double }
|
138
162
|
|
139
163
|
before do
|
140
|
-
|
141
|
-
expect(KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector).to receive(:new).and_return(rspec_test_example_detector)
|
142
|
-
expect(rspec_test_example_detector).to receive(:test_file_example_paths).and_return(test_file_example_paths)
|
164
|
+
expect(allocator_builder).to receive(:get_slow_test_files).and_return(slow_test_files)
|
143
165
|
|
144
|
-
expect(KnapsackPro).to receive(:logger).
|
145
|
-
end
|
166
|
+
expect(KnapsackPro).to receive(:logger).and_return(logger)
|
146
167
|
|
147
|
-
|
148
|
-
let(:test_files) { double(size: 1000) }
|
168
|
+
expect(Kernel).to receive(:system).with(cmd).and_return(cmd_result)
|
149
169
|
|
150
|
-
|
151
|
-
|
170
|
+
rspec_test_example_detector = instance_double(KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector)
|
171
|
+
expect(KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector).to receive(:new).and_return(rspec_test_example_detector)
|
172
|
+
expect(rspec_test_example_detector).to receive(:test_file_example_paths).and_return(test_file_example_paths)
|
152
173
|
|
153
|
-
|
154
|
-
end
|
174
|
+
expect(KnapsackPro::TestFilesWithTestCasesComposer).to receive(:call).with(test_files_to_run, slow_test_files, test_file_example_paths).and_return(test_files_with_test_cases)
|
155
175
|
end
|
156
176
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
it do
|
161
|
-
expect(logger).to receive(:warn).with("Generating RSpec test examples JSON report to prepare your test suite to be split by test examples (by individual 'it's. Thanks to that a single test file can be split across parallel CI nodes). Analyzing 1001 test files.")
|
162
|
-
expect(logger).to receive(:warn).with('You have more than 1000 test files, it may take longer to generate test examples. Please wait...')
|
177
|
+
it do
|
178
|
+
expect(logger).to receive(:info).with("Generating RSpec test examples JSON report for slow test files to prepare it to be split by test examples (by individual 'it's. Thanks to that a single slow test file can be split across parallel CI nodes). Analyzing 5 slow test files.")
|
163
179
|
|
164
|
-
|
165
|
-
end
|
180
|
+
expect(subject).to eq test_files_with_test_cases
|
166
181
|
end
|
167
182
|
end
|
168
183
|
|
169
184
|
context 'when rake task to detect RSpec test examples failed' do
|
185
|
+
let(:slow_test_files) { double(size: 5) }
|
170
186
|
let(:cmd_result) { false }
|
171
187
|
|
188
|
+
before do
|
189
|
+
expect(allocator_builder).to receive(:get_slow_test_files).and_return(slow_test_files)
|
190
|
+
|
191
|
+
expect(Kernel).to receive(:system).with(cmd).and_return(cmd_result)
|
192
|
+
end
|
193
|
+
|
172
194
|
it do
|
173
|
-
expect { subject }.to raise_error(RuntimeError, 'Could not generate JSON report for RSpec. Rake task failed when running bundle exec rake knapsack_pro:rspec_test_example_detector')
|
195
|
+
expect { subject }.to raise_error(RuntimeError, 'Could not generate JSON report for RSpec. Rake task failed when running RACK_ENV=test RAILS_ENV=test bundle exec rake knapsack_pro:rspec_test_example_detector')
|
174
196
|
end
|
175
197
|
end
|
176
198
|
end
|
177
199
|
end
|
200
|
+
|
201
|
+
describe 'private #get_slow_test_files' do
|
202
|
+
subject { allocator_builder.send(:get_slow_test_files) }
|
203
|
+
|
204
|
+
before do
|
205
|
+
expect(KnapsackPro::Config::Env).to receive(:slow_test_file_pattern).and_return(slow_test_file_pattern)
|
206
|
+
end
|
207
|
+
|
208
|
+
context 'when slow test file pattern is present' do
|
209
|
+
let(:slow_test_files) { double(:slow_test_files_based_on_pattern, size: 3) }
|
210
|
+
let(:slow_test_file_pattern) { double }
|
211
|
+
|
212
|
+
before do
|
213
|
+
expect(KnapsackPro::TestFileFinder).to receive(:slow_test_files_by_pattern).with(adapter_class).and_return(slow_test_files)
|
214
|
+
end
|
215
|
+
|
216
|
+
it { expect(subject).to eq slow_test_files }
|
217
|
+
end
|
218
|
+
|
219
|
+
context 'when slow test file pattern is not present' do
|
220
|
+
let(:slow_test_files) { double(:slow_test_files_based_on_api, size: 2) }
|
221
|
+
let(:slow_test_file_pattern) { nil }
|
222
|
+
|
223
|
+
before do
|
224
|
+
expect(KnapsackPro::SlowTestFileFinder).to receive(:call).with(adapter_class).and_return(slow_test_files)
|
225
|
+
end
|
226
|
+
|
227
|
+
it { expect(subject).to eq slow_test_files }
|
228
|
+
end
|
229
|
+
end
|
178
230
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
describe KnapsackPro::BuildDistributionFetcher do
|
2
|
+
describe '.call' do
|
3
|
+
subject { described_class.call }
|
4
|
+
|
5
|
+
it do
|
6
|
+
build_distribution_fetcher = instance_double(described_class)
|
7
|
+
expect(described_class).to receive(:new).and_return(build_distribution_fetcher)
|
8
|
+
result = double
|
9
|
+
expect(build_distribution_fetcher).to receive(:call).and_return(result)
|
10
|
+
|
11
|
+
expect(subject).to eq result
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#call' do
|
16
|
+
let(:ci_node_total) { double }
|
17
|
+
let(:ci_node_index) { double }
|
18
|
+
let(:repository_adapter) { instance_double(KnapsackPro::RepositoryAdapters::EnvAdapter, commit_hash: double, branch: double) }
|
19
|
+
|
20
|
+
subject { described_class.new.call }
|
21
|
+
|
22
|
+
before do
|
23
|
+
expect(KnapsackPro::RepositoryAdapterInitiator).to receive(:call).and_return(repository_adapter)
|
24
|
+
|
25
|
+
expect(KnapsackPro::Config::Env).to receive(:ci_node_total).and_return(ci_node_total)
|
26
|
+
expect(KnapsackPro::Config::Env).to receive(:ci_node_index).and_return(ci_node_index)
|
27
|
+
|
28
|
+
action = double
|
29
|
+
expect(KnapsackPro::Client::API::V1::BuildDistributions).to receive(:last).with({
|
30
|
+
commit_hash: repository_adapter.commit_hash,
|
31
|
+
branch: repository_adapter.branch,
|
32
|
+
node_total: ci_node_total,
|
33
|
+
node_index: ci_node_index,
|
34
|
+
}).and_return(action)
|
35
|
+
|
36
|
+
connection = instance_double(KnapsackPro::Client::Connection,
|
37
|
+
call: response,
|
38
|
+
success?: success?,
|
39
|
+
errors?: errors?)
|
40
|
+
expect(KnapsackPro::Client::Connection).to receive(:new).with(action).and_return(connection)
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'when successful request to API' do
|
44
|
+
let(:success?) { true }
|
45
|
+
|
46
|
+
context 'when response has errors' do
|
47
|
+
let(:errors?) { true }
|
48
|
+
let(:response) { 'fake error response' }
|
49
|
+
|
50
|
+
it do
|
51
|
+
expect { subject }.to raise_error(ArgumentError, response)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'when response has no errors' do
|
56
|
+
let(:errors?) { false }
|
57
|
+
let(:response) do
|
58
|
+
{
|
59
|
+
'build_distribution_id' => 'be2b95b1-1b8b-43a3-9d66-cabebbf135b8',
|
60
|
+
'time_execution' => 2.5,
|
61
|
+
'test_files' => [
|
62
|
+
{ 'path' => 'a_spec.rb', 'time_execution' => 1.5 },
|
63
|
+
{ 'path' => 'b_spec.rb', 'time_execution' => 1.0 },
|
64
|
+
]
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
it { expect(subject).to be_a described_class::BuildDistributionEntity }
|
69
|
+
it { expect(subject.time_execution).to eq 2.5 }
|
70
|
+
it do
|
71
|
+
expect(subject.test_files).to eq([
|
72
|
+
{ 'path' => 'a_spec.rb', 'time_execution' => 1.5 },
|
73
|
+
{ 'path' => 'b_spec.rb', 'time_execution' => 1.0 },
|
74
|
+
])
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'when not successful request to API' do
|
80
|
+
let(:success?) { false }
|
81
|
+
let(:errors?) { false }
|
82
|
+
let(:response) { double }
|
83
|
+
|
84
|
+
it { expect(subject).to be_a described_class::BuildDistributionEntity }
|
85
|
+
it { expect(subject.time_execution).to eq 0.0 }
|
86
|
+
it { expect(subject.test_files).to eq([]) }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -38,4 +38,35 @@ describe KnapsackPro::Client::API::V1::BuildDistributions do
|
|
38
38
|
expect(subject).to eq action
|
39
39
|
end
|
40
40
|
end
|
41
|
+
|
42
|
+
describe '.last' do
|
43
|
+
let(:commit_hash) { double }
|
44
|
+
let(:branch) { double }
|
45
|
+
let(:node_total) { double }
|
46
|
+
let(:node_index) { double }
|
47
|
+
|
48
|
+
subject do
|
49
|
+
described_class.last(
|
50
|
+
commit_hash: commit_hash,
|
51
|
+
branch: branch,
|
52
|
+
node_total: node_total,
|
53
|
+
node_index: node_index,
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
it do
|
58
|
+
action = double
|
59
|
+
expect(KnapsackPro::Client::API::Action).to receive(:new).with({
|
60
|
+
endpoint_path: '/v1/build_distributions/last',
|
61
|
+
http_method: :get,
|
62
|
+
request_hash: {
|
63
|
+
commit_hash: commit_hash,
|
64
|
+
branch: branch,
|
65
|
+
node_total: node_total,
|
66
|
+
node_index: node_index,
|
67
|
+
}
|
68
|
+
}).and_return(action)
|
69
|
+
expect(subject).to eq action
|
70
|
+
end
|
71
|
+
end
|
41
72
|
end
|
@@ -1,167 +1,229 @@
|
|
1
|
+
shared_examples 'when request got response from API' do
|
2
|
+
context 'when body response is JSON and API response code is 400' do
|
3
|
+
let(:body) { '{"errors": "value"}' }
|
4
|
+
let(:code) { '400' } # it must be string code
|
5
|
+
|
6
|
+
before do
|
7
|
+
expect(KnapsackPro).to receive(:logger).exactly(4).and_return(logger)
|
8
|
+
expect(logger).to receive(:debug).with("#{expected_http_method} http://api.knapsackpro.test:3000/v1/fake_endpoint")
|
9
|
+
expect(logger).to receive(:debug).with('API request UUID: fake-uuid')
|
10
|
+
expect(logger).to receive(:debug).with('API response:')
|
11
|
+
end
|
12
|
+
|
13
|
+
it do
|
14
|
+
parsed_response = { 'errors' => 'value' }
|
15
|
+
|
16
|
+
expect(logger).to receive(:error).with(parsed_response)
|
17
|
+
|
18
|
+
expect(subject).to eq(parsed_response)
|
19
|
+
expect(connection.success?).to be true
|
20
|
+
expect(connection.errors?).to be true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'when body response is JSON with build_distribution_id' do
|
25
|
+
let(:body) { '{"build_distribution_id": "seed-uuid"}' }
|
26
|
+
let(:code) { '200' } # it must be string code
|
27
|
+
|
28
|
+
before do
|
29
|
+
expect(KnapsackPro).to receive(:logger).exactly(5).and_return(logger)
|
30
|
+
expect(logger).to receive(:debug).with("#{expected_http_method} http://api.knapsackpro.test:3000/v1/fake_endpoint")
|
31
|
+
expect(logger).to receive(:debug).with('API request UUID: fake-uuid')
|
32
|
+
expect(logger).to receive(:debug).with("Test suite split seed: seed-uuid")
|
33
|
+
expect(logger).to receive(:debug).with('API response:')
|
34
|
+
end
|
35
|
+
|
36
|
+
it do
|
37
|
+
parsed_response = { 'build_distribution_id' => 'seed-uuid' }
|
38
|
+
|
39
|
+
expect(logger).to receive(:debug).with(parsed_response)
|
40
|
+
|
41
|
+
expect(subject).to eq(parsed_response)
|
42
|
+
expect(connection.success?).to be true
|
43
|
+
expect(connection.errors?).to be false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'when body response is empty' do
|
48
|
+
let(:body) { '' }
|
49
|
+
let(:code) { '200' } # it must be string code
|
50
|
+
|
51
|
+
before do
|
52
|
+
expect(KnapsackPro).to receive(:logger).exactly(4).and_return(logger)
|
53
|
+
expect(logger).to receive(:debug).with("#{expected_http_method} http://api.knapsackpro.test:3000/v1/fake_endpoint")
|
54
|
+
expect(logger).to receive(:debug).with('API request UUID: fake-uuid')
|
55
|
+
expect(logger).to receive(:debug).with('API response:')
|
56
|
+
end
|
57
|
+
|
58
|
+
it do
|
59
|
+
expect(logger).to receive(:debug).with('')
|
60
|
+
|
61
|
+
expect(subject).to eq('')
|
62
|
+
expect(connection.success?).to be true
|
63
|
+
expect(connection.errors?).to be false
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
shared_examples 'when retry request' do
|
69
|
+
context 'when body response is JSON and API response code is 500' do
|
70
|
+
let(:body) { '{"error": "Internal Server Error"}' }
|
71
|
+
let(:code) { '500' } # it must be string code
|
72
|
+
|
73
|
+
before do
|
74
|
+
expect(KnapsackPro).to receive(:logger).at_least(1).and_return(logger)
|
75
|
+
expect(logger).to receive(:debug).exactly(3).with("#{expected_http_method} http://api.knapsackpro.test:3000/v1/fake_endpoint")
|
76
|
+
expect(logger).to receive(:debug).exactly(3).with('API request UUID: fake-uuid')
|
77
|
+
expect(logger).to receive(:debug).exactly(3).with('API response:')
|
78
|
+
end
|
79
|
+
|
80
|
+
it do
|
81
|
+
parsed_response = { 'error' => 'Internal Server Error' }
|
82
|
+
|
83
|
+
expect(logger).to receive(:error).exactly(3).with(parsed_response)
|
84
|
+
|
85
|
+
server_error = described_class::ServerError.new(parsed_response)
|
86
|
+
expect(logger).to receive(:warn).exactly(3).with(server_error.inspect)
|
87
|
+
|
88
|
+
expect(logger).to receive(:warn).with("Wait 4s and retry request to Knapsack Pro API.")
|
89
|
+
expect(logger).to receive(:warn).with("Wait 8s and retry request to Knapsack Pro API.")
|
90
|
+
expect(Kernel).to receive(:sleep).with(4)
|
91
|
+
expect(Kernel).to receive(:sleep).with(8)
|
92
|
+
|
93
|
+
expect(subject).to eq(parsed_response)
|
94
|
+
|
95
|
+
expect(connection.success?).to be false
|
96
|
+
expect(connection.errors?).to be true
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
1
101
|
describe KnapsackPro::Client::Connection do
|
2
102
|
let(:endpoint_path) { '/v1/fake_endpoint' }
|
3
|
-
let(:http_method) { :post }
|
4
103
|
let(:request_hash) { { fake: 'hash' } }
|
104
|
+
let(:http_method) { :post }
|
5
105
|
let(:action) do
|
6
106
|
instance_double(KnapsackPro::Client::API::Action,
|
7
107
|
endpoint_path: endpoint_path,
|
8
108
|
http_method: http_method,
|
9
109
|
request_hash: request_hash)
|
10
110
|
end
|
111
|
+
let(:test_suite_token) { '3fa64859337f6e56409d49f865d13fd7' }
|
11
112
|
|
12
113
|
let(:connection) { described_class.new(action) }
|
13
114
|
|
14
115
|
before do
|
15
116
|
stub_const('ENV', {
|
16
117
|
'KNAPSACK_PRO_ENDPOINT' => 'http://api.knapsackpro.test:3000',
|
17
|
-
'KNAPSACK_PRO_TEST_SUITE_TOKEN' =>
|
118
|
+
'KNAPSACK_PRO_TEST_SUITE_TOKEN' => test_suite_token,
|
18
119
|
})
|
19
120
|
end
|
20
121
|
|
21
122
|
describe '#call' do
|
22
123
|
let(:logger) { instance_double(Logger) }
|
124
|
+
let(:http) { instance_double(Net::HTTP) }
|
125
|
+
let(:http_response) do
|
126
|
+
header = { 'X-Request-Id' => 'fake-uuid' }
|
127
|
+
instance_double(Net::HTTPOK, body: body, header: header, code: code)
|
128
|
+
end
|
23
129
|
|
24
130
|
subject { connection.call }
|
25
131
|
|
26
|
-
|
27
|
-
|
28
|
-
http = instance_double(Net::HTTP)
|
132
|
+
before do
|
133
|
+
expect(Net::HTTP).to receive(:new).with('api.knapsackpro.test', 3000).and_return(http)
|
29
134
|
|
30
|
-
|
135
|
+
expect(http).to receive(:use_ssl=).with(false)
|
136
|
+
expect(http).to receive(:open_timeout=).with(15)
|
137
|
+
expect(http).to receive(:read_timeout=).with(15)
|
138
|
+
end
|
31
139
|
|
32
|
-
|
33
|
-
|
34
|
-
expect(http).to receive(:read_timeout=).with(15)
|
140
|
+
context 'when http method is POST' do
|
141
|
+
let(:http_method) { :post }
|
35
142
|
|
36
|
-
|
37
|
-
http_response = instance_double(Net::HTTPOK, body: body, header: header, code: code)
|
143
|
+
before do
|
38
144
|
expect(http).to receive(:post).with(
|
39
145
|
endpoint_path,
|
40
|
-
|
146
|
+
request_hash.to_json,
|
41
147
|
{
|
42
148
|
'Content-Type' => 'application/json',
|
43
149
|
'Accept' => 'application/json',
|
44
150
|
'KNAPSACK-PRO-CLIENT-NAME' => 'knapsack_pro-ruby',
|
45
151
|
'KNAPSACK-PRO-CLIENT-VERSION' => KnapsackPro::VERSION,
|
152
|
+
'KNAPSACK-PRO-TEST-SUITE-TOKEN' => test_suite_token,
|
46
153
|
}
|
47
154
|
).and_return(http_response)
|
48
155
|
end
|
49
156
|
|
50
|
-
|
51
|
-
let(:
|
52
|
-
let(:code) { '400' } # it must be string code
|
53
|
-
|
54
|
-
before do
|
55
|
-
expect(KnapsackPro).to receive(:logger).exactly(3).and_return(logger)
|
56
|
-
expect(logger).to receive(:debug).with('API request UUID: fake-uuid')
|
57
|
-
expect(logger).to receive(:debug).with('API response:')
|
58
|
-
end
|
59
|
-
|
60
|
-
it do
|
61
|
-
parsed_response = { 'errors' => 'value' }
|
62
|
-
|
63
|
-
expect(logger).to receive(:error).with(parsed_response)
|
64
|
-
|
65
|
-
expect(subject).to eq(parsed_response)
|
66
|
-
expect(connection.success?).to be true
|
67
|
-
expect(connection.errors?).to be true
|
68
|
-
end
|
157
|
+
it_behaves_like 'when request got response from API' do
|
158
|
+
let(:expected_http_method) { 'POST' }
|
69
159
|
end
|
160
|
+
end
|
70
161
|
|
71
|
-
|
72
|
-
|
73
|
-
let(:code) { '200' } # it must be string code
|
74
|
-
|
75
|
-
before do
|
76
|
-
expect(KnapsackPro).to receive(:logger).exactly(4).and_return(logger)
|
77
|
-
expect(logger).to receive(:debug).with('API request UUID: fake-uuid')
|
78
|
-
expect(logger).to receive(:debug).with("Test suite split seed: seed-uuid")
|
79
|
-
expect(logger).to receive(:debug).with('API response:')
|
80
|
-
end
|
81
|
-
|
82
|
-
it do
|
83
|
-
parsed_response = { 'build_distribution_id' => 'seed-uuid' }
|
84
|
-
|
85
|
-
expect(logger).to receive(:debug).with(parsed_response)
|
162
|
+
context 'when http method is GET' do
|
163
|
+
let(:http_method) { :get }
|
86
164
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
165
|
+
before do
|
166
|
+
uri = URI.parse("http://api.knapsackpro.test:3000#{endpoint_path}")
|
167
|
+
uri.query = URI.encode_www_form(request_hash)
|
168
|
+
expect(http).to receive(:get).with(
|
169
|
+
uri,
|
170
|
+
{
|
171
|
+
'Content-Type' => 'application/json',
|
172
|
+
'Accept' => 'application/json',
|
173
|
+
'KNAPSACK-PRO-CLIENT-NAME' => 'knapsack_pro-ruby',
|
174
|
+
'KNAPSACK-PRO-CLIENT-VERSION' => KnapsackPro::VERSION,
|
175
|
+
'KNAPSACK-PRO-TEST-SUITE-TOKEN' => test_suite_token,
|
176
|
+
}
|
177
|
+
).and_return(http_response)
|
91
178
|
end
|
92
179
|
|
93
|
-
|
94
|
-
let(:
|
95
|
-
let(:code) { '200' } # it must be string code
|
96
|
-
|
97
|
-
before do
|
98
|
-
expect(KnapsackPro).to receive(:logger).exactly(3).and_return(logger)
|
99
|
-
expect(logger).to receive(:debug).with('API request UUID: fake-uuid')
|
100
|
-
expect(logger).to receive(:debug).with('API response:')
|
101
|
-
end
|
102
|
-
|
103
|
-
it do
|
104
|
-
expect(logger).to receive(:debug).with('')
|
105
|
-
|
106
|
-
expect(subject).to eq('')
|
107
|
-
expect(connection.success?).to be true
|
108
|
-
expect(connection.errors?).to be false
|
109
|
-
end
|
180
|
+
it_behaves_like 'when request got response from API' do
|
181
|
+
let(:expected_http_method) { 'GET' }
|
110
182
|
end
|
111
183
|
end
|
112
184
|
|
113
185
|
context 'when retry request for http method POST' do
|
114
|
-
|
115
|
-
http = instance_double(Net::HTTP)
|
186
|
+
let(:http_method) { :post }
|
116
187
|
|
117
|
-
|
118
|
-
|
119
|
-
expect(http).to receive(:use_ssl=).exactly(3).with(false)
|
120
|
-
expect(http).to receive(:open_timeout=).exactly(3).with(15)
|
121
|
-
expect(http).to receive(:read_timeout=).exactly(3).with(15)
|
122
|
-
|
123
|
-
header = { 'X-Request-Id' => 'fake-uuid' }
|
124
|
-
http_response = instance_double(Net::HTTPOK, body: body, header: header, code: code)
|
188
|
+
before do
|
125
189
|
expect(http).to receive(:post).exactly(3).with(
|
126
190
|
endpoint_path,
|
127
|
-
|
191
|
+
request_hash.to_json,
|
128
192
|
{
|
129
193
|
'Content-Type' => 'application/json',
|
130
194
|
'Accept' => 'application/json',
|
131
195
|
'KNAPSACK-PRO-CLIENT-NAME' => 'knapsack_pro-ruby',
|
132
196
|
'KNAPSACK-PRO-CLIENT-VERSION' => KnapsackPro::VERSION,
|
197
|
+
'KNAPSACK-PRO-TEST-SUITE-TOKEN' => test_suite_token,
|
133
198
|
}
|
134
199
|
).and_return(http_response)
|
135
200
|
end
|
136
201
|
|
137
|
-
|
138
|
-
let(:
|
139
|
-
|
140
|
-
|
141
|
-
before do
|
142
|
-
expect(KnapsackPro).to receive(:logger).at_least(1).and_return(logger)
|
143
|
-
expect(logger).to receive(:debug).exactly(3).with('API request UUID: fake-uuid')
|
144
|
-
expect(logger).to receive(:debug).exactly(3).with('API response:')
|
145
|
-
end
|
146
|
-
|
147
|
-
it do
|
148
|
-
parsed_response = { 'error' => 'Internal Server Error' }
|
149
|
-
|
150
|
-
expect(logger).to receive(:error).exactly(3).with(parsed_response)
|
151
|
-
|
152
|
-
server_error = described_class::ServerError.new(parsed_response)
|
153
|
-
expect(logger).to receive(:warn).exactly(3).with(server_error.inspect)
|
202
|
+
it_behaves_like 'when retry request' do
|
203
|
+
let(:expected_http_method) { 'POST' }
|
204
|
+
end
|
205
|
+
end
|
154
206
|
|
155
|
-
|
156
|
-
|
157
|
-
expect(Kernel).to receive(:sleep).with(4)
|
158
|
-
expect(Kernel).to receive(:sleep).with(8)
|
207
|
+
context 'when retry request for http method GET' do
|
208
|
+
let(:http_method) { :get }
|
159
209
|
|
160
|
-
|
210
|
+
before do
|
211
|
+
uri = URI.parse("http://api.knapsackpro.test:3000#{endpoint_path}")
|
212
|
+
uri.query = URI.encode_www_form(request_hash)
|
213
|
+
expect(http).to receive(:get).exactly(3).with(
|
214
|
+
uri,
|
215
|
+
{
|
216
|
+
'Content-Type' => 'application/json',
|
217
|
+
'Accept' => 'application/json',
|
218
|
+
'KNAPSACK-PRO-CLIENT-NAME' => 'knapsack_pro-ruby',
|
219
|
+
'KNAPSACK-PRO-CLIENT-VERSION' => KnapsackPro::VERSION,
|
220
|
+
'KNAPSACK-PRO-TEST-SUITE-TOKEN' => test_suite_token,
|
221
|
+
}
|
222
|
+
).and_return(http_response)
|
223
|
+
end
|
161
224
|
|
162
|
-
|
163
|
-
|
164
|
-
end
|
225
|
+
it_behaves_like 'when retry request' do
|
226
|
+
let(:expected_http_method) { 'GET' }
|
165
227
|
end
|
166
228
|
end
|
167
229
|
end
|