rainforest-cli 1.10.2 → 1.10.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0e31fc0fb7e685c2de8fd444e7d5f2a074d19875
4
- data.tar.gz: ad40b1b738892368b8597e779a23c25f621dc5a3
3
+ metadata.gz: 404d70dc620de15e7e6eeb220d60a1809f722ee1
4
+ data.tar.gz: 9a23f6f532cb6cf9eaf6139868a876d020453392
5
5
  SHA512:
6
- metadata.gz: 9ea8b9dc6ad22b570efae9b6f379631130062653a92f982b02660fe9dfc8b60f61a2f7678732a0837faf4fb94f613e5c5bd5d8fdfc03e8ecc250fd299ec46c1f
7
- data.tar.gz: 644af9ed6b8064c8cfcfd17fe7aaeed20d992d073d968bcb73e66e13533c665c3010bcfea52957444a03dde6488622d9691e74fcf6660b229e881463ba217afc
6
+ metadata.gz: 34ce70554f9807d0e2db16dd7c6eb58f5a219da7198b3c4db64dfa97ebd745585303ceac5c6f4bf501e45c2eb1bc0274fc023d73bd0642f35216ca65f5c3e38b
7
+ data.tar.gz: 10b6f5678629a9b9516ca2e6837e04569ffb79b61a35c97e2602073fff16f4f83372fb37debb9b3b82b30182846041ff88d27c1e2b184f7409409bc4424aee68
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Rainforest CLI Changelog
2
2
 
3
+ ## 1.10.3 - 21st October 2016
4
+ - Fix bug with accidental removal of 3 commands. (d9ebbd702ba1b1b64b9a1a222c301315b89d743e, @curtis-rainforestqa)
5
+ - Add better tolerance for server errors. (b4180332ca13fec0972c3d65dbce00242907ca38, @epaulet)
6
+
3
7
  ## 1.10.2 - 11th October 2016
4
8
  - Limit CSV batch upload size to chunks of 20 at a time for more reliability.
5
9
  (abfed07df5635eb88029ee0b6cf8eea3a538fff6, @epaulet)
@@ -33,6 +33,9 @@ module RainforestCli
33
33
  c.add('export', 'Export your remote Rainforest tests to RFML') { Exporter.new(options).export }
34
34
  c.add('csv-upload', 'Upload a new tabular variable from a CSV file') { CSVImporter.new(options).import }
35
35
  c.add('report', 'Create a JUnit report from your run results') { Reporter.new(options).report }
36
+ c.add('sites', 'Lists the available sites') { Resources.new(options).sites }
37
+ c.add('folders', 'Lists the available folders') { Resources.new(options).folders }
38
+ c.add('browsers', 'Lists the available browsers') { Resources.new(options).browsers }
36
39
  end
37
40
 
38
41
  @http_client = HttpClient.new(token: options.token)
@@ -6,6 +6,8 @@ module RainforestCli
6
6
 
7
7
  attr_reader :commands
8
8
 
9
+ SUPPORTED_COMMANDS = %w(run new validate upload rm export csv-upload report sites folders browsers)
10
+
9
11
  def initialize
10
12
  @commands = []
11
13
  yield(self) if block_given?
@@ -40,8 +40,12 @@ module RainforestCli
40
40
  end
41
41
  end
42
42
 
43
- print 'Creating new tabular variable'
44
- response = http_client.post '/generators', { name: @generator_name, description: @generator_name, columns: columns }
43
+ puts 'Creating new tabular variable'
44
+ response = http_client.post(
45
+ '/generators',
46
+ { name: @generator_name, description: @generator_name, columns: columns },
47
+ { retries_on_failures: true },
48
+ )
45
49
  raise "Error creating tabular variable: #{response['error']}" if response['error']
46
50
  puts "\t[OK]"
47
51
 
@@ -57,7 +61,8 @@ module RainforestCli
57
61
  response = http_client.post(
58
62
  "/generators/#{generator_id}/rows/batch",
59
63
  { data: data_slice },
60
- retries_on_failures: true)
64
+ { retries_on_failures: true },
65
+ )
61
66
  # NOTE: Response for this endpoint will usually be an array representing all the rows created
62
67
  raise response['error'] if response.is_a?(Hash) && response['error']
63
68
  p.increment
@@ -14,6 +14,7 @@ class RainforestCli::Deleter
14
14
  validate_file_extension
15
15
  delete_remote_test(test_file)
