api-tester 0.1.0 → 1.1.1

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 (79) hide show
  1. checksums.yaml +5 -5
  2. data/.github/dependabot.yml +15 -0
  3. data/.github/workflows/push.yml +39 -0
  4. data/.github/workflows/test.yml +31 -0
  5. data/.rspec +1 -0
  6. data/.rubocop.yml +61 -0
  7. data/Gemfile +2 -0
  8. data/Guardfile +70 -0
  9. data/README.md +106 -74
  10. data/Rakefile +8 -3
  11. data/api-tester.gemspec +31 -23
  12. data/changelog.txt +35 -0
  13. data/lib/api-tester.rb +15 -0
  14. data/lib/api-tester/config.rb +43 -0
  15. data/lib/api-tester/definition/boundary_case.rb +16 -0
  16. data/lib/api-tester/definition/contract.rb +20 -0
  17. data/lib/api-tester/definition/endpoint.rb +84 -0
  18. data/lib/api-tester/definition/fields/array_field.rb +47 -0
  19. data/lib/api-tester/definition/fields/boolean_field.rb +23 -0
  20. data/lib/api-tester/definition/fields/email_field.rb +25 -0
  21. data/lib/api-tester/definition/fields/enum_field.rb +32 -0
  22. data/lib/api-tester/definition/fields/field.rb +50 -0
  23. data/lib/api-tester/definition/fields/number_field.rb +22 -0
  24. data/lib/api-tester/definition/fields/object_field.rb +47 -0
  25. data/lib/api-tester/definition/fields/plain_array_field.rb +25 -0
  26. data/lib/api-tester/definition/method.rb +22 -0
  27. data/lib/api-tester/definition/request.rb +96 -0
  28. data/lib/api-tester/definition/response.rb +39 -0
  29. data/lib/api-tester/method_case_test.rb +83 -0
  30. data/lib/api-tester/modules/extra_verbs.rb +53 -0
  31. data/lib/api-tester/modules/format.rb +47 -0
  32. data/lib/api-tester/modules/good_case.rb +46 -0
  33. data/lib/api-tester/modules/injection_module.rb +81 -0
  34. data/lib/api-tester/modules/required_fields.rb +51 -0
  35. data/lib/api-tester/modules/server_information.rb +42 -0
  36. data/lib/api-tester/modules/typo.rb +70 -0
  37. data/lib/api-tester/modules/unexpected_fields.rb +61 -0
  38. data/lib/api-tester/modules/unused_fields.rb +31 -0
  39. data/lib/api-tester/reporter/api_report.rb +47 -0
  40. data/lib/api-tester/reporter/missing_field_report.rb +24 -0
  41. data/lib/api-tester/reporter/report.rb +30 -0
  42. data/lib/api-tester/reporter/status_code_report.rb +21 -0
  43. data/lib/api-tester/test_helper.rb +12 -0
  44. data/lib/api-tester/util/response_evaluator.rb +88 -0
  45. data/lib/api-tester/util/supported_verbs.rb +39 -0
  46. data/lib/api-tester/version.rb +5 -0
  47. metadata +159 -42
  48. data/.travis.yml +0 -6
  49. data/lib/tester.rb +0 -7
  50. data/lib/tester/api_tester.rb +0 -50
  51. data/lib/tester/definition/api_contract.rb +0 -13
  52. data/lib/tester/definition/api_method.rb +0 -11
  53. data/lib/tester/definition/boundary_case.rb +0 -11
  54. data/lib/tester/definition/endpoint.rb +0 -57
  55. data/lib/tester/definition/fields/array_field.rb +0 -44
  56. data/lib/tester/definition/fields/boolean_field.rb +0 -18
  57. data/lib/tester/definition/fields/email_field.rb +0 -20
  58. data/lib/tester/definition/fields/enum_field.rb +0 -27
  59. data/lib/tester/definition/fields/field.rb +0 -47
  60. data/lib/tester/definition/fields/number_field.rb +0 -17
  61. data/lib/tester/definition/fields/object_field.rb +0 -42
  62. data/lib/tester/definition/request.rb +0 -49
  63. data/lib/tester/definition/response.rb +0 -34
  64. data/lib/tester/method_case_test.rb +0 -67
  65. data/lib/tester/modules/extra_verbs.rb +0 -25
  66. data/lib/tester/modules/format.rb +0 -26
  67. data/lib/tester/modules/good_case.rb +0 -29
  68. data/lib/tester/modules/module.rb +0 -18
  69. data/lib/tester/modules/typo.rb +0 -41
  70. data/lib/tester/modules/unused_fields.rb +0 -22
  71. data/lib/tester/reporter/api_report.rb +0 -33
  72. data/lib/tester/reporter/missing_field_report.rb +0 -23
  73. data/lib/tester/reporter/missing_response_field_report.rb +0 -19
  74. data/lib/tester/reporter/report.rb +0 -25
  75. data/lib/tester/reporter/status_code_report.rb +0 -12
  76. data/lib/tester/test_helper.rb +0 -10
  77. data/lib/tester/util/response_evaluator.rb +0 -73
  78. data/lib/tester/util/supported_verbs.rb +0 -34
  79. data/lib/tester/version.rb +0 -3
