proxy_tester 0.0.1 → 0.0.2

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.
Files changed (49) hide show
  1. data/Gemfile +2 -0
  2. data/Gemfile.lock +18 -1
  3. data/README.md +88 -4
  4. data/bin/proxy_tester +11 -0
  5. data/config.yaml +0 -0
  6. data/features/.keep +0 -0
  7. data/features/add_test_case.feature +41 -0
  8. data/features/step_definitions.rb +30 -0
  9. data/features/support/debugging.rb +3 -0
  10. data/features/support/env.rb +12 -0
  11. data/features/support/environment.rb +22 -0
  12. data/features/support/filesystem.rb +19 -0
  13. data/lib/proxy_tester.rb +11 -1
  14. data/lib/proxy_tester/actions/add_test_case.rb +40 -0
  15. data/lib/proxy_tester/actions/clear_environment.rb +21 -0
  16. data/lib/proxy_tester/actions/fetch_urls.rb +94 -0
  17. data/lib/proxy_tester/actions/handle_error.rb +10 -6
  18. data/lib/proxy_tester/capybara_proxy.rb +1 -1
  19. data/lib/proxy_tester/cli/main.rb +10 -3
  20. data/lib/proxy_tester/cli/test.rb +79 -0
  21. data/lib/proxy_tester/config.rb +1 -1
  22. data/lib/proxy_tester/error_handler.rb +16 -7
  23. data/lib/proxy_tester/error_messages.rb +45 -3
  24. data/lib/proxy_tester/exceptions.rb +31 -2
  25. data/lib/proxy_tester/git_repository.rb +6 -7
  26. data/lib/proxy_tester/http_proxy.rb +48 -0
  27. data/lib/proxy_tester/locales/en.yml +18 -0
  28. data/lib/proxy_tester/main.rb +1 -1
  29. data/lib/proxy_tester/models/user.rb +2 -2
  30. data/lib/proxy_tester/remote_repository.rb +55 -0
  31. data/lib/proxy_tester/reporters/fetch_urls.rb +34 -0
  32. data/lib/proxy_tester/version.rb +2 -1
  33. data/proxy_tester.gemspec +2 -0
  34. data/script/ci +4 -0
  35. data/script/test_web +5 -2
  36. data/spec/actions/add_test_case_spec.rb +81 -0
  37. data/spec/actions/fetch_urls_spec.rb +52 -0
  38. data/spec/actions/handle_error_spec.rb +1 -1
  39. data/spec/capybara_proxy_spec.rb +1 -1
  40. data/spec/git_repository_spec.rb +22 -0
  41. data/spec/http_proxy_spec.rb +47 -0
  42. data/spec/remote_repository_spec.rb +103 -0
  43. data/spec/reporters/fetch_urls_spec.rb +58 -0
  44. data/spec/support/filesystem.rb +2 -0
  45. data/spec/support/reporting.rb +6 -0
  46. data/spec/user_spec.rb +10 -2
  47. metadata +66 -5
  48. data/lib/proxy_tester/actions/clone_repository.rb +0 -39
  49. data/spec/actions/clone_repository_spec.rb +0 -83
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+ module ProxyTester
3
+ module Actions
4
+ class ClearEnvironment
5
+
6
+ private
7
+
8
+ attr_reader :variables
9
+
10
+ public
11
+
12
+ def initialize(options = {})
13
+ @variables = options.fetch(:variables, %w{ http_proxy https_proxy HTTP_PROXY HTTPS_PROXY })
14
+ end
15
+
16
+ def run
17
+ variables.each { |var| ENV.delete(var) }
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,94 @@
1
+ # encoding: utf-8
2
+ module ProxyTester
3
+ module Actions
4
+ class FetchUrls
5
+
6
+ private
7
+
8
+ attr_reader :urls, :proxy, :timeout, :count, :result, :reporter, :concurrent, :output
9
+
10
+ public
11
+
12
+ def initialize(options = {})
13
+ @urls = options.fetch(:urls).collect { |u| Addressable::URI.heuristic_parse(u).to_s }
14
+ @proxy = options.fetch(:proxy)
15
+ @timeout = options.fetch(:timeout).to_i
16
+ @count = options.fetch(:count).to_i
17
+ @concurrent = options.fetch(:concurrent)
18
+
19
+ ProxyTester.ui_logger.debug "Options: " + options.to_s
20
+
21
+ @output = options.fetch(:output)
22
+ @reporter = options.fetch(:reporter, Reporters::FetchUrls.new(output))
23
+
24
+ @result = []
25
+ end
26
+
27
+ def run
28
+ threads = []
29
+
30
+ reporter.header
31
+
32
+ urls.each do |u|
33
+ count.times do |n|
34
+ threads << Thread.new do
35
+ ProxyTester.ui_logger.info "Starting #{n + 1}-nth thread to fetch \"#{u}\" via \"#{proxy.to_string}\"."
36
+
37
+ begin
38
+ fetch_url(u)
39
+ end while concurrent
40
+ end
41
+ end
42
+ end
43
+
44
+
45
+ sleep_time = 0
46
+ while sleep_time < timeout and threads.any? { |t| t.alive? }
47
+ sleep 1
48
+ sleep_time += 1
49
+ end
50
+
51
+ if sleep_time < timeout
52
+ ProxyTester.ui_logger.info "All threads finished before waiting time \"#{timeout}\" was over."
53
+ else
54
+ ProxyTester.ui_logger.info "Waiting time #{timeout} is over. Killing all active threads..."
55
+ end
56
+
57
+ threads.each do |t|
58
+ Thread.kill(t)
59
+ end
60
+
61
+ end
62
+
63
+ private
64
+
65
+ def fetch_url(url)
66
+ begin
67
+ response = Excon.get(url,
68
+ proxy: proxy.to_string(cleartext: true),
69
+ middlewares: Excon.defaults[:middlewares] + [Excon::Middleware::RedirectFollower],
70
+ read_timeout: timeout,
71
+ write_timeout: timeout,
72
+ headers: {
73
+ 'User-Agent' => 'ProxyTester',
74
+ }
75
+ )
76
+ rescue Excon::Errors::SocketError
77
+ handler = ErrorHandler.find(Exceptions::ProxyNotReachable)
78
+ handler.use(host: proxy.host, port: proxy.port)
79
+
80
+ ProxyTester.ui_logger.fatal handler.details
81
+
82
+ raise Exceptions::ProxyNotReachable, JSON.dump(host: proxy.port, port: proxy.port)
83
+ end
84
+
85
+ reporter.data(
86
+ headers: response.headers,
87
+ status: response.status,
88
+ url: u,
89
+ proxy: proxy.to_string,
90
+ )
91
+ end
92
+ end
93
+ end
94
+ end
@@ -5,19 +5,23 @@ module ProxyTester
5
5
 