16
16
  delete_local_file(test_file.file_name)
17
+ logger.info 'Test successfully deleted.'
17
18
  end
18
19
 
19
20
  private
@@ -13,81 +13,58 @@ module RainforestCli
13
13
  @token = options.fetch(:token)
14
14
  end
15
15
 
16
- def delete(url, body = {})
17
- response = HTTParty.delete make_url(url), {
18
- body: body,
19
- headers: headers,
20
- verify: false,
21
- }
22
-
23
- JSON.parse(response.body)
16
+ def delete(path, body = {}, options = {})
17
+ request(:delete, path, body, options)
24
18
  end
25
19
 
26
- def post(url, body = {}, options = {})
27
- wrap_exceptions(options[:retries_on_failures]) do
28
- response = HTTParty.post make_url(url), {
29
- body: body,
30
- headers: headers,
31
- verify: false,
32
- }
33
-
34
- return JSON.parse(response.body)
35
- end
20
+ def post(path, body = {}, options = {})
21
+ request(:post, path, body, options)
36
22
  end
37
23
 
38
- def get(url, body = {}, options = {})
39
- wrap_exceptions(options[:retries_on_failures]) do
40
- response = HTTParty.get make_url(url), {
41
- body: body,
42
- headers: headers,
43
- verify: false,
44
- }
45
-
46
- if response.code == 200
47
- return JSON.parse(response.body)
48
- else
49
- RainforestCli.logger.warn("Status Code: #{response.code}, #{response.body}")
50
- return nil
51
- end
52
- end
24
+ def get(path, body = {}, options = {})
25
+ request(:get, path, body, options)
53
26
  end
54
27
 
55
- def api_token_set?
56
- !@token.nil?
57
- end
58
-
59
- private
28
+ def request(method, path, body, options)
29
+ url = File.join(API_URL, path)
60
30
 
61
- def wrap_exceptions(retries_on_failures)
62
- @retry_delay = 0
63
- @waiting_on_retries = false
64
31
  loop do
65
32
  begin
66
- # Suspend tries until wait period is over
67
- if @waiting_on_retries
68
- Kernel.sleep 5
33
+ response = Http::Exceptions.wrap_exception do
34
+ HTTParty.send(method, url, { body: body, headers: headers, verify: false })
35
+ end
36
+
37
+ if response.code.between?(200, 299)
38
+ return JSON.parse(response.body)
39
+ elsif options[:retries_on_failures] && response.code >= 500
40
+ delay = retry_delay
41
+ logger.warn "HTTP request was unsuccessful. URL: #{url}. Status: #{response.code}"
42
+ logger.warn "Retrying again in #{delay} seconds..."
43
+ Kernel.sleep delay
69
44
  else
70
- Http::Exceptions.wrap_exception { yield }
71
- break
45
+ logger.fatal "Non 200 code received for request to #{url}"
46
+ logger.fatal "Server response: #{response.body}"
47
+ exit 1
72
48
  end
73
49
  rescue Http::Exceptions::HttpException, Timeout::Error => e
74
- raise e unless retries_on_failures
50
+ raise e unless options[:retries_on_failures]
75
51
 
76
- unless @waiting_on_retries
77
- @waiting_on_retries = true
78
- @retry_delay += RETRY_INTERVAL
52
+ delay = retry_delay
53
+ logger.warn 'Exception Encountered while trying to contact Rainforest API:'
54
+ logger.warn "\t\t#{e.message}"
55
+ logger.warn "Retrying again in #{delay} seconds..."
79
56
 
80
- RainforestCli.logger.warn 'Exception Encountered while trying to contact Rainforest API:'
81
- RainforestCli.logger.warn "\t\t#{e.message}"
82
- RainforestCli.logger.warn "Retrying again in #{@retry_delay} seconds..."
83
-
84
- Kernel.sleep @retry_delay
85
- @waiting_on_retries = false
86
- end
57
+ Kernel.sleep delay
87
58
  end
88
59
  end
89
60
  end
90
61
 
62
+ def api_token_set?
63
+ !@token.nil?
64
+ end
65
+
66
+ private
67
+
91
68
  def make_url(url)
92
69
  File.join(API_URL, url)
93
70
  end
@@ -98,5 +75,14 @@ module RainforestCli
98
75
  'User-Agent' => "Rainforest-cli-#{RainforestCli::VERSION}",
99
76
  }
100
77
  end
