api-tester 1.0.0 → 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +5 -5
  2. data/.github/dependabot.yml +15 -0
  3. data/.github/workflows/dependabot.yml +29 -0
  4. data/.github/workflows/push.yml +39 -0
  5. data/.github/workflows/test.yml +31 -0
  6. data/.rubocop.yml +171 -0
  7. data/Gemfile +2 -0
  8. data/Guardfile +70 -0
  9. data/README.md +65 -61
  10. data/Rakefile +8 -3
  11. data/api-tester.gemspec +28 -24
  12. data/changelog.txt +10 -0
  13. data/lib/api-tester/config.rb +17 -15
  14. data/lib/api-tester/definition/boundary_case.rb +5 -4
  15. data/lib/api-tester/definition/contract.rb +10 -5
  16. data/lib/api-tester/definition/endpoint.rb +58 -32
  17. data/lib/api-tester/definition/fields/array_field.rb +26 -21
  18. data/lib/api-tester/definition/fields/boolean_field.rb +17 -7
  19. data/lib/api-tester/definition/fields/email_field.rb +28 -11
  20. data/lib/api-tester/definition/fields/enum_field.rb +19 -12
  21. data/lib/api-tester/definition/fields/field.rb +52 -45
  22. data/lib/api-tester/definition/fields/number_field.rb +20 -6
  23. data/lib/api-tester/definition/fields/object_field.rb +37 -30
  24. data/lib/api-tester/definition/fields/plain_array_field.rb +25 -0
  25. data/lib/api-tester/definition/method.rb +8 -5
  26. data/lib/api-tester/definition/request.rb +43 -19
  27. data/lib/api-tester/definition/response.rb +35 -25
  28. data/lib/api-tester/method_case_test.rb +68 -54
  29. data/lib/api-tester/modules/benchmark_module.rb +35 -0
  30. data/lib/api-tester/modules/extra_verbs.rb +36 -10
  31. data/lib/api-tester/modules/format.rb +23 -7
  32. data/lib/api-tester/modules/good_case.rb +25 -10
  33. data/lib/api-tester/modules/good_variations.rb +69 -0
  34. data/lib/api-tester/modules/injection_module.rb +44 -23
  35. data/lib/api-tester/modules/missing_resource.rb +63 -0
  36. data/lib/api-tester/modules/required_fields.rb +51 -0
  37. data/lib/api-tester/modules/server_information.rb +14 -12
  38. data/lib/api-tester/modules/typo.rb +39 -14
  39. data/lib/api-tester/modules/unexpected_fields.rb +59 -0
  40. data/lib/api-tester/modules/unused_fields.rb +13 -7
  41. data/lib/api-tester/reporter/api_report.rb +25 -16
  42. data/lib/api-tester/reporter/missing_field_report.rb +11 -15
  43. data/lib/api-tester/reporter/report.rb +12 -13
  44. data/lib/api-tester/reporter/response_time_report.rb +24 -0
  45. data/lib/api-tester/reporter/status_code_report.rb +10 -4
  46. data/lib/api-tester/test_helper.rb +8 -6
  47. data/lib/api-tester/util/response_evaluator.rb +84 -56
  48. data/lib/api-tester/util/supported_verbs.rb +8 -5
  49. data/lib/api-tester/version.rb +3 -1
  50. data/lib/api-tester.rb +6 -3
  51. metadata +92 -27
  52. data/.travis.yml +0 -6
  53. data/lib/api-tester/reporter/missing_response_field_report.rb +0 -21
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'api-tester/reporter/status_code_report'
4
+ require 'api-tester/method_case_test'
5
+
6
+ module ApiTester
7
+ # Checks the good case as defined in contract
8
+ module GoodVariations
9
+ def self.go(contract)
10
+ reports = []
11
+
12
+ contract.endpoints.each do |endpoint|
13
+ endpoint.methods.each do |method|
14
+ method.request.fields.each do |field|
15
+ field.good_cases.each do |value|
16
+ payload = method.request.default_payload
17
+ payload[field.name] = value
18
+ call = BoundaryCase.new description: contract.base_url + endpoint.display_url,
19
+ payload: payload,
20
+ headers: method.request.default_headers
21
+ response = endpoint.call base_url: contract.base_url,
22
+ method: method,
23
+ payload: payload,
24
+ headers: call.headers
25
+ test = GoodVariationTest.new response: response,
26
+ url: contract.base_url + endpoint.url,
27
+ method: method
28
+ reports.concat test.check
29
+ end
30
+ end
31
+ method.request.query_params.each do |field|
32
+ field.good_cases.each do |value|
33
+ payload = method.request.default_payload
34
+ payload[field.name] = value
35
+ call = BoundaryCase.new description: contract.base_url + endpoint.display_url,
36
+ payload: payload,
37
+ headers: method.request.default_headers
38
+ response = endpoint.call base_url: contract.base_url,
39
+ method: method,
40
+ payload: payload,
41
+ headers: call.headers
42
+ test = GoodVariationTest.new response: response,
43
+ url: contract.base_url + endpoint.url,
44
+ method: method
45
+ reports.concat test.check
46
+ end
47
+ end
48
+ end
49
+ end
50
+ reports
51
+ end
52
+
53
+ def self.order
54
+ 1
55
+ end
56
+ end
57
+
58
+ # Test layout used by module
59
+ class GoodVariationTest < MethodCaseTest
60
+ def initialize(response:, url:, method:)
61
+ super response: response,
62
+ payload: method.request.default_payload,
63
+ expected_response: method.expected_response,
64
+ url: url,
65
+ verb: method.verb,
66
+ module_name: 'GoodVariationsModule'
67
+ end
68
+ end
69
+ end
@@ -1,29 +1,39 @@
1
- require "injection_vulnerability_library"
1
+ # frozen_string_literal: true
2
+
3
+ require 'injection_vulnerability_library'
2
4
 