6
6
  private
7
7
 
8
- attr_reader :exception, :original_message, :handler_klass
8
+ attr_reader :handler_klass, :exception
9
9
 
10
10
  public
11
11
 
12
12
  def initialize(exception, handler_klass = ErrorHandler)
13
- @exception = exception.class
14
- @original_message = exception.message
15
- @handler_klass = handler_klass
13
+ @exception = exception
14
+ @handler_klass = handler_klass
16
15
  end
17
16
 
18
17
  def run
19
18
  handler = handler_klass.find exception
20
- handler.original_message = original_message
19
+
20
+ if exception.respond_to? :cause
21
+ handler.cause = exception.cause
22
+ else
23
+ handler.cause = exception
24
+ end
21
25
 
22
26
  handler.execute(parsed_message)
23
27
  end
@@ -25,7 +29,7 @@ module ProxyTester
25
29
  private
26
30
 
27
31
  def parsed_message
28
- result = JSON.parse(original_message)
32
+ result = JSON.parse(exception.message)
29
33
  return {} unless result.kind_of? Hash
30
34
 
31
35
  result
@@ -48,7 +48,7 @@ module ProxyTester
48
48
  end
49
49
 
50
50
  def user_string
51
- user.to_string.shellescape
51
+ user.to_string(cleartext: true).shellescape
52
52
  end
53
53
  end
54
54
  end
@@ -7,10 +7,14 @@ module ProxyTester
7
7
  class_option :debug_mode, type: :boolean, desc: 'Run application in debug mode'
8
8
 
9
9
  desc 'test', 'Run tests'
