proxy_tester 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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