3
5
  module ApiTester
6
+ # Tests injection cases
4
7
  module InjectionModule
5
- def self.go contract
8
+ def self.go(contract)
6
9
  reports = []
7
10
  contract.endpoints.each do |endpoint|
8
11
  endpoint.methods.each do |method|
9
- reports.concat inject_payload endpoint, method
12
+ reports.concat inject_payload contract.base_url, endpoint, method
10
13
  end
11
14
  end
12
15
  reports
13
16
  end
14
17
 
15
- def self.inject_payload endpoint, method
18
+ def self.inject_payload(base_url, endpoint, method)
16
19
  reports = []
17
20
  sql_injections = InjectionVulnerabilityLibrary.sql_vulnerabilities
18
21
 
19
22
  method.request.fields.each do |field|
20
23
  sql_injections.each do |injection|
21
- injection_value = "#{field.default_value}#{injection}"
22
- payload = method.request.altered_payload(field.name, injection_value)
23
- response = endpoint.call method, payload, method.request.default_headers
24
- if(!check_response(response, endpoint)) then
25
- reports << InjectionReport.new("sql", endpoint.url, payload, response)
26
- end
24
+ injection_value = "#{field.default}#{injection}"
25
+ payload = method.request.altered_payload field_name: field.name,
26
+ value: injection_value
27
+ response = endpoint.call base_url: base_url,
28
+ method: method,
29
+ payload: payload,
30
+ headers: method.request.default_headers
31
+ next if check_response(response, endpoint)
32
+
33
+ reports << InjectionReport.new('sql',
34
+ endpoint.url,
35
+ payload,
36
+ response)
27
37
  end
28
38
  end
29
39
 
@@ -31,25 +41,36 @@ module ApiTester
31
41
  end
32
42
 
33
43
  def self.check_response(response, endpoint)
34
- response.code == 200 || check_error(response, endpoint)
44
+ if response.code == 200 || check_error(response, endpoint)
45
+ print '.'
46
+ return true
47
+ end
48
+ print 'F'
49
+ false
35
50
  end
36
51
 
37
- def self.check_error response, endpoint
38
- evaluator = ApiTester::ResponseEvaluator.new response.body, endpoint.bad_request_response
52
+ def self.check_error(response, endpoint)
53
+ evaluator = ApiTester::ResponseEvaluator.new(
54
+ actual_body: response.body,
55
+ expected_fields: endpoint.bad_request_response
56
+ )
39
57
  missing_fields = evaluator.missing_fields
40
58
  extra_fields = evaluator.extra_fields
41
- response.code == endpoint.bad_request_response.code && missing_fields.size == 0 && extra_fields.size == 0
59
+ response.code == endpoint.bad_request_response.code &&
60
+ missing_fields.size.zero? && extra_fields.size.zero?
61
+ end
62
+
63
+ def self.order
64
+ 5
42
65
  end
