knapsack_pro 1.5.0 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +31 -0
- data/lib/knapsack_pro/client/connection.rb +18 -4
- data/lib/knapsack_pro/version.rb +1 -1
- data/spec/knapsack_pro/client/connection_spec.rb +88 -21
- 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: 59483963a5cda7dabf89075bf4aebff864e813d0192db2c10e8162918ecbef11
|
4
|
+
data.tar.gz: c4e50a75d402de63289db9b6126df9f6fbced9a562776125d94903752961b10a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b8341410b23dc56c9843734d6f264dc679eca71e8cda951f5594f782433f2c20b7998b708d21b8e76616a41b1cc5b14d4d80d9e468c61bc015edd605aac2a9b1
|
7
|
+
data.tar.gz: df0e11899060062d5ad447f749263ce05ab4c7dd11b93cbcb837e3c380c9879a08ee247292c9fed32839d17b13ac8a6b696493032b71d92d3d6d914b458eeed0
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
### 1.6.0
|
4
|
+
|
5
|
+
* Retry request 3 times when API returns 5xx HTTP status
|
6
|
+
|
7
|
+
https://github.com/KnapsackPro/knapsack_pro-ruby/pull/78
|
8
|
+
|
9
|
+
https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v1.5.0...v1.6.0
|
10
|
+
|
3
11
|
### 1.5.0
|
4
12
|
|
5
13
|
* Add support for Semaphore CI 2.0
|
data/README.md
CHANGED
@@ -144,6 +144,7 @@ You can see list of questions for common problems and tips in below [Table of Co
|
|
144
144
|
- [for knapack_pro regular mode](#for-knapack_pro-regular-mode-1)
|
145
145
|
- [for knapsack_pro queue mode](#for-knapsack_pro-queue-mode-1)
|
146
146
|
- [How can I change log level?](#how-can-i-change-log-level)
|
147
|
+
- [How to write knapack_pro logs to a file?](#how-to-write-knapack_pro-logs-to-a-file)
|
147
148
|
- [How to split tests based on test level instead of test file level?](#how-to-split-tests-based-on-test-level-instead-of-test-file-level)
|
148
149
|
- [A. Create multiple small test files](#a-create-multiple-small-test-files)
|
149
150
|
- [B. Use tags to mark set of tests in particular test file](#b-use-tags-to-mark-set-of-tests-in-particular-test-file)
|
@@ -1752,6 +1753,36 @@ Recommended log levels you can use:
|
|
1752
1753
|
* `debug` is default log level and it is recommended to log details about requests to Knapsack Pro API. Thanks to that you can debug things or ensure everything works. For instance in [user dashboard](https://knapsackpro.com/dashboard) you can find tips referring to debug logs.
|
1753
1754
|
* `info` level shows message like how to retry tests in development or info why something works this way or the other (for instance why tests were not executed on the CI node). You can use `info` level when you really don't want to see all debug messages from default log level.
|
1754
1755
|
|
1756
|
+
#### How to write knapack_pro logs to a file?
|
1757
|
+
|
1758
|
+
In your `rails_helper.rb` or `spec_helper.rb` you can set custom Knapsack Pro logger and write to custom log file.
|
1759
|
+
|
1760
|
+
```ruby
|
1761
|
+
require 'logger'
|
1762
|
+
KnapsackPro.logger = Logger.new(Rails.root.join('log', "knapsack_pro_node_#{KnapsackPro::Config::Env.ci_node_index}.log"))
|
1763
|
+
KnapsackPro.logger.level = Logger::DEBUG
|
1764
|
+
```
|
1765
|
+
|
1766
|
+
Note if you run knapsack_pro in Queue Mode then the very first request to Knapsack Pro API still will be shown to stdout because we need to have set of test files needed to run RSpec before we load `rails_helper.rb`/`spec_helper.rb` where the configuration of logger actually is loaded for the first time.
|
1767
|
+
|
1768
|
+
If you would like to keep knapsack_pro logs after your CI build finished then you could use artifacts or some cache mechanize for your CI provider.
|
1769
|
+
|
1770
|
+
For instance, for [CircleCI 2.0 artifacts](https://circleci.com/docs/2.0/artifacts/) you can specify log directory:
|
1771
|
+
|
1772
|
+
```yaml
|
1773
|
+
- run:
|
1774
|
+
name: RSpec via knapsack_pro Queue Mode
|
1775
|
+
command: |
|
1776
|
+
# export word is important here!
|
1777
|
+
export RAILS_ENV=test
|
1778
|
+
bundle exec rake "knapsack_pro:queue:rspec[--format documentation]"
|
1779
|
+
|
1780
|
+
- store_artifacts:
|
1781
|
+
path: log
|
1782
|
+
```
|
1783
|
+
|
1784
|
+
Now you can preview logs in `Artifacts` tab in the Circle CI build view.
|
1785
|
+
|
1755
1786
|
#### How to split tests based on test level instead of test file level?
|
1756
1787
|
|
1757
1788
|
If you want to split one big test file (test file with long time execution) across multiple CI nodes then you can:
|
@@ -1,7 +1,10 @@
|
|
1
1
|
module KnapsackPro
|
2
2
|
module Client
|
3
3
|
class Connection
|
4
|
+
class ServerError < StandardError; end
|
5
|
+
|
4
6
|
TIMEOUT = 15
|
7
|
+
MAX_RETRY = 3
|
5
8
|
REQUEST_RETRY_TIMEBOX = 2
|
6
9
|
|
7
10
|
def initialize(action)
|
@@ -23,6 +26,11 @@ module KnapsackPro
|
|
23
26
|
!!(response_body && (response_body['errors'] || response_body['error']))
|
24
27
|
end
|
25
28
|
|
29
|
+
def server_error?
|
30
|
+
status = http_response.code.to_i
|
31
|
+
status >= 500 && status < 600
|
32
|
+
end
|
33
|
+
|
26
34
|
private
|
27
35
|
|
28
36
|
attr_reader :action, :http_response, :response_body
|
@@ -91,7 +99,7 @@ module KnapsackPro
|
|
91
99
|
@http_response = http.post(uri.path, request_body, json_headers)
|
92
100
|
@response_body = parse_response_body(http_response.body)
|
93
101
|
|
94
|
-
request_uuid = http_response.header['X-Request-Id']
|
102
|
+
request_uuid = http_response.header['X-Request-Id'] || 'N/A'
|
95
103
|
|
96
104
|
logger.debug("API request UUID: #{request_uuid}")
|
97
105
|
logger.debug("Test suite split seed: #{seed}") if has_seed?
|
@@ -102,15 +110,21 @@ module KnapsackPro
|
|
102
110
|
logger.debug(response_body)
|
103
111
|
end
|
104
112
|
|
113
|
+
if server_error?
|
114
|
+
raise ServerError.new(response_body)
|
115
|
+
end
|
116
|
+
|
105
117
|
response_body
|
106
|
-
rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT, Errno::EPIPE, EOFError, SocketError, Net::OpenTimeout, Net::ReadTimeout => e
|
118
|
+
rescue ServerError, Errno::ECONNREFUSED, Errno::ETIMEDOUT, Errno::EPIPE, EOFError, SocketError, Net::OpenTimeout, Net::ReadTimeout => e
|
107
119
|
logger.warn(e.inspect)
|
108
120
|
retries += 1
|
109
|
-
if retries <
|
121
|
+
if retries < MAX_RETRY
|
110
122
|
wait = retries * REQUEST_RETRY_TIMEBOX
|
111
123
|
logger.warn("Wait #{wait}s and retry request to Knapsack Pro API.")
|
112
|
-
sleep
|
124
|
+
Kernel.sleep(wait)
|
113
125
|
retry
|
126
|
+
else
|
127
|
+
response_body
|
114
128
|
end
|
115
129
|
end
|
116
130
|
end
|
data/lib/knapsack_pro/version.rb
CHANGED
@@ -68,27 +68,6 @@ describe KnapsackPro::Client::Connection do
|
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
|
-
context 'when body response is json and API response code is 500' do
|
72
|
-
let(:body) { '{"error": "Internal Server Error"}' }
|
73
|
-
let(:code) { '500' } # it must be string code
|
74
|
-
|
75
|
-
before do
|
76
|
-
expect(KnapsackPro).to receive(:logger).exactly(3).and_return(logger)
|
77
|
-
expect(logger).to receive(:debug).with('API request UUID: fake-uuid')
|
78
|
-
expect(logger).to receive(:debug).with('API response:')
|
79
|
-
end
|
80
|
-
|
81
|
-
it do
|
82
|
-
parsed_response = { 'error' => 'Internal Server Error' }
|
83
|
-
|
84
|
-
expect(logger).to receive(:error).with(parsed_response)
|
85
|
-
|
86
|
-
expect(subject).to eq(parsed_response)
|
87
|
-
expect(connection.success?).to be false
|
88
|
-
expect(connection.errors?).to be true
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
71
|
context 'when body response is json with build_distribution_id' do
|
93
72
|
let(:body) { '{"build_distribution_id": "seed-uuid"}' }
|
94
73
|
let(:code) { '200' } # it must be string code
|
@@ -130,6 +109,61 @@ describe KnapsackPro::Client::Connection do
|
|
130
109
|
end
|
131
110
|
end
|
132
111
|
end
|
112
|
+
|
113
|
+
context 'when retry request for http method POST' do
|
114
|
+
before do
|
115
|
+
http = instance_double(Net::HTTP)
|
116
|
+
|
117
|
+
expect(Net::HTTP).to receive(:new).exactly(3).with('api.knapsackpro.test', 3000).and_return(http)
|
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)
|
125
|
+
expect(http).to receive(:post).exactly(3).with(
|
126
|
+
endpoint_path,
|
127
|
+
"{\"fake\":\"hash\",\"test_suite_token\":\"3fa64859337f6e56409d49f865d13fd7\"}",
|
128
|
+
{
|
129
|
+
'Content-Type' => 'application/json',
|
130
|
+
'Accept' => 'application/json',
|
131
|
+
'KNAPSACK-PRO-CLIENT-NAME' => 'knapsack_pro-ruby',
|
132
|
+
'KNAPSACK-PRO-CLIENT-VERSION' => KnapsackPro::VERSION,
|
133
|
+
}
|
134
|
+
).and_return(http_response)
|
135
|
+
end
|
136
|
+
|
137
|
+
context 'when body response is json and API response code is 500' do
|
138
|
+
let(:body) { '{"error": "Internal Server Error"}' }
|
139
|
+
let(:code) { '500' } # it must be string code
|
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)
|
154
|
+
|
155
|
+
expect(logger).to receive(:warn).with("Wait 2s and retry request to Knapsack Pro API.")
|
156
|
+
expect(logger).to receive(:warn).with("Wait 4s and retry request to Knapsack Pro API.")
|
157
|
+
expect(Kernel).to receive(:sleep).with(2)
|
158
|
+
expect(Kernel).to receive(:sleep).with(4)
|
159
|
+
|
160
|
+
expect(subject).to eq(parsed_response)
|
161
|
+
|
162
|
+
expect(connection.success?).to be false
|
163
|
+
expect(connection.errors?).to be true
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
133
167
|
end
|
134
168
|
|
135
169
|
describe '#success?' do
|
@@ -220,4 +254,37 @@ describe KnapsackPro::Client::Connection do
|
|
220
254
|
end
|
221
255
|
end
|
222
256
|
end
|
257
|
+
|
258
|
+
describe '#server_error?' do
|
259
|
+
subject { connection.server_error? }
|
260
|
+
|
261
|
+
before do
|
262
|
+
http_response = double(code: code)
|
263
|
+
allow(connection).to receive(:http_response).and_return(http_response)
|
264
|
+
end
|
265
|
+
|
266
|
+
context 'when response code is 200' do
|
267
|
+
let(:code) { '200' } # it must be string code
|
268
|
+
|
269
|
+
it { should be false }
|
270
|
+
end
|
271
|
+
|
272
|
+
context 'when response code is 300' do
|
273
|
+
let(:code) { '300' } # it must be string code
|
274
|
+
|
275
|
+
it { should be false }
|
276
|
+
end
|
277
|
+
|
278
|
+
context 'when response code is 400' do
|
279
|
+
let(:code) { '400' } # it must be string code
|
280
|
+
|
281
|
+
it { should be false }
|
282
|
+
end
|
283
|
+
|
284
|
+
context 'when response code is 500' do
|
285
|
+
let(:code) { '500' } # it must be string code
|
286
|
+
|
287
|
+
it { should be true }
|
288
|
+
end
|
289
|
+
end
|
223
290
|
end
|
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: 1.
|
4
|
+
version: 1.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: 2019-03-
|
11
|
+
date: 2019-03-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|