78
+
79
+ def retry_delay
80
+ # make retry delay random to desynchronize multiple threads
81
+ RETRY_INTERVAL + rand(RETRY_INTERVAL)
82
+ end
83
+
84
+ def logger
85
+ RainforestCli.logger
86
+ end
101
87
  end
102
88
  end
@@ -22,11 +22,6 @@ module RainforestCli
22
22
 
23
23
  run = client.get("/runs/#{@run_id}.json")
24
24
 
25
- if run == nil
26
- logger.fatal "Non 200 code recieved"
27
- exit 1
28
- end
29
-
30
25
  if run['error']
31
26
  logger.fatal "Error retrieving results for your run: #{run['error']}"
32
27
  exit 1
@@ -35,11 +30,6 @@ module RainforestCli
35
30
  if run.has_key?('total_tests') and run['total_tests'] != 0
36
31
  tests = client.get("/runs/#{@run_id}/tests.json?page_size=#{run['total_tests']}")
37
32
 
38
- if tests == nil
39
- logger.fatal "Non 200 code recieved"
40
- exit 1
41
- end
42
-
43
33
  if tests.kind_of?(Hash) and tests['error'] # if this had worked tests would be an array
44
34
  logger.fatal "Error retrieving test details for your run: #{tests['error']}"
45
35
  exit 1
@@ -54,16 +54,14 @@ module RainforestCli
54
54
  running = true
55
55
  while running
56
56
  response = client.get("/runs/#{run_id}", {}, retries_on_failures: true)
57
- if response
58
- state_details = response.fetch('state_details')
59
- unless state_details.fetch('is_final_state')
60
- state, current_progress = response.values_at('state', 'current_progress')
61
- logger.info "Run #{run_id} is #{state} and is #{current_progress['percent']}% complete"
62
- running = false if response['result'] == 'failed' && options.failfast?
63
- else
64
- logger.info "Run #{run_id} is now #{response["state"]} and has #{response["result"]}"
65
- running = false
66
- end
57
+ state_details = response.fetch('state_details')
58
+ unless state_details.fetch('is_final_state')
59
+ state, current_progress = response.values_at('state', 'current_progress')
60
+ logger.info "Run #{run_id} is #{state} and is #{current_progress['percent']}% complete"
61
+ running = false if response['result'] == 'failed' && options.failfast?
62
+ else
63
+ logger.info "Run #{run_id} is now #{response["state"]} and has #{response["result"]}"
64
+ running = false
67
65
  end
68
66
  Kernel.sleep 5 if running
69
67
  end
@@ -184,10 +182,6 @@ module RainforestCli
184
182
  end
185
183
 
186
184
  url = client.get('/uploads', {}, retries_on_failures: true)
187
- unless url
188
- logger.fatal "Failed to upload file #{app_source_url}. Please, check your API token."
189
- exit 1
190
- end
191
185
  data = File.read(app_source_url)
192
186
  logger.info 'Uploading app source file, this operation may take few minutes...'
193
187
  response_code = upload_file(url, data)
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module RainforestCli
3
- VERSION = '1.10.2'
3
+ VERSION = '1.10.3'
4
4
  end
@@ -32,7 +32,7 @@ describe RainforestCli::CSVImporter do
32
32
  name: 'variables',
33
33
  description: 'variables',
34
34
  columns: columns,
35
- })
35
+ }, retries_on_failures: true)
36
36
  .and_return success_response
37
37
 
38
38
  expect(http_client).to receive(:post)
@@ -1,79 +1,87 @@
1
1
  # frozen_string_literal: true
2
2
  describe RainforestCli::HttpClient do
3
- subject { described_class.new({ token: 'foo' }) }
3
+ let(:path) { '/my/path' }
4
+ let(:success_response) { instance_double('HTTParty::Response', code: 200, body: {'success'=>'true'}.to_json) }
4
5
 
5
- describe '#get' do
6
- describe 'maximum tolerated exceptions' do
7
- let(:url) { 'http://some.url.com' }
6
+ [:get, :post, :delete].each do |m|
7
+ describe "##{m}" do
8
+ subject { described_class.new({ token: 'foo' }) }
9
+
10
+ it 'makes the correct type of request' do
11
+ expect(HTTParty).to receive(m).and_return(success_response)
12
+ subject.public_send(m, path)
13
+ end
14
+ end
15
+ end
8
16
 