data/Rakefile CHANGED
@@ -1,6 +1,11 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+ require 'rubocop/rake_task'
4
+
5
+ RuboCop::RakeTask.new(:rubocop) do |t|
6
+ t.options = ['--display-cop-names']
7
+ end
3
8
 
4
9
  RSpec::Core::RakeTask.new(:spec)
5
10
 
6
- task :default => :spec
11
+ task :default => [:rubocop, :spec]
@@ -1,39 +1,47 @@
1
1
  # coding: utf-8
2
2
  lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'tester/version'
4
+ require 'api-tester/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "api-tester"
8
- spec.version = Tester::VERSION
9
- spec.authors = ["arane"]
10
- spec.email = ["arane9@gmail.com"]
7
+ spec.name = 'api-tester'
8
+ spec.version = ApiTester::VERSION
9
+ spec.authors = ['arane']
10
+ spec.email = ['arane9@gmail.com']
11
11
 
12
- spec.summary = %q{Tool to help test APIs}
13
- spec.description = %q{Tool to test APIs which will eventually do boundary testing and other sorts of testing automatically given a contract}
14
- spec.homepage = "https://github.com/araneforseti/api-tester"
15
- spec.license = "MIT"
12
+ spec.summary = 'Tool to help test APIs'
13
+ spec.description = 'Tool to test APIs which will eventually do boundary testing and other sorts of testing automatically given a contract'
14
+ spec.homepage = 'https://github.com/araneforseti/api-tester'
15
+ spec.license = 'MIT'
16
16
 
17
17
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
18
  # to allow pushing to a single host or delete this section to allow pushing to any host.
19
- if spec.respond_to?(:metadata)
20
- spec.metadata['allowed_push_host'] = 'https://rubygems.org/'
21
- else
22
- raise "RubyGems 2.0 or newer is required to protect against " \
23
- "public gem pushes."
24
- end
19
+ # if spec.respond_to?(:metadata)
20
+ # spec.metadata['allowed_push_host'] = 'https://rubygems.org/'
21
+ # else
22
+ # raise 'RubyGems 2.0 or newer is required to protect against ' \
23
+ # 'public gem pushes.'
24
+ # end
25
25
 
26
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
26
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
27
27
  f.match(%r{^(test|spec|features)/})
28
28
  end
29
- spec.bindir = "exe"
29
+ spec.bindir = 'exe'
30
30
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
- spec.require_paths = ["lib"]
31
+ spec.require_paths = ['lib']
32
32
 
33
- spec.add_development_dependency "bundler", "~> 1.13"
34
- spec.add_development_dependency "rake", "~> 10.0"
35
- spec.add_development_dependency "rspec", "~> 3.0"
36
- spec.add_development_dependency "webmock", "~> 3.4"
33
+ spec.add_development_dependency 'bundler'
34
+ spec.add_development_dependency 'bundler-audit', '~>0.7.0'
35
+ spec.add_development_dependency 'guard-rspec', '~> 4.7.3'
36
+ spec.add_development_dependency 'pry', '~> 0.11'
37
+ spec.add_development_dependency 'rake', '~> 13.0.1'
38
+ spec.add_development_dependency 'require_all', '~>3.0.0'
39
+ spec.add_development_dependency 'rspec', '~> 3.0'
40
+ spec.add_development_dependency 'rubocop', '~> 0.93.0'
41
+ spec.add_development_dependency 'terminal-notifier', '~> 2.0.0'
42
+ spec.add_development_dependency 'terminal-notifier-guard', '~> 1.7.0'
43
+ spec.add_development_dependency 'webmock', '~> 3.4'
37
44
 
38
- spec.add_runtime_dependency "rest-client", "~> 2.0"
45
+ spec.add_runtime_dependency 'injection_vulnerability_library', '0.0.2'
46
+ spec.add_runtime_dependency 'rest-client', '~> 2.0'
39
47
  end