43
66
  end
44
67
  end
45
68
 
69
+ # Report for InjectionModule
46
70
  class InjectionReport
47
- attr_accessor :injection_type
48
- attr_accessor :url
49
- attr_accessor :payload
50
- attr_accessor :response
71
+ attr_accessor :injection_type, :url, :payload, :response
51
72
 
52
- def initialize injection_type, url, payload, response
73
+ def initialize(injection_type, url, payload, response)
53
74
  self.injection_type = injection_type
54
75
  self.url = url
55
76
  self.payload = payload
@@ -57,10 +78,10 @@ class InjectionReport
57
78
  end
58
79
 
59
80
  def print
60
- puts "Found potential #{self.injection_type}: "
61
- puts " Requested #{self.url} with payload:"
62
- puts " #{self.payload}"
81
+ puts "Found potential #{injection_type}: "
82
+ puts " Requested #{url} with payload:"
83
+ puts " #{payload}"
63
84
  puts ' Received: '
64
- puts " #{self.response}"
85
+ puts " #{response}"
65
86
  end
66
87
  end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'api-tester/reporter/status_code_report'
4
+ require 'api-tester/util/supported_verbs'
5
+
6
+ module ApiTester
7
+ # Module checking various not found scenarios
8
+ module MissingResource
9
+ def self.go(contract)
10
+ reports = []
11
+
12
+ contract.endpoints.each do |endpoint|
13
+ endpoint.path_params.each do |path_param|
14
+ bad_resource = endpoint.relative_url.gsub("{#{path_param}}", 'gibberish')
15
+
16
+ bad_endpoint = ApiTester::Endpoint.new name: 'Bad Resource',
17
+ relative_url: bad_resource
18
+ method = ApiTester::Method.new verb: ApiTester::SupportedVerbs::GET,
19
+ response: ApiTester::Response.new(
20
+ status_code: 200
21
+ ),
22
+ request: ApiTester::Request.new
23
+ response = bad_endpoint.call base_url: contract.base_url + bad_resource,
24
+ method: method,
25
+ payload: {},
26
+ headers: contract.required_headers
27
+ test = MissingResourceTest.new response,
28
+ {},
29
+ endpoint.not_found_response,
30
+ bad_resource,
31
+ ApiTester::SupportedVerbs::GET
32
+ test.check
33
+ end
34
+ end
35
+
36
+ reports
37
+ end
38
+
39
+ def self.allowed_verbs(endpoint)
40
+ allowances = []
41
+ endpoint.methods.each do |method|
42
+ allowances << method.verb
43
+ end
44
+ allowances.uniq
45
+ end
46
+
47
+ def self.order
48
+ 4
49
+ end
50
+ end
51
+
52
+ # Test layout for Missing Resource
53
+ class MissingResourceTest < MethodCaseTest
54
+ def initialize(response, payload, expected_response, url, verb)
55
+ super response: response,
56
+ payload: payload,
57
+ expected_response: expected_response,
58
+ url: url,
59
+ verb: verb,
60
+ module_name: 'Missing Resource'
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApiTester
4
+ # Ensures the fields marked as required in contract are guarded
5
+ module RequiredFields
6
+ def self.go(contract)
7
+ reports = []
8
+ contract.endpoints.each do |endpoint|
9
+ endpoint.methods.each do |method|
10
+ request_def = method.request
11
+ required_fields = request_def.fields.keep_if(&:required)
12
+ combinations = (1..required_fields.size).flat_map { |size| required_fields.combination(size).to_a }
13
+ combinations.each do |remove_fields|
14
+ fields = remove_fields.map do |field|
15
+ { name: field.name, value: nil }
16
+ end
17
+ payload = request_def.altered_payload_with fields
18
+ response = endpoint.call base_url: contract.base_url,
19
+ method: method,
20
+ payload: payload,
21
+ headers: request_def.default_headers
22
+ test = RequiredFieldsTest.new response: response,
23
+ payload: payload,
24
+ expected_response: endpoint.bad_request_response,
25
+ url: endpoint.url,
26
+ verb: method.verb
27
+ reports.concat test.check
28
+ end
29
+ end
30
+ end
31
+
32
+ reports
33
+ end
34
+
35
+ def self.order
36
+ 5
37
+ end
38
+ end
39
+
40
+ # Test layout used for RequiredFieldsModule
41
+ class RequiredFieldsTest < MethodCaseTest
42
+ def initialize(response:, payload:, expected_response:, url:, verb:)
43
+ super response: response,
44
+ payload: payload,
45
+ expected_response: expected_response,
46
+ url: url,
47
+ verb: verb,
48
+ module_name: 'RequiredFieldsModule'
49
+ end
50
+ end
51
+ end
@@ -1,15 +1,17 @@
1
- require 'pry'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module ApiTester
4
+ # Module for ensuring the server isn't broadcasting information about itself
4
5
  module ServerInformation