17
+ describe '#request' do
18
+ let(:method) { :get }
19
+ let(:body) { { foo: :bar } }
20
+ let(:options) { {} }
21
+ subject { described_class.new({ token: 'foo' }).request(method, path, body, options) }
22
+
23
+ describe 'maximum tolerated exceptions' do
9
24
  before do
10
- allow(HTTParty).to receive(:get).and_raise(SocketError)
25
+ allow(HTTParty).to receive(method).and_raise(SocketError)
11
26
  end
12
27
 
13
28
  context 'retries_on_failures omitted' do
14
29
  it 'raises the error on the first exception' do
15
- expect(HTTParty).to receive(:get).once
16
- expect { subject.get(url) }.to raise_error(Http::Exceptions::HttpException)
30
+ expect(HTTParty).to receive(method).once
31
+ expect { subject }.to raise_error(Http::Exceptions::HttpException)
17
32
  end
18
33
  end
19
34
 
20
35
  context 'retries_on_failures == false' do
36
+ let(:options) { { retries_on_failures: false } }
37
+
21
38
  it 'raises the error on the first exception' do
22
- expect(HTTParty).to receive(:get).once
23
- expect { subject.get(url, {}, retries_on_failures: false) }.to raise_error(Http::Exceptions::HttpException)
39
+ expect(HTTParty).to receive(method).once
40
+ expect { subject }.to raise_error(Http::Exceptions::HttpException)
24
41
  end
25
42
  end
26
43
 
27
44
  context 'retries_on_failures == true' do
45
+ let(:options) { { retries_on_failures: true } }
28
46
  let(:response) { instance_double('HTTParty::Response', code: 200, body: {foo: :bar}.to_json) }
29
47
  let(:delay_interval) { described_class::RETRY_INTERVAL }
30
- subject { described_class.new({ token: 'foo' }).get(url, {}, retries_on_failures: true) }
31
48
 
32
49
  it 'it sleeps after failures before a retry' do
33
- expect(HTTParty).to receive(:get).and_raise(SocketError).once.ordered
34
- expect(HTTParty).to receive(:get).and_return(response).ordered
35
- expect(Kernel).to receive(:sleep).with(delay_interval).once
50
+ expect(HTTParty).to receive(method).and_raise(SocketError).once.ordered
51
+ expect(HTTParty).to receive(method).and_return(response).ordered
52
+ expect(Kernel).to receive(:sleep)
36
53
  expect { subject }.to_not raise_error
37
54
  end
38
55
 
39
56
  it 'sleeps for longer periods with repeated exceptions' do
40
- expect(HTTParty).to receive(:get).and_raise(SocketError).exactly(3).times.ordered
41
- expect(HTTParty).to receive(:get).and_return(response).ordered
42
- expect(Kernel).to receive(:sleep).with(delay_interval).once
43
- expect(Kernel).to receive(:sleep).with(delay_interval * 2).once
44
- expect(Kernel).to receive(:sleep).with(delay_interval * 3).once
57
+ expect(HTTParty).to receive(method).and_raise(SocketError).exactly(3).times.ordered
58
+ expect(HTTParty).to receive(method).and_return(response).ordered
59
+ expect(Kernel).to receive(:sleep).exactly(3).times
45
60
  expect { subject }.to_not raise_error
46
61
  end
47
62
 
48
63
  it 'returns the response upon success' do
49
- expect(HTTParty).to receive(:get).and_raise(SocketError).once.ordered
50
- expect(HTTParty).to receive(:get).and_return(response).ordered
51
- expect(Kernel).to receive(:sleep).with(delay_interval).once
64
+ expect(HTTParty).to receive(method).and_raise(SocketError).once.ordered
65
+ expect(HTTParty).to receive(method).and_return(response).ordered
66
+ expect(Kernel).to receive(:sleep).once
52
67
  expect(subject).to eq(JSON.parse(response.body))
53
68
  end
54
69
  end
55
70
  end
56
71
 
57
- describe 'non 200 codes' do
58
- context '404 not found'do
59
- let(:url) { 'http://some.url.com' }
60
- let(:response) { instance_double('HTTParty::Response', code: 404, body: {'error'=>'some error', 'type'=>'some type'}.to_json) }
61
- subject { described_class.new({ token: 'foo' }).get(url, {}) }
62
-
63
- before do
64
- allow(HTTParty).to receive(:get).and_raise(SocketError)
65
- end
72
+ describe 'unsuccessful status codes' do
73
+ let(:url) { 'http://some.url.com' }
74
+ let(:bad_response) { instance_double('HTTParty::Response', code: 404, body: {'error'=>'some error', 'type'=>'some type'}.to_json) }
66
75
 