@@ -1,3 +1,38 @@
1
+ 2.0.0
2
+
3
+ - Added Required Fields module
4
+ - Added Unexpected Fields module
5
+ - Adding query params to definition
6
+ - Moved base_url to contract
7
+ - Changed endpoints to be relative urls
8
+ - Removed missing field report as it was unused
9
+ - Changing everything with more than one parameter to named parameters
10
+
11
+ 1.0.0
12
+
13
+ - Switching to using named variables to make future changes less breaky
14
+
15
+ 0.3.2
16
+
17
+ - Fixing some issues with how config adds modules
18
+ - Fixing README for same module issue as config
19
+
20
+ 0.3.1
21
+
22
+ - Adding ability to change headers for request objects
23
+ - Removing the reports from the sub modules and making them just return their reports
24
+ - Adjusted modules to use the full contract instead of endpoints
25
+ - Created module to check for server information being broadcast
26
+
27
+ 0.3.0
28
+
29
+ - Making modules actually be modules
30
+
31
+ 0.2.0
32
+
33
+ - Finally fixing the naming scheme
34
+ - Splitting configuration into its own class
35
+
1
36
  0.1.0
2
37
 
3
38
  - Moving call to endpoint for path param support
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Tool for testing through API definitions
4
+ module ApiTester
5
+ def self.go(contract, config)
6
+ reporter = config.reporter
7
+
8
+ config.modules.sort_by(&:order).each do |mod|
9
+ reporter.add_reports mod.go contract
10
+ end
11
+
12
+ reporter.print
13
+ reporter.reports.size.zero?
14
+ end
15
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'api-tester/reporter/api_report'
4
+
5
+ module ApiTester
6
+ # Config class for changing how the tool operates
7
+ class Config
8
+ attr_accessor :reporter
9
+ attr_accessor :modules
10
+
11
+ def initialize(reporter: ApiTester::ApiReport.new)
12
+ self.reporter = reporter
13
+ self.modules = []
14
+ end
15
+
16
+ def with_reporter(reporter)
17
+ self.reporter = reporter
18
+ self
19
+ end
20
+
21
+ def with_module(new_module)
22
+ modules << new_module
23
+ self
24
+ end
25
+
26
+ def with_default_modules
27
+ modules << Format
28
+ modules << GoodCase
29
+ modules << Typo
30
+ modules << UnusedFields
31
+ self
32
+ end
33
+
34
+ def with_all_modules
35
+ modules << Format
36
+ modules << ExtraVerbs
37
+ modules << GoodCase
38
+ modules << Typo
39
+ modules << UnusedFields
40
+ self
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApiTester
4
+ # Holds data necessary for tests
5
+ class BoundaryCase
6
+ attr_accessor :payload
7
+ attr_accessor :headers
8
+ attr_accessor :description
9
+
10
+ def initialize(description:, payload:, headers:)
11
+ self.description = description
12
+ self.payload = payload
13
+ self.headers = headers
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApiTester
4
+ # Class to define the whole contract
5
+ class Contract
6
+ attr_accessor :name
7
+ attr_accessor :endpoints
8
+ attr_accessor :base_url
9
+
10
+ def initialize(name:, base_url:)
11
+ self.name = name
12
+ self.endpoints = []
13
+ self.base_url = base_url
14
+ end
15
+
16
+ def add_endpoint(endpoint)
17
+ endpoints << endpoint
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'api-tester/definition/response'
4
+ require 'api-tester/definition/method'
5
+ require 'api-tester/test_helper'
6
+ require 'rest-client'
7
+ require 'json'
8
+
9
+ module ApiTester
10
+ # Class for defining and interacting with endpoints in a contract
11
+ class Endpoint
12
+ attr_accessor :name
13
+ attr_accessor :relative_url
14
+ attr_accessor :path_params
15
+ attr_accessor :methods
16
+ attr_accessor :test_helper
17
+ attr_accessor :bad_request_response
18
+ attr_accessor :not_allowed_response
19
+ attr_accessor :not_found_response
20
+
21
+ def initialize(name:, relative_url:)
22
+ self.relative_url = relative_url
23
+ self.name = name
24
+ self.methods = []
25
+ self.path_params = []
26
+ self.test_helper = ApiTester::TestHelper.new
27
+ self.bad_request_response = ApiTester::Response.new status_code: 400
28
+ self.not_allowed_response = ApiTester::Response.new status_code: 415
29
+ self.not_found_response = ApiTester::Response.new status_code: 404
30
+ end
31
+
32
+ def url
33
+ temp_url = relative_url
34
+ path_params.each do |param|
35
+ temp_url.sub! "{#{param}}", test_helper.retrieve_param(param)
36
+ end
37
+ temp_url
38
+ end
39
+
40
+ def default_call(base_url)
41
+ test_helper.before
42
+ method_defaults = methods[0].default_request
43
+ method_defaults[:url] = "#{base_url}#{url}"
44
+ begin
45
+ response = RestClient::Request.execute(method_defaults)
46
+ rescue RestClient::ExceptionWithResponse => e
47
+ response = e.response
48
+ end
49
+ test_helper.after
50
+ response
51
+ end
52
+
53
+ def call(base_url:, method:, query: '', payload: {}, headers: {})
54
+ test_helper.before
55
+ url = query ? "#{base_url}#{self.url}?#{query}" : "#{base_url}#{self.url}"
56
+ begin
57
+ response = RestClient::Request.execute(method: method.verb,
58
+ url: url,
59
+ payload: payload.to_json,
60
+ headers: headers)
61
+ rescue RestClient::ExceptionWithResponse => e
62
+ response = e.response
63
+ end
64
+ test_helper.after
65
+ response
66
+ end
67
+
68
+ def add_method(verb:, response:, request: Request.new)
69
+ methods << ApiTester::Method.new(verb: verb,
70
+ response: response,
71
+ request: request)
72
+ self
73
+ end
74
+
75
+ def add_path_param(param)
76
+ path_params << param
77
+ self
78
+ end
79
+
80
+ def verbs
81
+ methods.map(&:verb)
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'api-tester/definition/fields/field'
4
+
5
+ module ApiTester
6
+ # Class used for defining array fields
7
+ class ArrayField < Field
8
+ attr_accessor :fields
9
+
10
+ def initialize(name:, required: false)
11
+ super name: name, required: required
12
+ self.fields = []
13
+ end
14
+
15
+ def with_field(new_field)
16
+ fields << new_field
17
+ self
18
+ end
19
+
20
+ def subfields?
21
+ true
22
+ end
23
+
24
+ def default_value
25
+ return [] if fields.size.zero?
26
+
27
+ obj = {}
28
+ fields.each do |field|
29
+ obj[field.name] = field.default_value
30
+ end
31
+ [obj]
32
+ end
33
+
34
+ def negative_boundary_values
35
+ super +
36
+ [
37
+ 'string',
38
+ 123,
39
+ 0,
40
+ 1,
41
+ true,
42
+ false,
43
+ {}
44
+ ]
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'api-tester/definition/fields/field'
4
+
5
+ module ApiTester
6
+ # Class for defining booleans in contract
7
+ class BooleanField < Field
8
+ def initialize(name:, default_value: true, required: false)
9
+ super name: name, default_value: default_value, required: required
10
+ end
11
+
12
+ def negative_boundary_values
13
+ super +
14
+ [
15
+ 'string',
16
+ 123,
17
+ 0,
18
+ 1,
19
+ {}
20
+ ]
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'api-tester/definition/fields/field'
4
+
5
+ module ApiTester
6
+ # Class for defining email fields in contract
7
+ class EmailField < Field
8
+ def initialize(name:, default_value: 'test@test.com', required: false)
9
+ super name: name, default_value: default_value, required: required
10
+ end
11
+
12
+ def negative_boundary_values
13
+ super +
14
+ [
15
+ 'string',
16
+ 123,
17
+ 1,
18
+ 0,
19
+ true,
20
+ false,
21
+ {}
22
+ ]
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'api-tester/definition/fields/field'
4
+
5
+ module ApiTester
6
+ # Class for defining enumerators
7
+ class EnumField < Field
8
+ attr_accessor :acceptable_values
9
+
10
+ def initialize(name:, acceptable_values:, default_value: nil, required: false)
11
+ if default_value
12
+ super name: name, default_value: default_value, required: required
13
+ else
14
+ super name: name, default_value: acceptable_values[0], required: required
15
+ end
16
+
17
+ self.acceptable_values = acceptable_values
18
+ end
19
+
20
+ def negative_boundary_values
21
+ super +
22
+ [
23
+ 123,
24
+ 0,
25
+ 1,
26
+ true,
27
+ false,
28
+ {}
29
+ ]
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApiTester
4
+ # Base class for field definitions
5
+ class Field
6
+ attr_accessor :name
7
+ attr_accessor :default_value
8
+ attr_accessor :required
9
+ attr_accessor :is_seen
10
+
11
+ def initialize(name:, required: false, default_value: 'string')
12
+ self.name = name
13
+ self.default_value = default_value
14
+ self.required = required
15
+ self.is_seen = 0
16
+ end
17
+
18
+ def is_required
19
+ self.required = true
20
+ self
21
+ end
22
+
23
+ def is_not_required
24
+ self.required = false
25
+ self
26
+ end
27
+
28
+ def subfields?
29
+ false
30
+ end
31
+
32
+ def fields
33
+ []
34
+ end
35
+
36
+ def negative_boundary_values
37
+ cases = []
38
+ cases << nil if required
39
+ cases
40
+ end
41
+
42
+ def seen
43
+ self.is_seen += 1
44
+ end
45
+
46
+ def display_class
47
+ self.class
48
+ end
49
+ end
50
+ end