5
- def self.go contract
6
+ def self.go(contract)
6
7
  reports = []
7
8
  endpoint = contract.endpoints[0]
8
- response = endpoint.default_call
9
+ response = endpoint.default_call contract.base_url
9
10
 
10
- [:server, :x_powered_by, :x_aspnetmvc_version, :x_aspnet_version].each do |server_key|
11
- if response.headers[server_key] then
12
- reports << ServerBroadcastReport.new(response.headers[server_key], server_key)
11
+ %i[server x_powered_by x_aspnetmvc_version x_aspnet_version].each do |key|
12
+ if response.headers[key]
13
+ reports << ServerBroadcastReport.new(response.headers[key],
14
+ key)
13
15
  end
14
16
  end
15
17
 
@@ -22,18 +24,18 @@ module ApiTester
22
24
  end
23
25
  end
24
26
 
27
+ # Report used by module
25
28
  class ServerBroadcastReport
26
- attr_accessor :server_info
27
- attr_accessor :server_key
29
+ attr_accessor :server_info, :server_key
28
30
 
29
- def initialize server_info, server_key
31
+ def initialize(server_info, server_key)
30
32
  self.server_info = server_info
31
33
  self.server_key = server_key
32
34
  end
33
35
 
34
36
  def print
35
- puts "Found server information being broadcast in headers:"
36
- puts " #{self.server_info}"
37
- puts " as #{self.server_key}"
37
+ puts 'Found server information being broadcast in headers:'
38
+ puts " #{server_info}"
39
+ puts " as #{server_key}"
38
40
  end
39
41
  end
@@ -1,35 +1,54 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'api-tester/reporter/status_code_report'
2
4
  require 'api-tester/util/supported_verbs'
3
5
 
4
6
  module ApiTester
5
- class Typo
6
- def self.go contract
7
+ # Module checking various not found scenarios
8
+ module Typo
9
+ def self.go(contract)
7
10
  reports = []
8
11
 
9
- contract.endpoints.each do |endpoint|
10
- allowances(endpoint).each do |verbs|
11
- reports.concat check_typo_url(endpoint)
12
+ # Filtering out endpoints with ids since not a better way to check this
13
+ # Need to redesign system to handle this better
14
+ contract.endpoints.reject { |e| e.relative_url.include?('{') }.each do |endpoint|
15
+ allowances(endpoint).each do
16
+ reports.concat check_typo_url(contract.base_url, endpoint)
12
17
  end
13
18
  end
14
19
 
15
20
  reports
16
21
  end
17
22
 
18
- def self.check_typo_url endpoint
23
+ def self.check_typo_url(base_url, endpoint)
19
24
  bad_url = "#{endpoint.url}gibberishadsfasdf"
20
- bad_endpoint = ApiTester::Endpoint.new "Bad URL", bad_url
21
- typo_case = BoundaryCase.new("Typo URL check", {}, {})
22
- method = ApiTester::Method.new ApiTester::SupportedVerbs::GET, ApiTester::Response.new(200), ApiTester::Request.new
23
- response = bad_endpoint.call method, typo_case.payload, typo_case.headers
25
+ bad_endpoint = ApiTester::Endpoint.new name: 'Bad URL',
26
+ relative_url: bad_url
27
+ typo_case = BoundaryCase.new description: 'Typo URL check',
28
+ payload: {},
29
+ headers: {}
30
+ method = ApiTester::Method.new verb: ApiTester::SupportedVerbs::GET,
31
+ response: ApiTester::Response.new(
32
+ status_code: 200
33
+ ),
34
+ request: ApiTester::Request.new
35
+ response = bad_endpoint.call base_url: base_url,
36
+ method: method,
37
+ payload: typo_case.payload,
38
+ headers: typo_case.headers
24
39
 