10
+ subcommand 'test', Cli::Test
11
+
12
+ desc 'add SOURCE', 'Add new testcases by cloning them via vcs'
13
+ option :force, type: :boolean, default: false, desc: 'Overwrite existing files?'
10
14
  option :test_cases_directory, type: :string, desc: 'Directory with test cases'
11
- option :tags, type: :array, desc: 'Filter tests based on tags'
12
- def test
15
+ def add(source)
13
16
  ProxyTester.config = ProxyTester::Config.new(options[:config_file]) if options[:config_file]
17
+
14
18
  ProxyTester.config.log_level = options[:log_level] if options[:log_level]
15
19
  ProxyTester.config.debug_mode = options[:debug_mode] if options[:debug_mode]
16
20
  ProxyTester.config.test_cases_directory = options[:test_cases_directory] if options[:test_cases_directory]
@@ -19,7 +23,10 @@ module ProxyTester
19
23
  ProxyTester.ui_logger.level = ProxyTester.config.log_level
20
24
  ProxyTester.enable_debug_mode if ProxyTester.config.debug_mode
21
25
 
22
- ProxyTester::RspecRunner.new.run(options[:tags])
26
+ ProxyTester.ui_logger.debug('Options: ' + options.to_s)
27
+ ProxyTester.ui_logger.debug("Config:\n" + ProxyTester.config.to_s)
28
+
29
+ Actions::AddTestCase.new(RemoteRepository.new(source), ProxyTester.config.test_cases_directory, force: options[:force]).run
23
30
  end
24
31
 
25
32
  desc 'init', 'Create files/directories to use proxy_tester'
@@ -0,0 +1,79 @@
1
+ # encoding
2
+ module ProxyTester
3
+ module Cli
4
+ class Test < Thor
5
+ desc 'rspec', 'Run rspec tests'
6
+ option :test_cases_directory, type: :string, desc: 'Directory with test cases'
7
+ option :tags, type: :array, desc: 'Filter tests based on tags'
8
+ def rspec
9
+ ProxyTester.config = ProxyTester::Config.new(options[:config_file]) if options[:config_file]
10
+ ProxyTester.config.log_level = options[:log_level] if options[:log_level]
11
+ ProxyTester.config.debug_mode = options[:debug_mode] if options[:debug_mode]
12
+ ProxyTester.config.test_cases_directory = options[:test_cases_directory] if options[:test_cases_directory]
13
+ ProxyTester.config.lock
14
+
15
+ ProxyTester.ui_logger.level = ProxyTester.config.log_level
16
+ ProxyTester.enable_debug_mode if ProxyTester.config.debug_mode
17
+
18
+ ProxyTester::RspecRunner.new.run(options[:tags])
19
+ end
20
+
21
+ desc 'url', 'Fetch url'
22
+ option :count, type: :numeric, desc: 'Number of requests', default: 10
23
+ option :timeout, type: :numeric, desc: 'Wait n seconds before kill children', default: 60
24
+ option :proxy, type: :array, desc: 'System(s) under test', required: true
25
+ option :user, type: :string, desc: 'User for authentication', default: ENV['USER']
26
+ option :log_level, type: :string, desc: 'Log level'
27
+ option :concurrent, type: :boolean, desc: 'Simulate concurrent requests', default: false
28
+ option :output, type: :string, desc: 'Write output to file'
29
+ def url(*urls)
30
+ ProxyTester.config = ProxyTester::Config.new(options[:config_file]) if options[:config_file]
31
+ ProxyTester.config.log_level = options[:log_level] if options[:log_level]
32
+ ProxyTester.config.debug_mode = options[:debug_mode] if options[:debug_mode]
33
+ ProxyTester.config.lock
34
+
35
+ ProxyTester.ui_logger.level = ProxyTester.config.log_level
36
+ ProxyTester.enable_debug_mode if ProxyTester.config.debug_mode
37
+
38
+ proxies = options[:proxy].collect { |p| HttpProxy.new(p) }
39
+
40
+ unless options[:user].blank?
41
+ user = User.new
42
+ user.name = options[:user]
43
+
44
+ while user.password.blank?
45
+ user.password = HighLine.new.ask("Enter password for proxy user \"#{user.name}\": ") { |q| q.echo = '*' }
46
+ end
47
+
48
+ puts ''
49
+
50
+ proxies.each { |p| p.use_user user }
51
+ end
52
+
53
+ output = options.fetch(:output, $stdout)
54
+ if output.kind_of? String
55
+ output = File.open(output, 'w')
56
+ else
57
+ output = $stdout
58
+ end
59
+
60
+ Actions::ClearEnvironment.new.run
61
+
62
+ proxies.each do |p|
63
+ ProxyTester.ui_logger.debug "Using proxy \"#{p.to_string}\""
64
+ ProxyTester.ui_logger.debug "Using user \"#{user.to_string}\""
65
+
66
+ action = Actions::FetchUrls.new( urls: urls,
67
+ timeout: options[:timeout],
68
+ count: options[:count],
69
+ proxy: p,
70
+ concurrent: options[:concurrent],
71
+ output: output,
72
+ )
73
+ action.run
74
+ end
75
+ end
76
+
77
+ end
78
+ end
79
+ end
@@ -27,7 +27,7 @@ module ProxyTester
27
27
  begin