67
- it 'gets an error 404 and prints the error' do
68
- expect(HTTParty).to receive(:get).and_return(response)
69
- expect_any_instance_of(Logger).to receive(:warn).with('Status Code: 404, {"error":"some error","type":"some type"}')
70
- expect(subject)
71
- end
76
+ before do
77
+ allow(HTTParty).to receive(method).and_raise(SocketError)
78
+ end
72
79
 
73
- it 'returns nil' do
74
- expect(HTTParty).to receive(:get).and_return(response)
75
- expect(subject).to eq(nil)
76
- end
80
+ it 'gets an error 404 and prints the error and exits' do
81
+ expect(HTTParty).to receive(method).and_return(bad_response)
82
+ expect_any_instance_of(Logger).to receive(:fatal).with(a_string_including('Non 200 code received'))
83
+ expect_any_instance_of(Logger).to receive(:fatal).with(a_string_including(bad_response.body.to_s))
84
+ expect { subject }.to raise_error(SystemExit)
77
85
  end
78
86
  end
79
87
  end
@@ -86,18 +86,6 @@ describe RainforestCli::Runner do
86
86
  }
87
87
  end
88
88
 
89
- it 'errors out and exits if valid file but invalid token' do
90
- File.should_receive(:exist?).with('fobar.ipa') { true }
91
- subject.client.should_receive(:get).with('/uploads', {}, retries_on_failures: true) { nil }
92
- expect_any_instance_of(Logger).to receive(:fatal).with(
93
- 'Failed to upload file fobar.ipa. Please, check your API token.')
94
- expect do
95
- subject.upload_app('fobar.ipa')
96
- end.to raise_error(SystemExit) { |error|
97
- expect(error.status).to eq 1
98
- }
99
- end
100
-
101
89
  it 'errors out and exits if was not possible to upload the file' do
102
90
  File.should_receive(:exist?).with('fobar.ipa') { true }
103
91
  File.should_receive(:read).with('fobar.ipa') { 'File data' }
@@ -210,19 +210,24 @@ describe RainforestCli do
210
210
  expect(described_class.start(valid_args)).to eq(true)
211
211
  end
212
212
  end
213
+ end
213
214
 
214
- context 'a run where the server 500s after a while' do
215
- before do
216
- allow_any_instance_of(http_client).to receive(:post).and_return('id' => 1)
217
- expect_any_instance_of(http_client).to receive(:get).twice.and_return(ok_progress)
218
- expect_any_instance_of(http_client).to receive(:get)
219
- expect_any_instance_of(http_client).to receive(:get).twice.and_return(ok_progress)
220
- expect_any_instance_of(http_client).to receive(:get).and_return(complete_response)
221
- end
215
+ context 'commands' do
216
+ let(:valid_args) { %w(some args) }
217
+ let(:command) { double(:command, call: true) }
218
+ let(:option_parser) { double(:option_parser, token: '123abc', validate!: true, command: 'some-cmd') }
222
219
 
223
- it 'should return true' do
224
- expect(described_class.start(valid_args)).to eq(true)
220
+ before do
221
+ allow(RainforestCli::OptionParser).to receive(:new) { option_parser }
222
+ allow(RainforestCli::Commands).to receive(:new).and_yield(command).and_return(command)
223
+ end
224
+
225
+ it 'we register all currently supported commands' do
226
+ RainforestCli::Commands::SUPPORTED_COMMANDS.each do |supported_command|
227
+ expect(command).to receive(:add).with(supported_command, kind_of(String))
225
228
  end
229
+
230
+ described_class.start(valid_args)
226
231
  end
227
232
  end
228
233
  end
data/spec/spec_helper.rb CHANGED
@@ -15,9 +15,6 @@ RSpec.configure do |config|
15
15
  config.order = 'random'
16
16
 
17
17
  config.before do
18
- # suppress output in terminal
19
- allow_any_instance_of(Object).to receive(:puts)
20
-
21
18
  progressbar_mock = double('ProgressBar')
22
19
  allow(ProgressBar).to receive(:create).and_return(progressbar_mock)
23
20
  allow(progressbar_mock).to receive(:increment)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rainforest-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.10.2
4
+ version: 1.10.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Russell Smith
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-10-11 00:00:00.000000000 Z
12
+ date: 2016-10-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: httparty