25
- test = TypoClass.new response, typo_case.payload, endpoint.not_found_response, bad_url, ApiTester::SupportedVerbs::GET
40
+ test = TypoClass.new response,
41
+ typo_case.payload,
42
+ endpoint.not_found_response,
43
+ bad_url,
44
+ ApiTester::SupportedVerbs::GET
26
45
  test.check
27
46
  end
28
47
 
29
48
  def self.allowances(endpoint)
30
49
  allowances = []
31
50
  endpoint.methods.each do |method|
32
- allowances << method.verb
51
+ allowances << method.verb
33
52
  end
34
53
  allowances.uniq
35
54
  end
@@ -39,9 +58,15 @@ module ApiTester
39
58
  end
40
59
  end
41
60
 
61
+ # Test layout for TypoModule
42
62
  class TypoClass < MethodCaseTest
43
- def initialize response, payload, expected_response, url, verb
44
- super response, payload, expected_response, url, verb, "TypoModule"
63
+ def initialize(response, payload, expected_response, url, verb)
64
+ super response: response,
65
+ payload: payload,
66
+ expected_response: expected_response,
67
+ url: url,
68
+ verb: verb,
69
+ module_name: 'TypoModule'
45
70
  end
46
71
  end
47
72
  end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApiTester
4
+ # Module checking nothing shows up in response which is not defined in contract
5
+ module UnexpectedFields
6
+ def self.go(contract)
7
+ reports = []
8
+
9
+ contract.endpoints.each do |endpoint|
10
+ endpoint.methods.each do |method|
11
+ default_case = BoundaryCase.new description: endpoint.url,
12
+ payload: method.request.default_payload,
13
+ headers: method.request.default_headers
14
+ response = endpoint.call base_url: contract.base_url,
15
+ method: method,
16
+ payload: default_case.payload,
17
+ headers: default_case.headers
18
+ test = UnexpectedFieldsTest.new response, endpoint.url, method
19
+ reports.concat test.check
20
+ end
21
+ end
22
+ reports
23
+ end
24
+
25
+ def self.order
26
+ 90
27
+ end
28
+ end
29
+
30
+ # Test layout for UnexpectedFields module
31
+ class UnexpectedFieldsTest < MethodCaseTest
32
+ def initialize(response, url, method)
33
+ super response: response,
34
+ payload: method.request.default_payload,
35
+ expected_response: method.expected_response,
36
+ url: url,
37
+ verb: method.verb,
38
+ module_name: 'UnexpectedFieldsModule'
39
+ end
40
+
41
+ def check
42
+ evaluator = ApiTester::ResponseEvaluator.new actual_body: json_parse(response.body),
43
+ expected_fields: expected_response
44
+ response_fields = evaluator.response_field_array
45
+ expected_fields = evaluator.expected_fields
46
+ increment_fields evaluator.seen_fields
47
+ extra = response_fields - expected_fields
48
+ extra.each do |extra_field|
49
+ report = Report.new description: "UnexpectedFieldsModule - Found unexpected field #{extra_field}",
50
+ url: url,
51
+ request: payload,
52
+ expected_response: expected_response,
53
+ actual_response: response
54
+ reports << report
55
+ end
56
+ reports
57
+ end
58
+ end
59
+ end
@@ -1,16 +1,22 @@
1
- require 'api-tester/reporter/missing_response_field_report'
1
+ # frozen_string_literal: true
2
+
3
+ require 'api-tester/reporter/missing_field_report'
2
4
 
3
5
  module ApiTester
4
- class UnusedFields
5
- def self.go contract
6
+ # Ensures all fields defined in contract are returned during test suite
7
+ module UnusedFields
8
+ def self.go(contract)
6
9
  reports = []
7
10
 
8
11
  contract.endpoints.each do |endpoint|
9
12
  endpoint.methods.each do |method|
10
- method.expected_response.body.each do |field|
11
- if field.is_seen == 0
12
- reports << MissingResponseFieldReport.new(endpoint.url, method.verb, field.name, "UnusedFieldsModule")
13
- end
13
+ method.expected_response.body.filter(&:has_key).each do |field|
14
+ next unless field.is_seen.zero?
15
+
16
+ reports << MissingFieldReport.new(url: endpoint.url,
17
+ verb: method.verb,
18
+ expected_field: field.name,
19
+ description: 'UnusedFieldsModule')
14
20
  end
15
21
  end