28
28
  config[option.to_sym] = value
29
29
  rescue RuntimeError
30
- raise Exceptions::ConfigLocked
30
+ raise Exceptions::ConfigLocked, JSON.dump(option: option)
31
31
  end
32
32
  end
33
33
 
@@ -16,7 +16,7 @@ module ProxyTester
16
16
  public
17
17
 
18
18
  attr_reader :exception
19
- attr_accessor :original_message
19
+ attr_accessor :cause
20
20
 
21
21
  def initialize(options = {})
22
22
  @exception = options.fetch(:exception)
@@ -36,7 +36,7 @@ module ProxyTester
36
36
  end
37
37
 
38
38
  def find(exception)
39
- handlers.find(proc { default_handler }) { |h| h.exception == exception }
39
+ handlers.find(proc { default_handler }) { |h| h.exception == exception.class }
40
40
  end
41
41
 
42
42
  private
@@ -44,7 +44,7 @@ module ProxyTester
44
44
  def default_handler
45
45
  mutex.synchronize do
46
46
  @default_handler ||= new(
47
- exception: StandardError,
47
+ exception: Exceptions::DefaultError,
48
48
  details: 'errors.default.details',
49
49
  summary: 'errors.default.summary',
50
50
  exit_code: 99,
@@ -76,17 +76,26 @@ module ProxyTester
76
76
  end
77
77
 
78
78
  def use(data)
79
- data = JSON.parse(data).symbolize_keys if data.kind_of? String
79
+ data = JSON.parse(data) if data.kind_of? String
80
+ data = data.symbolize_keys
80
81
 
81
- @details = I18n.t(details_i18n, data)
82
- @summary = I18n.t(summary_i18n, data)
82
+ @details ||= I18n.t(details_i18n, data)
83
+ @summary ||= I18n.t(summary_i18n, data)
83
84
  end
84
85
 
85
86
  def execute(data = {})
86
87
  use(data)
87
88
 
88
89
  ProxyTester.ui_logger.fatal details
89
- ProxyTester.ui_logger.debug original_message if original_message
90
+ ProxyTester.ui_logger.debug "Resulting Exception: #{exception}"
91
+ ProxyTester.ui_logger.debug "Resulting Exit Code: #{exit_code}"
92
+
93
+ if cause
94
+ ProxyTester.ui_logger.debug "Cause: #{cause.class}"
95
+ ProxyTester.ui_logger.debug "Cause Message: #{cause.message}"
96
+ ProxyTester.ui_logger.debug "Cause Backtrace:\n" + Array(cause.backtrace).join("\n")
97
+ end
98
+
90
99
  Kernel.exit exit_code
91
100
  end
92
101
 
@@ -3,7 +3,7 @@ module ProxyTester
3
3
  module ErrorMessages
4
4
 
5
5
  ErrorHandler.create(
6
- exception: StandardError,
6
+ exception: Exceptions::DefaultError,
7
7
  summary: 'errors.default.summary',
8
8
  details: 'errors.default.details',
9
9
  exit_code: 99,
@@ -69,14 +69,56 @@ module ProxyTester
69
69
  exception: Exceptions::PacResultInvalid,
70
70
  details: 'errors.invalid_pac_result.details',
71
71
  summary: 'errors.invalid_pac_result.summary',
72
- exit_code: 19,
72
+ exit_code: 9,
73
73
  )
74
74
 
75
75
  ErrorHandler.create(
76
76
  exception: Exceptions::ProxyTypeInvalid,
77
77
  details: 'errors.invalid_proxy_type.details',
78
78
  summary: 'errors.invalid_proxy_type.summary',
79
- exit_code: 19,
79
+ exit_code: 10,
80
+ )
81
+
82
+ ErrorHandler.create(
83
+ exception: Exceptions::ProxyConnectionStringInvalid,
84
+ details: 'errors.invalid_proxy_connection_string.details',
85
+ summary: 'errors.invalid_proxy_connection_string.summary',
86
+ exit_code: 11,
87
+ )
88
+
89
+ ErrorHandler.create(
90
+ exception: Interrupt,
91
+ details: 'errors.sigint.details',
92
+ summary: 'errors.sigint.summary',
93
+ exit_code: 12,
94
+ )
95
+
96
+ ErrorHandler.create(
97
+ exception: Exceptions::SIGTERM,
98
+ details: 'errors.sigterm.details',
99
+ summary: 'errors.sigterm.summary',
100
+ exit_code: 13,
101
+ )
102
+
103
+ ErrorHandler.create(
104
+ exception: Exceptions::ProxyNotReachable,
105
+ details: 'errors.unreachable_proxy.details',
106
+ summary: 'errors.unreachable_proxy.summary',
107
+ exit_code: 14,
108
+ )
109
+
110
+ ErrorHandler.create(
111
+ exception: Exceptions::RepositoryDoesNotExist,
112
+ details: 'errors.unexisting_repository.details',
113
+ summary: 'errors.unexisting_repository.summary',
114
+ exit_code: 15,
115
+ )
116
+
117
+ ErrorHandler.create(
118
+ exception: Exceptions::RepositoryInvalid,
119
+ details: 'errors.invalid_repository.details',
120
+ summary: 'errors.invalid_repository.summary',
121
+ exit_code: 16,
80
122
  )
81
123
  end
82
124
  end
@@ -2,11 +2,28 @@
2
2
  module ProxyTester
3
3
  # Exceptions
4
4
  module Exceptions
5
+ # my standard error
6
+ if RUBY_VERSION < "2.1"
7
+ class MyStandardError < StandardError
8
+ attr_reader :cause
9
+
10
+ def initialize(msg, cause = $!)
11
+ super(msg)
12
+ @cause = cause;
13
+ end
14
+ end
15
+ else
16
+ class MyStandardError < StandardError; end
17
+ end
18
+
5
19
  # raised if user error occurred
6
- class UserError < StandardError; end
20
+ class UserError < MyStandardError; end
21
+
22
+ # raised if an internal error occurred
23
+ class InternalError < MyStandardError; end
7
24
 
8
25
  # raised if an internal error occurred
9
- class InternalError < StandardError; end
26
+ class DefaultError < MyStandardError; end
10
27
 
11
28
  # raised if config file does not exist
12
29
  class ConfigFileNotReadable < UserError; end
@@ -26,6 +43,9 @@ module ProxyTester
26
43
  # raised if repository does not exist
27
44
  class RepositoryDoesNotExist < UserError; end
28
45
 
46
+ # raised if repository is not a supported vcs repository
47
+ class RepositoryInvalid < UserError; end
48
+
29
49
  # raised if proxy user does not exist
30
50
  class ProxyUserInvalid < UserError; end
31
51
 
@@ -49,5 +69,14 @@ module ProxyTester
49
69
 
50
70
  # raised if chosen proxy type is invalid
51
71
  class ProxyTypeInvalid < UserError; end
72
+
73
+ # raised if chosen proxy connection string is invalid
74
+ class ProxyConnectionStringInvalid< UserError; end
75
+
76
+ # raised if program was killed with SIGTERM
77
+ class SIGTERM < UserError; end
78
+
79
+ # raised if proxy is not reachable
80
+ class ProxyNotReachable < UserError; end
52
81
  end
53
82
  end