16
22
  end
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'api-tester/reporter/report'
2
4
 
3
5
  module ApiTester
6
+ # class for dealing with reports generated by the modules during test suite
4
7
  class ApiReport
5
8
  attr_accessor :reports
6
9
 
@@ -8,31 +11,37 @@ module ApiTester
8
11
  self.reports = []
9
12
  end
10
13
 
11
- def add_new url, request, expected_response, actual_response, description="A case"
12
- report = Report.new description, url, request, expected_response, actual_response
13
- self.reports << report
14
+ def add_new(url:, request:, expected_response:, actual_response:, description: 'case')
15
+ report = Report.new description,
16
+ url,
17
+ request,
18
+ expected_response,
19
+ actual_response
20
+ reports << report
14
21
  end
15
22
 
16
- def add_new_report report
17
- self.reports << report
23
+ def add_new_report(report)
24
+ reports << report
18
25
  end
19
26
 
20
- def add_reports reports
21
- self.reports.concat reports
27
+ def add_reports(reports)
28
+ reports.each do |report|
29
+ add_new_report(report)
30
+ end
22
31
  end
23
32
 
24
33
  def print
25
- if self.reports.size > 0
26
- puts "Issues discovered: #{self.reports.size}"
27
- self.reports.each do |report|
34
+ puts ''
35
+ if reports.size.zero?
36
+ puts 'No issues found'
37
+ else
38
+ puts "Issues discovered: #{reports.size}"
39
+ reports.each do |report|
28
40
  report.print
29
- puts "\n"
30
- puts "\n"
41
+ puts ''
42
+ puts ''
31
43
  end
32
- puts ""
33
- puts "Issues discovered: #{self.reports.size}"
34
- else
35
- puts "No issues found"
44
+ puts "Total issues: #{reports.size}"
36
45
  end
37
46
  end
38
47
  end
@@ -1,25 +1,21 @@
1
- module ApiReport
1
+ # frozen_string_literal: true
2
+
3
+ module ApiTester
4
+ # Report used for when response is missing a field
2
5
  class MissingFieldReport
3
- attr_accessor :description
4
- attr_accessor :url
5
- attr_accessor :request
6
- attr_accessor :expected_field
7
- attr_accessor :actual_response
6
+ attr_accessor :url, :verb, :expected_field, :description
8
7
 
9
- def initialize(description, url, request, expected_field)
10
- self.description = description
8
+ def initialize(url:, verb:, expected_field:, description:)
11
9
  self.url = url
12
- self.request = request
10
+ self.verb = verb
13
11
  self.expected_field = expected_field
14
- self.actual_response = ''
12
+ self.description = description
15
13
  end
16
14
 
17
15
  def print
18
- puts "#{self.description}: "
19
- puts " Requested #{self.url} with payload:"
20
- puts " #{self.request}"
21
- puts ' Missing field: '
22
- puts " #{self.expected_field}"
16
+ puts "#{description}:"
17
+ puts " #{verb} #{url} is missing response field:"
18
+ puts " #{expected_field}"
23
19
  end
24
20
  end
25
21
  end
@@ -1,12 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ApiTester
4
+ # Standard report format for differing responses
2
5
  class Report
3
- attr_accessor :description
4
- attr_accessor :url
5
- attr_accessor :request
6
- attr_accessor :expected_response
7
- attr_accessor :actual_response
6
+ attr_accessor :description, :url, :request, :expected_response, :actual_response
8
7
 
9
- def initialize description, url, request, expected_response, actual_response
8
+ def initialize(description:, url:, request:, expected_response:, actual_response:)
10
9
  self.description = description
11
10
  self.url = url
12
11
  self.request = request
@@ -15,13 +14,13 @@ module ApiTester
15
14
  end
16
15
 
17
16
  def print
18
- puts "#{self.description}: "
19
- puts " Requested #{self.url} with payload:"
20
- puts " #{self.request.to_json}"
21
- puts " Expecting: "
22
- puts " " + self.expected_response.to_s
23
- puts " Receiving: "
24
- puts " #{self.actual_response}"
17
+ puts "#{description}: "
18
+ puts " Requested #{url} with payload:"
19
+ puts " #{request.to_json}"
20
+ puts ' Expecting: '
21
+ puts " #{expected_response}"
22
+ puts ' Receiving: '
23
+ puts " #{actual_response}"
25
24
  end
26
25
  end
27
26
  end